diff --git a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java index e11360c4..3aad3272 100644 --- a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -656,10 +656,15 @@ public abstract class ViewNode extends DoricContextHolder { } private Animator parseAnimator(JSValue value) { - if (value.isArray()) { + if (!value.isObject()) { + DoricLog.e("parseAnimator error"); + return null; + } + JSValue animations = value.asObject().getProperty("animations"); + if (animations.isArray()) { AnimatorSet animatorSet = new AnimatorSet(); - for (int i = 0; i < value.asArray().size(); i++) { - animatorSet.play(parseAnimator(value.asArray().get(i))); + for (int i = 0; i < animations.asArray().size(); i++) { + animatorSet.play(parseAnimator(animations.asArray().get(i))); } return animatorSet; } else if (value.isObject()) { diff --git a/demo/src/AnimatorDemo.ts b/demo/src/AnimatorDemo.ts index e3792f7b..a2a95102 100644 --- a/demo/src/AnimatorDemo.ts +++ b/demo/src/AnimatorDemo.ts @@ -1,4 +1,4 @@ -import { animate, Group, Panel, gravity, Color, AnimationSet, vlayout, scroller, layoutConfig, IVLayout, modal, IText, network, View, stack, IHLayout, hlayout, IView, text, TranslationAnimation, ScaleAnimation, RotationAnimation } from "doric"; +import { animate, Group, Panel, gravity, Color, AnimationSet, vlayout, scroller, layoutConfig, IVLayout, modal, IText, network, View, stack, IHLayout, hlayout, IView, text, TranslationAnimation, ScaleAnimation, RotationAnimation, FillMode } from "doric"; import { title, colors, box } from "./utils"; function thisLabel(str: string) { @@ -17,10 +17,13 @@ function thisLabel(str: string) { class AnimatorDemo extends Panel { build(rootView: Group): void { const view = box(2) + view.onClick = () => { + modal(context).toast('Clicked') + } const view2 = box(3) let idx = 0 vlayout([ - title("Animator zDemo"), + title("Animator Demo"), vlayout( [ hlayout([ @@ -167,27 +170,26 @@ class AnimatorDemo extends Panel { thisLabel('animationSet').apply({ onClick: () => { const animationSet = new AnimationSet - + animationSet.fillMode = FillMode.Forward + animationSet.delay = 2000 const translate = new TranslationAnimation - translate.fromTranslationX = 10 + translate.fromTranslationX = 100 translate.toTranslationX = 200 translate.fromTranslationY = 10 translate.toTranslationY = 200 - translate.duration = 3000 + translate.duration = 2000 translate.delay = 1000 const scale = new ScaleAnimation scale.fromScaleX = 1 scale.toScaleX = 5 scale.fromScaleY = 1 scale.toScaleY = 5 - scale.duration = 3000 - + //scale.delay = 1000 + scale.duration = 2000 const rotation = new RotationAnimation rotation.fromRotation = 0 - rotation.toRotation = 6 - rotation.delay = 1000 + rotation.toRotation = 6.2 rotation.duration = 3000 - animationSet.addAnimation(translate) animationSet.addAnimation(scale) animationSet.addAnimation(rotation) diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.m b/iOS/Pod/Classes/Shader/DoricViewNode.m index b78cb759..6c84979c 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/iOS/Pod/Classes/Shader/DoricViewNode.m @@ -323,14 +323,15 @@ - (void)blendLayoutConfig:(NSDictionary *)params { - (void)doAnimation:(id)params withPromise:(DoricPromise *)promise { CAAnimation *animation = [self parseAnimation:params]; - animation.removedOnCompletion = NO; - animation.fillMode = kCAFillModeForwards; AnimationCallback *animationCallback = [[AnimationCallback new] also:^(AnimationCallback *it) { it.endBlock = ^{ [promise resolve:nil]; }; }]; animation.delegate = animationCallback; + if (params[@"delay"]) { + animation.beginTime = CACurrentMediaTime() + [params[@"delay"] floatValue] / 1000; + } [self.view.layer addAnimation:animation forKey:nil]; } @@ -343,15 +344,20 @@ - (CFTimeInterval)computeDurationOfAnimations:(NSArray *)animatio } - (CAAnimation *)parseAnimation:(id)params { - if ([params isKindOfClass:[NSArray class]]) { - NSArray *anims = params; + if (params[@"animations"]) { + NSArray *anims = params[@"animations"]; CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; NSMutableArray *animations = [NSMutableArray new]; [anims forEach:^(id obj) { [animations addObject:[self parseAnimation:obj]]; }]; animationGroup.duration = [self computeDurationOfAnimations:animations]; + animationGroup.fillMode = [self translateToFillMode:params[@"fillMode"]]; + animationGroup.removedOnCompletion = [animationGroup.fillMode isEqualToString:kCAFillModeRemoved]; animationGroup.animations = animations; + if (params[@"delay"]) { + animationGroup.beginTime = [params[@"delay"] floatValue] / 1000; + } return animationGroup; } else if ([params isKindOfClass:[NSDictionary class]]) { NSArray *changeables = params[@"changeables"]; @@ -405,8 +411,8 @@ - (void)setAnimation:(CAAnimation *)animation params:(NSDictionary *)params { animation.beginTime = [params[@"delay"] floatValue] / 1000; } animation.duration = [params[@"duration"] floatValue] / 1000; - animation.removedOnCompletion = NO; - animation.fillMode = kCAFillModeForwards; + animation.fillMode = [self translateToFillMode:params[@"fillMode"]]; + animation.removedOnCompletion = [animation.fillMode isEqualToString:kCAFillModeRemoved]; } - (CAAnimation *)parseChangeable:(NSDictionary *)params { @@ -432,4 +438,17 @@ - (CAAnimation *)parseChangeable:(NSDictionary *)params { return animation; } +- (CAMediaTimingFillMode)translateToFillMode:(NSNumber *)fillMode { + switch ([fillMode integerValue]) { + case 1: + return kCAFillModeForwards; + case 2: + return kCAFillModeBackwards; + case 3: + return kCAFillModeBoth; + default: + return kCAFillModeRemoved; + } +} + @end diff --git a/js-framework/src/ui/animation.ts b/js-framework/src/ui/animation.ts index e32546a9..ed41a0d8 100644 --- a/js-framework/src/ui/animation.ts +++ b/js-framework/src/ui/animation.ts @@ -18,13 +18,16 @@ import { Modeling, Model } from "../util/types" export type AnimatedKey = "translationX" | "translationY" | "scaleX" | "scaleY" | "rotation" | "pivotX" | "pivotY" + export enum RepeatMode { RESTART = 1, REVERSE = 2, } + export interface IAnimation extends Modeling { duration: number delay?: number + fillMode: FillMode } export interface Changeable { @@ -34,6 +37,24 @@ export interface Changeable { repeatCount?: number repeatMode?: RepeatMode } +export enum FillMode { + /** + * The receiver is removed from the presentation when the animation is completed. + */ + Removed = 0, + /** + * The receiver remains visible in its final state when the animation is completed. + */ + Forward = 1, + /** + * The receiver clamps values before zero to zero when the animation is completed. + */ + Backward = 2, + /** + * The receiver clamps values at both ends of the object’s time space + */ + Both = 3, +} abstract class Animation implements IAnimation { changeables: Map = new Map @@ -41,7 +62,7 @@ abstract class Animation implements IAnimation { repeatCount?: number repeatMode?: RepeatMode delay?: number - + fillMode = FillMode.Forward toModel() { const changeables = [] for (let e of this.changeables.values()) { @@ -58,6 +79,7 @@ abstract class Animation implements IAnimation { changeables, repeatCount: this.repeatCount, repeatMode: this.repeatMode, + fillMode: this.fillMode, } } } @@ -193,7 +215,7 @@ export class AnimationSet implements IAnimation { private animations: IAnimation[] = [] _duration = 0 delay?: number - + fillMode = FillMode.Removed addAnimation(anim: IAnimation) { this.animations.push(anim) } @@ -208,8 +230,12 @@ export class AnimationSet implements IAnimation { } toModel() { - return this.animations.map(e => { - return e.toModel() - }) as Model + return { + animations: this.animations.map(e => { + return e.toModel() + }) as Model, + fillMode: this.fillMode, + delay: this.delay, + } } } \ No newline at end of file