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 52bdeb8e..7af8d289 100644 --- a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -28,6 +28,7 @@ import android.widget.LinearLayout; import androidx.annotation.NonNull; +import pub.doric.Doric; import pub.doric.DoricContext; import pub.doric.DoricRegistry; import pub.doric.async.AsyncResult; @@ -184,17 +185,6 @@ public abstract class ViewNode extends DoricContextHolder { setBgColor(prop.asNumber().toInt()); } break; - case "rotation": - if (isAnimating()) { - addAnimator(ObjectAnimator.ofFloat( - this, - name, - getRotation(), - prop.asNumber().toFloat())); - } else { - setRotation(prop.asNumber().toFloat()); - } - break; case "onClick": final String functionId = prop.asString().value(); view.setOnClickListener(new View.OnClickListener() { @@ -248,6 +238,103 @@ public abstract class ViewNode extends DoricContextHolder { ); } break; + case "translationX": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getTranslationX(), + prop.asNumber().toFloat())); + } else { + setTranslationX(prop.asNumber().toFloat()); + } + break; + case "translationY": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getTranslationY(), + prop.asNumber().toFloat())); + } else { + setTranslationY(prop.asNumber().toFloat()); + } + break; + case "scaleX": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getScaleX(), + prop.asNumber().toFloat())); + } else { + setScaleX(prop.asNumber().toFloat()); + } + case "scaleY": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getScaleY(), + prop.asNumber().toFloat())); + } else { + setScaleY(prop.asNumber().toFloat()); + } + case "pivotX": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getPivotX(), + prop.asNumber().toFloat())); + } else { + setPivotX(prop.asNumber().toFloat()); + } + break; + case "pivotY": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getPivotY(), + prop.asNumber().toFloat())); + } else { + setPivotY(prop.asNumber().toFloat()); + } + break; + case "rotation": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getRotation(), + prop.asNumber().toFloat())); + } else { + setRotation(prop.asNumber().toFloat()); + } + break; + case "rotationX": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getRotationX(), + prop.asNumber().toFloat())); + } else { + setRotationX(prop.asNumber().toFloat()); + } + break; + case "rotationY": + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getRotationY(), + prop.asNumber().toFloat())); + } else { + setRotationY(prop.asNumber().toFloat()); + } + break; default: break; } @@ -392,16 +479,6 @@ public abstract class ViewNode extends DoricContextHolder { return DoricUtils.px2dp(getNodeView().getHeight()); } - @DoricMethod - public void setRotation(float rotation) { - getNodeView().setRotation(rotation * 180); - } - - @DoricMethod - public float getRotation() { - return getNodeView().getRotation() / 180; - } - @DoricMethod protected void setWidth(float width) { if (mLayoutParams.width >= 0) { @@ -473,4 +550,94 @@ public abstract class ViewNode extends DoricContextHolder { public float getCorners() { return DoricUtils.px2dp((int) requireDoricLayer().getCornerRadius()); } + + @DoricMethod + public void setTranslationX(float v) { + getNodeView().setTranslationX(DoricUtils.dp2px(v)); + } + + @DoricMethod + public float getTranslationX() { + return DoricUtils.px2dp((int) getNodeView().getTranslationX()); + } + + @DoricMethod + public void setTranslationY(float v) { + getNodeView().setTranslationY(DoricUtils.dp2px(v)); + } + + @DoricMethod + public float getTranslationY() { + return DoricUtils.px2dp((int) getNodeView().getTranslationY()); + } + + @DoricMethod + public void setScaleX(float v) { + getNodeView().setScaleX(v); + } + + @DoricMethod + public float getScaleX() { + return getNodeView().getScaleX(); + } + + @DoricMethod + public void setScaleY(float v) { + getNodeView().setScaleY(v); + } + + @DoricMethod + public float getScaleY() { + return getNodeView().getScaleY(); + } + + @DoricMethod + public void setRotation(float rotation) { + getNodeView().setRotation(rotation * 180); + } + + @DoricMethod + public float getRotation() { + return getNodeView().getRotation() / 180; + } + + @DoricMethod + public void setRotationX(float rotation) { + getNodeView().setRotationX(rotation * 180); + } + + @DoricMethod + public float getRotationX() { + return getNodeView().getRotationX() / 180; + } + + @DoricMethod + public void setRotationY(float rotation) { + getNodeView().setRotationY(rotation * 180); + } + + @DoricMethod + public float getRotationY() { + return getNodeView().getRotationY() / 180; + } + + @DoricMethod + public void setPivotX(float v) { + getNodeView().setPivotX(v * getNodeView().getWidth()); + } + + @DoricMethod + public float getPivotX() { + return getNodeView().getPivotX() / getNodeView().getWidth(); + } + + @DoricMethod + public void setPivotY(float v) { + getNodeView().setPivotY(v * getNodeView().getHeight()); + } + + @DoricMethod + public float getPivotY() { + return getNodeView().getPivotY() / getNodeView().getHeight(); + } } diff --git a/demo/src/AnimatorDemo.ts b/demo/src/AnimatorDemo.ts index 22314130..dd86961d 100644 --- a/demo/src/AnimatorDemo.ts +++ b/demo/src/AnimatorDemo.ts @@ -32,6 +32,11 @@ class AnimatorDemo extends Panel { view.rotation = 0 view.bgColor = colors[2] view.corners = 0 + view.scaleX = 1 + view.scaleY = 1 + view.translationX = 0 + view.translationY = 0 + view.rotation = 0 }, duration: 1500, }).then(() => { @@ -126,6 +131,37 @@ class AnimatorDemo extends Panel { } }), ]).apply({ space: 10 } as IHLayout), + + hlayout([ + thisLabel('scaleX').apply({ + onClick: () => { + animate(this)({ + animations: () => { + if (view.scaleX) { + view.scaleX += 0.1 + } else { + view.scaleX = 1.1 + } + }, + duration: 1000, + }); + } + }), + thisLabel('scaleY').apply({ + onClick: () => { + animate(this)({ + animations: () => { + if (view.scaleY) { + view.scaleY += 0.1 + } else { + view.scaleY = 1.1 + } + }, + duration: 1000, + }); + } + }), + ]).apply({ space: 10 } as IHLayout), ] ).apply({ space: 10 } as IVLayout), stack([ diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.m b/iOS/Pod/Classes/Shader/DoricViewNode.m index d3d28881..56e66342 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/iOS/Pod/Classes/Shader/DoricViewNode.m @@ -68,6 +68,15 @@ CGPathRef DoricCreateRoundedRectPath(CGRect bounds, @interface DoricViewNode () @property(nonatomic, strong) NSMutableDictionary *callbackIds; +@property(nonatomic, copy) NSNumber *translationX; +@property(nonatomic, copy) NSNumber *translationY; +@property(nonatomic, copy) NSNumber *scaleX; +@property(nonatomic, copy) NSNumber *scaleY; +@property(nonatomic, copy) NSNumber *rotation; +@property(nonatomic, copy) NSNumber *rotationX; +@property(nonatomic, copy) NSNumber *rotationY; +@property(nonatomic, copy) NSNumber *pivotX; +@property(nonatomic, copy) NSNumber *pivotY; @end @implementation DoricViewNode @@ -79,7 +88,6 @@ - (instancetype)initWithContext:(DoricContext *)doricContext { return self; } - - (void)initWithSuperNode:(DoricSuperNode *)superNode { if ([self isKindOfClass:[DoricSuperNode class]]) { ((DoricSuperNode *) self).reusable = superNode.reusable; @@ -104,6 +112,27 @@ - (void)blend:(NSDictionary *)props { id value = props[key]; [self blendView:self.view forPropName:key propValue:value]; } + [self transformProperties]; +} + +- (void)transformProperties { + CGAffineTransform transform = CGAffineTransformIdentity; + if (self.translationX || self.translationY) { + transform = CGAffineTransformTranslate(transform, [self.translationX floatValue] ?: 0, [self.translationY floatValue] ?: 0); + } + if (self.scaleX || self.scaleY) { + transform = CGAffineTransformScale(transform, [self.scaleX floatValue] ?: 1, [self.scaleY floatValue] ?: 1); + } + if (self.rotation) { + transform = CGAffineTransformRotate(transform, (self.rotation.floatValue ?: 0) * M_PI); + } + if (!CGAffineTransformEqualToTransform(transform, self.view.transform)) { + self.view.transform = transform; + } + if (self.pivotX || self.pivotY) { + self.view.layer.anchorPoint = CGPointMake(self.pivotX.floatValue + ?: 0.5f, self.pivotY.floatValue ?: 0.5f); + } } - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop { @@ -123,8 +152,6 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop view.y = [(NSNumber *) prop floatValue]; } else if ([name isEqualToString:@"bgColor"]) { view.backgroundColor = DoricColor(prop); - } else if ([name isEqualToString:@"rotation"]) { - [self setRotation:prop]; } else if ([name isEqualToString:@"layoutConfig"]) { if (self.superNode && [prop isKindOfClass:[NSDictionary class]]) { [self.superNode blendSubNode:self layoutConfig:prop]; @@ -181,6 +208,24 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop view.clipsToBounds = YES; } + } else if ([name isEqualToString:@"translationX"]) { + self.translationX = prop; + } else if ([name isEqualToString:@"translationY"]) { + self.translationY = prop; + } else if ([name isEqualToString:@"scaleX"]) { + self.scaleX = prop; + } else if ([name isEqualToString:@"scaleY"]) { + self.scaleY = prop; + } else if ([name isEqualToString:@"pivotX"]) { + self.pivotX = prop; + } else if ([name isEqualToString:@"pivotY"]) { + self.pivotY = prop; + } else if ([name isEqualToString:@"rotation"]) { + self.rotation = prop; + } else if ([name isEqualToString:@"rotationX"]) { + self.rotationX = prop; + } else if ([name isEqualToString:@"rotationY"]) { + self.rotationY = prop; } else { DoricLog(@"Blend View error for View Type :%@, prop is %@", self.class, name); } @@ -236,20 +281,6 @@ - (NSNumber *)getHeight { return @(self.view.height); } -- (void)setRotation:(NSNumber *)rotation { - if (rotation.floatValue == 0) { - self.view.transform = CGAffineTransformIdentity; - } else { - self.view.transform = CGAffineTransformMakeRotation(M_PI * rotation.floatValue); - } -} - -- (NSNumber *)getRotation { - float radius = atan2f((float) self.view.transform.b, (float) self.view.transform.a); - float degree = (float) (radius / M_PI); - return @(degree); -} - - (void)blendLayoutConfig:(NSDictionary *)params { [params[@"widthSpec"] also:^(NSNumber *it) { if (it) { diff --git a/js-framework/src/ui/view.ts b/js-framework/src/ui/view.ts index 0281d84c..9ce21633 100644 --- a/js-framework/src/ui/view.ts +++ b/js-framework/src/ui/view.ts @@ -42,6 +42,30 @@ export interface IView { layoutConfig?: LayoutConfig onClick?: Function identifier?: string + + /**++++++++++transform++++++++++*/ + translationX?: number + + translationY?: number + + scaleX?: number + + scaleY?: number + /** + * float [0,..1] + */ + pivotX?: number + /** + * float [0,..1] + */ + pivotY?: number + + rotation?: number + + rotationX?: number + + rotationY?: number + /**----------transform----------*/ } @@ -61,9 +85,6 @@ export abstract class View implements Modeling, IView { @Property bgColor?: Color | GradientColor - @Property - rotation?: number - @Property corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number } @@ -288,6 +309,35 @@ export abstract class View implements Modeling, IView { getRotation(context: BridgeContext) { return this.nativeChannel(context, 'getRotation')() as Promise } + + /**++++++++++transform++++++++++*/ + @Property + translationX?: number + + @Property + translationY?: number + + @Property + scaleX?: number + + @Property + scaleY?: number + + @Property + pivotX?: number + + @Property + pivotY?: number + + @Property + rotation?: number + + @Property + rotationX?: number + + @Property + rotationY?: number + /**----------transform----------*/ } export abstract class Superview extends View {