Skip to content
This repository was archived by the owner on Jan 10, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/AfterEffectsGuideline.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
- Please DO NOT scale, skew, rotate or set opacity under the shape's transformation. DO IT in layers Transform Section
![Layer Transform](/docs/images/doc-ae-layer-transform.png)

- With 3D shape layers, you can alter the rotation of the object in the (x,y,z) coordinate space
![Layer Rotation](/docs/images/doc-ae-rotation-example.png)

- Path trim is NOT supported
- Polystar is NOT supported
- Rectangles and Ellipses are NOT supported
Expand Down
Binary file added docs/images/doc-ae-rotation-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 40 additions & 11 deletions ios/keyframes/src/Layers/KFVectorAnimationLayer.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ @implementation KFVectorAnimationLayer
NSMutableArray *_animations;
}

typedef enum {
X,
Y,
Z
} KFAxis;

- (instancetype)init
{
self = [super init];
Expand Down Expand Up @@ -59,7 +65,11 @@ - (void)setAnimations:(NSArray<KFVectorAnimation *> *)animations
} else if ([animation.property isEqualToString:@"SCALE"]) {
[self _applyScaleAnimation:animation];
} else if ([animation.property isEqualToString:@"ROTATION"]) {
[self _applyRotationAnimation:animation];
[self _applyRotationAnimation:animation onAxis: Z];
} else if ([animation.property isEqualToString:@"X_ROTATION"]) {
[self _applyRotationAnimation:animation onAxis: X];
} else if ([animation.property isEqualToString:@"Y_ROTATION"]) {
[self _applyRotationAnimation:animation onAxis: Y];
} else if ([animation.property isEqualToString:@"POSITION"] && KFVersionLessThan(self.formatVersion, @"1.0")) {
// TO DO: for backward capability, should be deprecated
[self _applyPositionAnimation:animation];
Expand Down Expand Up @@ -268,33 +278,52 @@ - (void)_applyAnchorPointAnimation:(KFVectorAnimation *)anchorPointAnimation
self.anchorPoint = [[anchorPointValues firstObject] CGPointValue];
}

