From e7ced92281ae7777bacd8c34c949b7fd8c66f5d1 Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Thu, 22 Apr 2021 11:46:24 +0800 Subject: [PATCH] feat:Add cancelAnimation and clearAnimation --- .../main/java/pub/doric/shader/ViewNode.java | 18 +-- doric-demo/src/AnimationsDemo.ts | 11 +- doric-iOS/Pod/Classes/Shader/DoricViewNode.m | 153 ++++++++++-------- doric-js/bundle/doric-lib.es5.js | 13 ++ doric-js/bundle/doric-lib.js | 7 +- doric-js/bundle/doric-vm.js | 7 +- doric-js/index.d.ts | 3 +- doric-js/lib/src/ui/view.d.ts | 3 +- doric-js/lib/src/ui/view.js | 7 +- doric-js/src/ui/view.es5.ts | 4 + 10 files changed, 132 insertions(+), 94 deletions(-) diff --git a/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java b/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java index 56ba9e24..6c464ab6 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -920,20 +920,10 @@ public abstract class ViewNode extends DoricContextHolder { if (animator != null) { String animatorId = value.asObject().getProperty("id").asString().value(); animators.put(animatorId, animator); -// final float tx = getTranslationX(); -// final float ty = getTranslationY(); -// final float sx = getScaleX(); -// final float sy = getScaleY(); -// final float r = getRotation(); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); -// setTranslationX(tx); -// setTranslationY(ty); -// setScaleX(sx); -// setScaleY(sy); -// setRotation(r); promise.reject(new JavaValue("Animation cancelled")); } @@ -956,10 +946,8 @@ public abstract class ViewNode extends DoricContextHolder { Animator animator = animators.get(id); if (animator != null) { animator.cancel(); - promise.resolve(); - } else { - promise.reject(new JavaValue("Cannot find running animation for " + id)); } + promise.resolve(); } @DoricMethod @@ -967,10 +955,8 @@ public abstract class ViewNode extends DoricContextHolder { Animator animator = animators.get(id); if (animator != null) { animator.cancel(); - promise.resolve(); - } else { - promise.reject(new JavaValue("Cannot find running animation for " + id)); } + promise.resolve(); } private Animator parseAnimator(JSValue value) { diff --git a/doric-demo/src/AnimationsDemo.ts b/doric-demo/src/AnimationsDemo.ts index 36563f3f..dd308326 100644 --- a/doric-demo/src/AnimationsDemo.ts +++ b/doric-demo/src/AnimationsDemo.ts @@ -18,21 +18,30 @@ class Animations extends Panel { height: 20, backgroundColor: Color.BLUE, }), + text({ text: "Start", onClick: () => { view.doAnimation(context, animation) } }), + text({ text: "Cancel", onClick: () => { view.cancelAnimation(context, animation) } }), + + text({ + text: "Clear", + onClick: () => { + view.clearAnimation(context, animation) + } + }), ], { - space: 10, + space: 20, layoutConfig: layoutConfig().most(), gravity: Gravity.Center }).in(root) diff --git a/doric-iOS/Pod/Classes/Shader/DoricViewNode.m b/doric-iOS/Pod/Classes/Shader/DoricViewNode.m index 38fdcf8f..9ce4fc60 100644 --- a/doric-iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/doric-iOS/Pod/Classes/Shader/DoricViewNode.m @@ -35,6 +35,10 @@ @interface AnimationCallback : NSObject @property(nonatomic, strong) void (^startBlock)(AnimationCallback *callback); @property(nonatomic, strong) void (^endBlock)(AnimationCallback *callback); + +@property(nonatomic, strong) void (^cancelBlock)(void); +@property(nonatomic, strong) void (^clearBlock)(void); + @end @implementation AnimationCallback @@ -507,37 +511,75 @@ - (NSDictionary *)transformation { return dictionary; } +- (void)setTransformation:(NSDictionary *)dictionary { + self.translationX = dictionary[@"translationX"]; + self.translationY = dictionary[@"translationY"]; + self.scaleX = dictionary[@"scaleX"]; + self.scaleY = dictionary[@"scaleY"]; + self.rotation = dictionary[@"rotation"]; +}; + #pragma animations - (void)doAnimation:(id)params withPromise:(DoricPromise *)promise { CAAnimation *animation = [self parseAnimation:params]; AnimationCallback *originDelegate = animation.delegate; AnimationCallback *animationCallback = [[AnimationCallback new] also:^(AnimationCallback *it) { + __block BOOL stop = NO; it.startBlock = ^(AnimationCallback *callback) { if (originDelegate) { originDelegate.startBlock(callback); } - [self transformProperties]; }; it.endBlock = ^(AnimationCallback *callback) { + if (stop) { + return; + } if (originDelegate) { originDelegate.endBlock(callback); } - [self.view.layer removeAllAnimations]; [self transformProperties]; [promise resolve:self.transformation]; }; + it.cancelBlock = ^{ + stop = YES; + self.view.layer.transform = self.view.layer.presentationLayer.transform; + [promise reject:@"Animation cancelled"]; + }; + it.clearBlock = ^{ + stop = YES; + [promise reject:@"Animation cleared"]; + }; }]; animation.delegate = animationCallback; if (params[@"delay"]) { animation.beginTime = CACurrentMediaTime() + [params[@"delay"] floatValue] / 1000; } - animation.removedOnCompletion = NO; + animation.removedOnCompletion = YES; animation.fillMode = kCAFillModeForwards; if (animation.duration == 0) { animation.duration = FLT_MIN; } - [self.view.layer addAnimation:animation forKey:nil]; + [self.view.layer addAnimation:animation forKey:params[@"id"]]; +} + +- (void)clearAnimation:(NSString *)animationId withPromise:(DoricPromise *)promise { + CAAnimation *caAnimation = [self.view.layer animationForKey:animationId]; + if ([caAnimation.delegate isKindOfClass:AnimationCallback.class]) { + ((AnimationCallback *) caAnimation.delegate).clearBlock(); + } + [self.view.layer removeAnimationForKey:animationId]; + [promise resolve:nil]; +} + + +- (void)cancelAnimation:(NSString *)animationId withPromise:(DoricPromise *)promise { + CAAnimation *caAnimation = [self.view.layer animationForKey:animationId]; + if ([caAnimation.delegate isKindOfClass:AnimationCallback.class]) { + ((AnimationCallback *) caAnimation.delegate).cancelBlock(); + } + [self.view.layer removeAnimationForKey:animationId]; + [promise resolve:nil]; } - (CFTimeInterval)computeDurationOfAnimations:(NSArray *)animations { @@ -584,74 +626,39 @@ - (CAAnimation *)parseAnimation:(id)params { return animationGroup; } else if ([params isKindOfClass:[NSDictionary class]]) { NSArray *changeables = params[@"changeables"]; - NSString *type = params[@"type"]; - if ([@"TranslationAnimation" isEqualToString:type]) { - __block CGPoint from = self.view.layer.position; - __block CGPoint to = self.view.layer.position; - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; - [changeables forEach:^(NSDictionary *obj) { - NSString *key = obj[@"key"]; - if ([@"translationX" isEqualToString:key]) { - from.x += [obj[@"fromValue"] floatValue] - self.translationX.floatValue; - to.x += [obj[@"toValue"] floatValue] - self.translationX.floatValue; - [self setFillMode:animation - key:key - startValue:obj[@"fromValue"] - endValue:obj[@"toValue"] - fillMode:params[@"fillMode"]]; - } else if ([@"translationY" isEqualToString:key]) { - from.y += [obj[@"fromValue"] floatValue] - self.translationY.floatValue; - to.y += [obj[@"toValue"] floatValue] - self.translationY.floatValue; - [self setFillMode:animation - key:key - startValue:obj[@"fromValue"] - endValue:obj[@"toValue"] - fillMode:params[@"fillMode"]]; - } + CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; + NSMutableArray *animations = [NSMutableArray new]; - }]; - animation.fromValue = [NSValue valueWithCGPoint:from]; - animation.toValue = [NSValue valueWithCGPoint:to]; + [changeables forEach:^(NSDictionary *obj) { + CABasicAnimation *animation = [self parseChangeable:obj fillMode:params[@"fillMode"]]; if (params[@"timingFunction"]) { animation.timingFunction = [self translateToTimingFunction:params[@"timingFunction"]]; } - [self setAnimation:animation params:params]; - return animation; - } else { - CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; - NSMutableArray *animations = [NSMutableArray new]; - - [changeables forEach:^(NSDictionary *obj) { - CABasicAnimation *animation = [self parseChangeable:obj fillMode:params[@"fillMode"]]; - if (params[@"timingFunction"]) { - animation.timingFunction = [self translateToTimingFunction:params[@"timingFunction"]]; - } - [animations addObject:animation]; - }]; - animationGroup.animations = animations; - animationGroup.delegate = [[AnimationCallback new] also:^(AnimationCallback *it) { - it.startBlock = ^(AnimationCallback *callback) { - [[animations map:^id(CABasicAnimation *obj) { - return obj.delegate; - }] forEach:^(AnimationCallback *obj) { - if (obj.startBlock) { - obj.startBlock(obj); - } - }]; - }; - it.endBlock = ^(AnimationCallback *callback) { - [[animations map:^id(CABasicAnimation *obj) { - return obj.delegate; - }] forEach:^(AnimationCallback *obj) { - if (obj.endBlock) { - obj.endBlock(obj); - } - }]; - }; - }]; - [self setAnimation:animationGroup params:params]; - return animationGroup; - } + [animations addObject:animation]; + }]; + animationGroup.animations = animations; + animationGroup.delegate = [[AnimationCallback new] also:^(AnimationCallback *it) { + it.startBlock = ^(AnimationCallback *callback) { + [[animations map:^id(CABasicAnimation *obj) { + return obj.delegate; + }] forEach:^(AnimationCallback *obj) { + if (obj.startBlock) { + obj.startBlock(obj); + } + }]; + }; + it.endBlock = ^(AnimationCallback *callback) { + [[animations map:^id(CABasicAnimation *obj) { + return obj.delegate; + }] forEach:^(AnimationCallback *obj) { + if (obj.endBlock) { + obj.endBlock(obj); + } + }]; + }; + }]; + [self setAnimation:animationGroup params:params]; + return animationGroup; } return nil; } @@ -752,7 +759,15 @@ - (void)setAnimatedValue:(NSString *)key value:(NSNumber *)value { - (CABasicAnimation *)parseChangeable:(NSDictionary *)params fillMode:(NSNumber *)fillMode { NSString *key = params[@"key"]; CABasicAnimation *animation = [CABasicAnimation animation]; - if ([@"scaleX" isEqualToString:key]) { + if ([@"translationX" isEqualToString:key]) { + animation.keyPath = @"transform.translation.x"; + animation.fromValue = params[@"fromValue"]; + animation.toValue = params[@"toValue"]; + } else if ([@"translationY" isEqualToString:key]) { + animation.keyPath = @"transform.translation.y"; + animation.fromValue = params[@"fromValue"]; + animation.toValue = params[@"toValue"]; + } else if ([@"scaleX" isEqualToString:key]) { animation.keyPath = @"transform.scale.x"; animation.fromValue = params[@"fromValue"]; animation.toValue = params[@"toValue"]; diff --git a/doric-js/bundle/doric-lib.es5.js b/doric-js/bundle/doric-lib.es5.js index b20cce2b..f151257a 100644 --- a/doric-js/bundle/doric-lib.es5.js +++ b/doric-js/bundle/doric-lib.es5.js @@ -399,6 +399,19 @@ var View = /** @class */ (function () { } }); }; + View.prototype.clearAnimation = function (context, animation) { + var _this = this; + return this.nativeChannel(context, "clearAnimation")(animation.id).then(function () { + _this.__dirty_props__.translationX = _this.translationX || 0; + _this.__dirty_props__.translationY = _this.translationY || 0; + _this.__dirty_props__.scaleX = _this.scaleX || 1; + _this.__dirty_props__.scaleY = _this.scaleY || 1; + _this.__dirty_props__.rotation = _this.rotation || 0; + }); + }; + View.prototype.cancelAnimation = function (context, animation) { + return this.nativeChannel(context, "cancelAnimation")(animation.id); + }; __decorate$d([ Property, __metadata$d("design:type", Number) diff --git a/doric-js/bundle/doric-lib.js b/doric-js/bundle/doric-lib.js index 04db52d7..21fb8773 100644 --- a/doric-js/bundle/doric-lib.js +++ b/doric-js/bundle/doric-lib.js @@ -314,8 +314,8 @@ class View { } }); } - cancelAnimation(context, animation) { - return this.nativeChannel(context, "cancelAnimation")(animation.id).then(() => { + clearAnimation(context, animation) { + return this.nativeChannel(context, "clearAnimation")(animation.id).then(() => { this.__dirty_props__.translationX = this.translationX || 0; this.__dirty_props__.translationY = this.translationY || 0; this.__dirty_props__.scaleX = this.scaleX || 1; @@ -323,6 +323,9 @@ class View { this.__dirty_props__.rotation = this.rotation || 0; }); } + cancelAnimation(context, animation) { + return this.nativeChannel(context, "cancelAnimation")(animation.id); + } } __decorate$d([ Property, diff --git a/doric-js/bundle/doric-vm.js b/doric-js/bundle/doric-vm.js index c2727206..e232a7cd 100644 --- a/doric-js/bundle/doric-vm.js +++ b/doric-js/bundle/doric-vm.js @@ -1835,8 +1835,8 @@ class View { } }); } - cancelAnimation(context, animation) { - return this.nativeChannel(context, "cancelAnimation")(animation.id).then(() => { + clearAnimation(context, animation) { + return this.nativeChannel(context, "clearAnimation")(animation.id).then(() => { this.__dirty_props__.translationX = this.translationX || 0; this.__dirty_props__.translationY = this.translationY || 0; this.__dirty_props__.scaleX = this.scaleX || 1; @@ -1844,6 +1844,9 @@ class View { this.__dirty_props__.rotation = this.rotation || 0; }); } + cancelAnimation(context, animation) { + return this.nativeChannel(context, "cancelAnimation")(animation.id); + } } __decorate$d([ Property, diff --git a/doric-js/index.d.ts b/doric-js/index.d.ts index 8d1b31c2..825a9d6c 100644 --- a/doric-js/index.d.ts +++ b/doric-js/index.d.ts @@ -298,7 +298,8 @@ declare module 'doric/lib/src/ui/view' { */ flexConfig?: FlexConfig; doAnimation(context: BridgeContext, animation: IAnimation): Promise; - cancelAnimation(context: BridgeContext, animation: IAnimation): Promise; + clearAnimation(context: BridgeContext, animation: IAnimation): Promise; + cancelAnimation(context: BridgeContext, animation: IAnimation): Promise; } export abstract class Superview extends View { subviewById(id: string): View | undefined; diff --git a/doric-js/lib/src/ui/view.d.ts b/doric-js/lib/src/ui/view.d.ts index a1fd4c36..8ba9c91c 100644 --- a/doric-js/lib/src/ui/view.d.ts +++ b/doric-js/lib/src/ui/view.d.ts @@ -130,7 +130,8 @@ export declare abstract class View implements Modeling { */ flexConfig?: FlexConfig; doAnimation(context: BridgeContext, animation: IAnimation): Promise; - cancelAnimation(context: BridgeContext, animation: IAnimation): Promise; + clearAnimation(context: BridgeContext, animation: IAnimation): Promise; + cancelAnimation(context: BridgeContext, animation: IAnimation): Promise; } export declare abstract class Superview extends View { subviewById(id: string): View | undefined; diff --git a/doric-js/lib/src/ui/view.js b/doric-js/lib/src/ui/view.js index 0d4c6e05..dbb13fe8 100644 --- a/doric-js/lib/src/ui/view.js +++ b/doric-js/lib/src/ui/view.js @@ -199,8 +199,8 @@ export class View { } }); } - cancelAnimation(context, animation) { - return this.nativeChannel(context, "cancelAnimation")(animation.id).then(() => { + clearAnimation(context, animation) { + return this.nativeChannel(context, "clearAnimation")(animation.id).then(() => { this.__dirty_props__.translationX = this.translationX || 0; this.__dirty_props__.translationY = this.translationY || 0; this.__dirty_props__.scaleX = this.scaleX || 1; @@ -208,6 +208,9 @@ export class View { this.__dirty_props__.rotation = this.rotation || 0; }); } + cancelAnimation(context, animation) { + return this.nativeChannel(context, "cancelAnimation")(animation.id); + } } __decorate([ Property, diff --git a/doric-js/src/ui/view.es5.ts b/doric-js/src/ui/view.es5.ts index 3e897347..b5838b52 100644 --- a/doric-js/src/ui/view.es5.ts +++ b/doric-js/src/ui/view.es5.ts @@ -352,6 +352,10 @@ export abstract class View implements Modeling { this.__dirty_props__.rotation = this.rotation || 0 }) } + + cancelAnimation(context: BridgeContext, animation: IAnimation) { + return this.nativeChannel(context, "cancelAnimation")(animation.id) + } } export abstract class Superview extends View {