- (void)_applyRotationAnimation:(KFVectorAnimation *)rotationAnimation
{
NSArray *rotationValues = KFMapArray(rotationAnimation.keyValues, ^id(KFVectorAnimationKeyValue *keyValue) {
- (void)_applyRotationAnimation:(KFVectorAnimation *)rotationAnimation onAxis:(KFAxis)axis {
NSArray *rotationValues = KFMapArray(rotationAnimation.keyValues,
^id(KFVectorAnimationKeyValue *keyValue) {
return @([[keyValue.keyValue firstObject] floatValue] * M_PI / 180);
});
if (rotationValues.count > 1) {
NSString *axisString;
switch (axis) {
case X: {
axisString = @"x";
break;
}
case Y: {
axisString = @"y";
break;
}
case Z: {
axisString = @"z";
break;
}
}
NSString *animationKeypath = [NSString stringWithFormat:@"transform.rotation.%@", axisString];
// Rotate along the key value, for our custom CGFloat property.
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:animationKeypath];
anim.duration = self.frameCount * 1.0 / self.frameRate;
anim.values = rotationValues;
anim.repeatCount = self.repeatCount;
anim.keyTimes = KFMapArray(rotationAnimation.keyValues, ^id(KFVectorAnimationKeyValue *keyFrame) {
anim.keyTimes = KFMapArray(rotationAnimation.keyValues,
^id(KFVectorAnimationKeyValue *keyFrame) {
return @(keyFrame.startFrame * 1.0 / self.frameCount);
});
anim.timingFunctions = KFVectorLayerMediaTimingFunction(rotationAnimation.timingCurves);
anim.fillMode = kCAFillModeBoth;
anim.removedOnCompletion = NO;
[anim setValue:@"rotation animation" forKey:@"animationKey"];
NSString *animationValue = [NSString stringWithFormat:@"rotation%@ animation",
[axisString uppercaseString]];
[anim setValue:animationValue forKey:@"animationKey"];
[_animations addObject:anim];
}

// When layer is initialized, and unanimated, layer renders without the first animation applied. This fixes it.
self.transform = CATransform3DRotate(self.transform,
[[rotationValues firstObject] floatValue],
0.0,
0.0,
1.0);
axis == X ? 1.0 : 0.0,
axis == Y ? 1.0 : 0.0,
axis == Z ? 1.0 : 0.0);
}

- (void)_applyOpacityAnimation:(KFVectorAnimation *)opacityAnimation
Expand Down
23 changes: 22 additions & 1 deletion js/src/AECompToKeyframesAnimation.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,13 +492,16 @@ function parseTransformGroup(
timing_curves: [],
};
let kfAnimGroupPropRotationAnim: KfPropertyRotation;
let kfAnimGroupPropRotationXAnim: KfPropertyRotation;
let kfAnimGroupPropRotationYAnim: KfPropertyRotation;
let kfAnimGroupPropScaleAnim: KfPropertyScale;
let kfAnimGroupPropOpacityAnim: KfPropertyOpacity;

const animations = [];

transformGroup.properties.forEach(tfProp => {
warnIfUsingMissingFeature(!!tfProp.expression, 'expression', tfProp, transformGroup, layer, comp);

switch (tfProp.matchName) {

case 'ADBE Anchor Point': {
Expand Down Expand Up @@ -556,11 +559,27 @@ function parseTransformGroup(
case 'ADBE Rotate Z': {
const timing_curves = parseTimingFunctionsFromKeyframes(tfProp.keyframes, parseTimingFunctions);
const key_values = keyValuesFor(comp, tfProp, (value: number) => [value]);
if (key_values.filter(({data:[value]}) => value % 360 !== 0).length > 0) {
if (key_values.filter(({data:[value]}) => value !== 0).length > 0) {
kfAnimGroupPropRotationAnim = {property: 'ROTATION', key_values, timing_curves};
}
} break;

case 'ADBE Rotate X': {
const timing_curves = parseTimingFunctionsFromKeyframes(tfProp.keyframes, parseTimingFunctions);
const key_values = keyValuesFor(comp, tfProp, (value: number) => [value]);
if (key_values.filter(({data:[value]}) => value !== 0).length > 0) {
kfAnimGroupPropRotationXAnim = {property: 'X_ROTATION', key_values, timing_curves};
}
} break;

case 'ADBE Rotate Y': {
const timing_curves = parseTimingFunctionsFromKeyframes(tfProp.keyframes, parseTimingFunctions);
const key_values = keyValuesFor(comp, tfProp, (value: number) => [value]);
if (key_values.filter(({data:[value]}) => value !== 0).length > 0) {
kfAnimGroupPropRotationYAnim = {property: 'Y_ROTATION', key_values, timing_curves};
}
} break;

default:
warnIfUsingMissingFeature(tfProp.isModified, tfProp.__type__, tfProp, transformGroup, layer, comp);
}
Expand All @@ -571,6 +590,8 @@ function parseTransformGroup(
kfAnimGroupPropYPositionAnim && animations.push(kfAnimGroupPropYPositionAnim);
kfAnimGroupPropScaleAnim && animations.push(kfAnimGroupPropScaleAnim);
kfAnimGroupPropRotationAnim && animations.push(kfAnimGroupPropRotationAnim);
kfAnimGroupPropRotationXAnim && animations.push(kfAnimGroupPropRotationXAnim);
kfAnimGroupPropRotationYAnim && animations.push(kfAnimGroupPropRotationYAnim);
kfAnimGroupPropOpacityAnim && animations.push(kfAnimGroupPropOpacityAnim);
return animations;
}
Expand Down
55 changes: 39 additions & 16 deletions scripts/(lib)/keyframes/AECompToKeyframesAnimation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
*/
'use strict';var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[typeof Symbol==='function'?Symbol.iterator:'@@iterator'](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"])_i["return"]();}finally{if(_d)throw _e;}}return _arr;}return function(arr,i){if(Array.isArray(arr)){return arr;}else if((typeof Symbol==='function'?Symbol.iterator:'@@iterator')in Object(arr)){return sliceIterator(arr,i);}else{throw new TypeError("Invalid attempt to destructure non-iterable instance");}};}();

Expand Down Expand Up @@ -161,7 +163,7 @@ throw'Root Vectors Group missing, corrupted input JSON';



parseRootVectorsGroup(rootVectorsGroup);var vectorShape=_parseRootVectorsGrou.vectorShape;var vectorFillColor=_parseRootVectorsGrou.vectorFillColor;var vectorStrokeColor=_parseRootVectorsGrou.vectorStrokeColor;var vectorStrokeWidth=_parseRootVectorsGrou.vectorStrokeWidth;var vectorStrokeLineCap=_parseRootVectorsGrou.vectorStrokeLineCap;var vectorPosition=_parseRootVectorsGrou.vectorPosition;var vectorScale=_parseRootVectorsGrou.vectorScale;var vectorRotation=_parseRootVectorsGrou.vectorRotation;var vectorOpacity=_parseRootVectorsGrou.vectorOpacity;
parseRootVectorsGroup(rootVectorsGroup),vectorShape=_parseRootVectorsGrou.vectorShape,vectorFillColor=_parseRootVectorsGrou.vectorFillColor,vectorStrokeColor=_parseRootVectorsGrou.vectorStrokeColor,vectorStrokeWidth=_parseRootVectorsGrou.vectorStrokeWidth,vectorStrokeLineCap=_parseRootVectorsGrou.vectorStrokeLineCap,vectorPosition=_parseRootVectorsGrou.vectorPosition,vectorScale=_parseRootVectorsGrou.vectorScale,vectorRotation=_parseRootVectorsGrou.vectorRotation,vectorOpacity=_parseRootVectorsGrou.vectorOpacity;

var shapeOffset=[0,0];

Expand All @@ -173,11 +175,11 @@ shapeOffset=[vectorPosition.value[0],vectorPosition.value[1]];
}
if(vectorScale){
var key_values=keyValuesFor(comp,vectorScale,function(value){return[value[0],value[1]];});
warnIfUsingMissingFeature(key_values.filter(function(_ref){var _ref$data=_slicedToArray(_ref.data,2);var x=_ref$data[0];var y=_ref$data[1];return x!==100||y!==100;}).length>0,'Scale on vector',vectorScale,rootVectorsGroup,layer,comp);
warnIfUsingMissingFeature(key_values.filter(function(_ref){var _ref$data=_slicedToArray(_ref.data,2),x=_ref$data[0],y=_ref$data[1];return x!==100||y!==100;}).length>0,'Scale on vector',vectorScale,rootVectorsGroup,layer,comp);
}
if(vectorRotation){
var _key_values=keyValuesFor(comp,vectorRotation,function(value){return[value%360];});
warnIfUsingMissingFeature(_key_values.filter(function(_ref2){var _ref2$data=_slicedToArray(_ref2.data,1);var value=_ref2$data[0];return value!==0;}).length>0,'Rotation on vector',vectorRotation,rootVectorsGroup,layer,comp);
warnIfUsingMissingFeature(_key_values.filter(function(_ref2){var _ref2$data=_slicedToArray(_ref2.data,1),value=_ref2$data[0];return value!==0;}).length>0,'Rotation on vector',vectorRotation,rootVectorsGroup,layer,comp);
}

if(vectorStrokeLineCap){
Expand Down Expand Up @@ -242,7 +244,7 @@ function getHexColorStringFromRGB(_ref3)



{var _ref4=_slicedToArray(_ref3,4);var r=_ref4[0];var g=_ref4[1];var b=_ref4[2];var a=_ref4[3];
{var _ref4=_slicedToArray(_ref3,4),r=_ref4[0],g=_ref4[1],b=_ref4[2],a=_ref4[3];
return"#"+componentToHex(a)+componentToHex(r)+componentToHex(g)+componentToHex(b);
}

Expand Down Expand Up @@ -336,7 +338,7 @@ function hasTangent(_ref5)



{var _ref6=_slicedToArray(_ref5,2);var x=_ref6[0];var y=_ref6[1];
{var _ref6=_slicedToArray(_ref5,2),x=_ref6[0],y=_ref6[1];
return!!x||!!y;
}

Expand All @@ -363,7 +365,7 @@ shapeOffset)
inTangents=


shape.inTangents;var outTangents=shape.outTangents;var closed=shape.closed;
shape.inTangents,outTangents=shape.outTangents,closed=shape.closed;

var vertices=
shape.vertices.map(function(vertex){return[vertex[0]+shapeOffset[0],vertex[1]+shapeOffset[1]];});
Expand Down Expand Up @@ -490,13 +492,16 @@ data:[0]}],
timing_curves:[]};

var kfAnimGroupPropRotationAnim=void 0;
var kfAnimGroupPropRotationXAnim=void 0;
var kfAnimGroupPropRotationYAnim=void 0;
var kfAnimGroupPropScaleAnim=void 0;
var kfAnimGroupPropOpacityAnim=void 0;

var animations=[];

transformGroup.properties.forEach(function(tfProp){
warnIfUsingMissingFeature(!!tfProp.expression,'expression',tfProp,transformGroup,layer,comp);

switch(tfProp.matchName){

case'ADBE Anchor Point':{
Expand All @@ -509,34 +514,34 @@ case'ADBE Position':{
var _timing_curves=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var x_key_values=keyValuesFor(comp,tfProp,function(value){return[value[0]];});
var y_key_values=keyValuesFor(comp,tfProp,function(value){return[value[1]];});
if(x_key_values.filter(function(_ref7){var _ref7$data=_slicedToArray(_ref7.data,1);var value=_ref7$data[0];return value!==0;}).length>0){
if(x_key_values.filter(function(_ref7){var _ref7$data=_slicedToArray(_ref7.data,1),value=_ref7$data[0];return value!==0;}).length>0){
kfAnimGroupPropXPositionAnim={property:'X_POSITION',key_values:x_key_values,timing_curves:_timing_curves};
}
if(y_key_values.filter(function(_ref8){var _ref8$data=_slicedToArray(_ref8.data,1);var value=_ref8$data[0];return value!==0;}).length>0){
if(y_key_values.filter(function(_ref8){var _ref8$data=_slicedToArray(_ref8.data,1),value=_ref8$data[0];return value!==0;}).length>0){
kfAnimGroupPropYPositionAnim={property:'Y_POSITION',key_values:y_key_values,timing_curves:_timing_curves};
}
}break;

case'ADBE Position_0':{
var _timing_curves2=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var _key_values2=keyValuesFor(comp,tfProp,function(value){return[value];});
if(_key_values2.filter(function(_ref9){var _ref9$data=_slicedToArray(_ref9.data,1);var value=_ref9$data[0];return value!==0;}).length>0){
if(_key_values2.filter(function(_ref9){var _ref9$data=_slicedToArray(_ref9.data,1),value=_ref9$data[0];return value!==0;}).length>0){
kfAnimGroupPropXPositionAnim={property:'X_POSITION',key_values:_key_values2,timing_curves:_timing_curves2};
}
}break;

case'ADBE Position_1':{
var _timing_curves3=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var _key_values3=keyValuesFor(comp,tfProp,function(value){return[value];});
if(_key_values3.filter(function(_ref10){var _ref10$data=_slicedToArray(_ref10.data,1);var value=_ref10$data[0];return value!==0;}).length>0){
if(_key_values3.filter(function(_ref10){var _ref10$data=_slicedToArray(_ref10.data,1),value=_ref10$data[0];return value!==0;}).length>0){
kfAnimGroupPropYPositionAnim={property:'Y_POSITION',key_values:_key_values3,timing_curves:_timing_curves3};
}
}break;

case'ADBE Scale':{
var _timing_curves4=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var _key_values4=keyValuesFor(comp,tfProp,function(value){return[value[0],value[1]];});
if(_key_values4.filter(function(_ref11){var _ref11$data=_slicedToArray(_ref11.data,2);var x=_ref11$data[0];var y=_ref11$data[1];return x!==100||y!==100;}).length>0){
if(_key_values4.filter(function(_ref11){var _ref11$data=_slicedToArray(_ref11.data,2),x=_ref11$data[0],y=_ref11$data[1];return x!==100||y!==100;}).length>0){
kfAnimGroupPropScaleAnim={property:'SCALE',key_values:_key_values4,timing_curves:_timing_curves4};
}
}break;
Expand All @@ -545,7 +550,7 @@ case'ADBE Opacity':{
if(layer.matchName==='ADBE Vector Layer'){
var _timing_curves5=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var _key_values5=keyValuesFor(comp,tfProp,function(value){return[value];});
if(_key_values5.filter(function(_ref12){var _ref12$data=_slicedToArray(_ref12.data,1);var value=_ref12$data[0];return value!==100;}).length>0){
if(_key_values5.filter(function(_ref12){var _ref12$data=_slicedToArray(_ref12.data,1),value=_ref12$data[0];return value!==100;}).length>0){
kfAnimGroupPropOpacityAnim={property:'OPACITY',key_values:_key_values5,timing_curves:_timing_curves5};
}
}
Expand All @@ -554,11 +559,27 @@ kfAnimGroupPropOpacityAnim={property:'OPACITY',key_values:_key_values5,timing_cu
case'ADBE Rotate Z':{
var _timing_curves6=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var _key_values6=keyValuesFor(comp,tfProp,function(value){return[value];});
if(_key_values6.filter(function(_ref13){var _ref13$data=_slicedToArray(_ref13.data,1);var value=_ref13$data[0];return value%360!==0;}).length>0){
if(_key_values6.filter(function(_ref13){var _ref13$data=_slicedToArray(_ref13.data,1),value=_ref13$data[0];return value!==0;}).length>0){
kfAnimGroupPropRotationAnim={property:'ROTATION',key_values:_key_values6,timing_curves:_timing_curves6};
}
}break;

case'ADBE Rotate X':{
var _timing_curves7=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var _key_values7=keyValuesFor(comp,tfProp,function(value){return[value];});
if(_key_values7.filter(function(_ref14){var _ref14$data=_slicedToArray(_ref14.data,1),value=_ref14$data[0];return value!==0;}).length>0){
kfAnimGroupPropRotationXAnim={property:'X_ROTATION',key_values:_key_values7,timing_curves:_timing_curves7};
}
}break;

case'ADBE Rotate Y':{
var _timing_curves8=parseTimingFunctionsFromKeyframes(tfProp.keyframes,parseTimingFunctions);
var _key_values8=keyValuesFor(comp,tfProp,function(value){return[value];});
if(_key_values8.filter(function(_ref15){var _ref15$data=_slicedToArray(_ref15.data,1),value=_ref15$data[0];return value!==0;}).length>0){
kfAnimGroupPropRotationYAnim={property:'Y_ROTATION',key_values:_key_values8,timing_curves:_timing_curves8};
}
}break;

default:
warnIfUsingMissingFeature(tfProp.isModified,tfProp.__type__,tfProp,transformGroup,layer,comp);}

Expand All @@ -569,6 +590,8 @@ kfAnimGroupPropXPositionAnim&&animations.push(kfAnimGroupPropXPositionAnim);
kfAnimGroupPropYPositionAnim&&animations.push(kfAnimGroupPropYPositionAnim);
kfAnimGroupPropScaleAnim&&animations.push(kfAnimGroupPropScaleAnim);
kfAnimGroupPropRotationAnim&&animations.push(kfAnimGroupPropRotationAnim);
kfAnimGroupPropRotationXAnim&&animations.push(kfAnimGroupPropRotationXAnim);
kfAnimGroupPropRotationYAnim&&animations.push(kfAnimGroupPropRotationYAnim);
kfAnimGroupPropOpacityAnim&&animations.push(kfAnimGroupPropOpacityAnim);
return animations;
}
Expand Down Expand Up @@ -674,7 +697,7 @@ tfProp,
convert)
{
var keyValues=[];
tfProp.keyframes&&tfProp.keyframes.forEach(function(_ref14){var time=_ref14.time;var value=_ref14.value;
tfProp.keyframes&&tfProp.keyframes.forEach(function(_ref16){var time=_ref16.time,value=_ref16.value;
var data=convert(value);
keyValues.push({start_frame:Math.round(time*comp.frameRate),data:data});
});
Expand All @@ -689,10 +712,10 @@ function warnIfUsingMissingFeature(shouldWarn,feature){
if(!shouldWarn){
return;
}for(var _len=arguments.length,objects=Array(_len>2?_len-2:0),_key=2;_key<_len;_key++){objects[_key-2]=arguments[_key];}
var keyPath=objects.map(function(_ref15){var name=_ref15.name;return name;}).reverse().concat(feature);
var keyPath=objects.map(function(_ref17){var name=_ref17.name;return name;}).reverse().concat(feature);
var comp=objects[objects.length-1];
keyPath.unshift(comp&&comp.parentFolder$name);
console.warn('UNSUPPORTED: %s',keyPath.join(' → '));
}

module.exports=AECompToKeyframesAnimation;
module.exports=AECompToKeyframesAnimation;