Merge branch 'feature/animation' into 'master'
Feature/animation See merge request !35
This commit is contained in:
commit
abd9fcdd0a
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package pub.doric;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.content.Context;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
@ -161,6 +163,7 @@ public class DoricContext {
|
||||
|
||||
public void reload(String script) {
|
||||
this.script = script;
|
||||
this.mRootNode.setId("");
|
||||
getDriver().createContext(mContextId, script, source);
|
||||
callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams);
|
||||
}
|
||||
@ -192,4 +195,15 @@ public class DoricContext {
|
||||
public IDoricNavBar getDoricNavBar() {
|
||||
return this.doricNavBar;
|
||||
}
|
||||
|
||||
private AnimatorSet animatorSet;
|
||||
|
||||
public void setAnimatorSet(AnimatorSet animatorSet) {
|
||||
this.animatorSet = animatorSet;
|
||||
}
|
||||
|
||||
public AnimatorSet getAnimatorSet() {
|
||||
return this.animatorSet;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import android.text.TextUtils;
|
||||
import pub.doric.loader.DoricAssetJSLoader;
|
||||
import pub.doric.loader.DoricHttpJSLoader;
|
||||
import pub.doric.loader.IDoricJSLoader;
|
||||
import pub.doric.plugin.AnimatePlugin;
|
||||
import pub.doric.plugin.NavBarPlugin;
|
||||
import pub.doric.plugin.NavigatorPlugin;
|
||||
import pub.doric.plugin.NetworkPlugin;
|
||||
@ -89,6 +90,7 @@ public class DoricRegistry {
|
||||
this.registerNativePlugin(NavigatorPlugin.class);
|
||||
this.registerNativePlugin(NavBarPlugin.class);
|
||||
this.registerNativePlugin(PopoverPlugin.class);
|
||||
this.registerNativePlugin(AnimatePlugin.class);
|
||||
|
||||
this.registerViewNode(RootNode.class);
|
||||
this.registerViewNode(TextNode.class);
|
||||
|
@ -0,0 +1,86 @@
|
||||
package pub.doric.plugin;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
import pub.doric.shader.RootNode;
|
||||
import pub.doric.shader.ViewNode;
|
||||
import pub.doric.utils.DoricLog;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-29
|
||||
*/
|
||||
@DoricPlugin(name = "animate")
|
||||
public class AnimatePlugin extends DoricJavaPlugin {
|
||||
public AnimatePlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void submit(DoricPromise promise) {
|
||||
promise.resolve();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void animateRender(final JSObject jsObject, final DoricPromise promise) {
|
||||
getDoricContext().getDriver().asyncCall(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
final long duration = jsObject.getProperty("duration").asNumber().toLong();
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
getDoricContext().setAnimatorSet(animatorSet);
|
||||
String viewId = jsObject.getProperty("id").asString().value();
|
||||
RootNode rootNode = getDoricContext().getRootNode();
|
||||
if (TextUtils.isEmpty(rootNode.getId())) {
|
||||
rootNode.setId(viewId);
|
||||
rootNode.blend(jsObject.getProperty("props").asObject());
|
||||
} else {
|
||||
ViewNode viewNode = getDoricContext().targetViewNode(viewId);
|
||||
if (viewNode != null) {
|
||||
viewNode.blend(jsObject.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
getDoricContext().setAnimatorSet(null);
|
||||
animatorSet.setDuration(duration);
|
||||
animatorSet.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
promise.resolve();
|
||||
}
|
||||
});
|
||||
animatorSet.start();
|
||||
return null;
|
||||
}
|
||||
}, ThreadMode.UI).setCallback(new AsyncResult.Callback<Object>() {
|
||||
@Override
|
||||
public void onResult(Object result) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
t.printStackTrace();
|
||||
DoricLog.e("Shader.render:error%s", t.getLocalizedMessage());
|
||||
promise.reject(new JavaValue(t.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -174,7 +174,11 @@ public class ShaderPlugin extends DoricJavaPlugin {
|
||||
if (clz == DoricPromise.class) {
|
||||
return doricPromise;
|
||||
} else {
|
||||
return jsValue;
|
||||
try {
|
||||
return DoricUtils.toJavaObject(clz, jsValue);
|
||||
} catch (Exception e) {
|
||||
return jsValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -912,7 +912,7 @@ public class DoricSwipeLayout extends ViewGroup implements NestedScrollingParent
|
||||
ViewCompat.offsetTopAndBottom(mRefreshView, offset);
|
||||
mCurrentTargetOffsetTop = mRefreshView.getTop();
|
||||
if (mRefreshView.getMeasuredHeight() > 0) {
|
||||
mRefreshView.setProgressRotation((float) mRefreshView.getBottom() / (float) mRefreshView.getMeasuredHeight());
|
||||
mRefreshView.setProgressRotation((float) mRefreshView.getBottom() / (float) mRefreshView.getMeasuredHeight() * 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,12 @@
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
@ -123,19 +128,72 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
||||
protected void blend(T view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "width":
|
||||
setWidth(prop.asNumber().toFloat());
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getWidth(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setWidth(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "height":
|
||||
setHeight(prop.asNumber().toFloat());
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getHeight(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setHeight(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "x":
|
||||
setX(prop.asNumber().toFloat());
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getX(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setX(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "y":
|
||||
setY(prop.asNumber().toFloat());
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getY(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setY(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "bgColor":
|
||||
view.setBackgroundColor(prop.asNumber().toInt());
|
||||
if (isAnimating()) {
|
||||
ObjectAnimator animator = ObjectAnimator.ofInt(
|
||||
this,
|
||||
name,
|
||||
getBgColor(),
|
||||
prop.asNumber().toInt());
|
||||
animator.setEvaluator(new ArgbEvaluator());
|
||||
addAnimator(animator);
|
||||
} else {
|
||||
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();
|
||||
@ -244,30 +302,6 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
||||
return mId;
|
||||
}
|
||||
|
||||
protected void setWidth(float width) {
|
||||
if (mLayoutParams.width >= 0) {
|
||||
mLayoutParams.width = DoricUtils.dp2px(width);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setHeight(float height) {
|
||||
if (mLayoutParams.height >= 0) {
|
||||
mLayoutParams.height = DoricUtils.dp2px(height);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setX(float x) {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setY(float y) {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setLayoutConfig(JSObject layoutConfig) {
|
||||
if (mSuperNode != null) {
|
||||
mSuperNode.blendSubLayoutConfig(this, layoutConfig);
|
||||
@ -329,30 +363,95 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public int getWidth() {
|
||||
return getNodeView().getWidth();
|
||||
protected boolean isAnimating() {
|
||||
return getDoricContext().getAnimatorSet() != null;
|
||||
}
|
||||
|
||||
protected void addAnimator(Animator animator) {
|
||||
if (getDoricContext().getAnimatorSet() == null) {
|
||||
return;
|
||||
}
|
||||
getDoricContext().getAnimatorSet().play(animator);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public int getHeight() {
|
||||
return getNodeView().getHeight();
|
||||
public float getWidth() {
|
||||
return DoricUtils.px2dp(getNodeView().getWidth());
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setRotation(JSValue jsValue) {
|
||||
float rotation = jsValue.asNumber().toFloat();
|
||||
while (rotation > 1) {
|
||||
rotation = rotation - 1;
|
||||
}
|
||||
while (rotation < -1) {
|
||||
rotation = rotation + 1;
|
||||
}
|
||||
getNodeView().setRotation(rotation * 360);
|
||||
public float getHeight() {
|
||||
return DoricUtils.px2dp(getNodeView().getHeight());
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setRotation(float rotation) {
|
||||
getNodeView().setRotation(rotation * 180);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getRotation() {
|
||||
return getNodeView().getRotation() / 360;
|
||||
return getNodeView().getRotation() / 180;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setWidth(float width) {
|
||||
if (mLayoutParams.width >= 0) {
|
||||
mLayoutParams.width = DoricUtils.dp2px(width);
|
||||
mView.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setHeight(float height) {
|
||||
if (mLayoutParams.height >= 0) {
|
||||
mLayoutParams.height = DoricUtils.dp2px(height);
|
||||
mView.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setX(float x) {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x);
|
||||
mView.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setY(float y) {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y);
|
||||
mView.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getX() {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getY() {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public int getBgColor() {
|
||||
if (mView.getBackground() instanceof ColorDrawable) {
|
||||
return ((ColorDrawable) mView.getBackground()).getColor();
|
||||
}
|
||||
return Color.TRANSPARENT;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setBgColor(int color) {
|
||||
mView.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
@ -14,5 +14,6 @@ export default [
|
||||
'src/NavbarDemo',
|
||||
'src/RefreshableDemo',
|
||||
'src/FlowLayoutDemo',
|
||||
'src/PopoverDemo'
|
||||
'src/PopoverDemo',
|
||||
'src/AnimatorDemo',
|
||||
]
|
128
demo/src/AnimatorDemo.ts
Normal file
128
demo/src/AnimatorDemo.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { animate, Group, Panel, gravity, Color, LayoutSpec, vlayout, scroller, layoutConfig, IVLayout, modal, IText, network, View, stack, IHLayout, hlayout, IView, text } from "doric";
|
||||
import { title, colors, box } from "./utils";
|
||||
|
||||
function thisLabel(str: string) {
|
||||
return text({
|
||||
text: str,
|
||||
width: 100,
|
||||
height: 50,
|
||||
bgColor: colors[4],
|
||||
textSize: 20,
|
||||
textColor: Color.WHITE,
|
||||
layoutConfig: layoutConfig().exactly(),
|
||||
})
|
||||
}
|
||||
|
||||
@Entry
|
||||
class AnimatorDemo extends Panel {
|
||||
build(rootView: Group): void {
|
||||
const view = box(2)
|
||||
let idx = 0
|
||||
vlayout([
|
||||
title("Animator zDemo"),
|
||||
vlayout(
|
||||
[
|
||||
hlayout([
|
||||
thisLabel('Reset').apply({
|
||||
onClick: () => {
|
||||
animate(this)({
|
||||
animations: () => {
|
||||
view.width = view.height = 20
|
||||
view.x = view.y = 0
|
||||
view.rotation = 0
|
||||
view.bgColor = colors[2]
|
||||
},
|
||||
duration: 1500,
|
||||
}).then(() => {
|
||||
modal(context).toast('Fininshed')
|
||||
}).catch(e => {
|
||||
modal(context).toast(`${e}`)
|
||||
})
|
||||
}
|
||||
}),
|
||||
thisLabel('Move X').apply({
|
||||
onClick: () => {
|
||||
animate(this)({
|
||||
animations: () => {
|
||||
view.x = view.x || 0
|
||||
view.x += 100
|
||||
},
|
||||
duration: 1000,
|
||||
})
|
||||
}
|
||||
}),
|
||||
thisLabel('Move Y').apply({
|
||||
onClick: () => {
|
||||
animate(this)({
|
||||
animations: () => {
|
||||
view.y = view.y || 0
|
||||
view.y += 100
|
||||
},
|
||||
duration: 1000,
|
||||
})
|
||||
}
|
||||
}),
|
||||
]).apply({ space: 10 } as IHLayout),
|
||||
hlayout([
|
||||
thisLabel('Width').apply({
|
||||
onClick: () => {
|
||||
animate(this)({
|
||||
animations: () => {
|
||||
view.width += 100
|
||||
},
|
||||
duration: 1000,
|
||||
})
|
||||
}
|
||||
}),
|
||||
thisLabel('Height').apply({
|
||||
onClick: () => {
|
||||
animate(this)({
|
||||
animations: () => {
|
||||
view.height += 100
|
||||
},
|
||||
duration: 1000,
|
||||
})
|
||||
}
|
||||
}),
|
||||
]).apply({ space: 10 } as IHLayout),
|
||||
hlayout([
|
||||
thisLabel('BgColor').apply({
|
||||
onClick: () => {
|
||||
animate(this)({
|
||||
animations: () => {
|
||||
view.bgColor = colors[(idx++) % colors.length]
|
||||
},
|
||||
duration: 1000,
|
||||
});
|
||||
}
|
||||
}),
|
||||
thisLabel('Rotation').apply({
|
||||
onClick: () => {
|
||||
animate(this)({
|
||||
animations: () => {
|
||||
if (view.rotation) {
|
||||
view.rotation += 0.5
|
||||
} else {
|
||||
view.rotation = 0.5
|
||||
}
|
||||
},
|
||||
duration: 1000,
|
||||
});
|
||||
}
|
||||
}),
|
||||
]).apply({ space: 10 } as IHLayout),
|
||||
]
|
||||
).apply({ space: 10 } as IVLayout),
|
||||
stack([
|
||||
view
|
||||
]).apply({
|
||||
layoutConfig: layoutConfig().atmost(),
|
||||
bgColor: colors[1].alpha(0.3 * 255),
|
||||
}),
|
||||
]).apply({
|
||||
layoutConfig: layoutConfig().atmost(),
|
||||
gravity: gravity().center(),
|
||||
space: 10,
|
||||
} as IVLayout).in(rootView)
|
||||
}
|
||||
}
|
@ -17,7 +17,11 @@ function fromDir(startPath, filter) {
|
||||
fromDir(filename, filter);
|
||||
}
|
||||
else if (filename.indexOf(filter) >= 0) {
|
||||
doMerge(startPath, files[i])
|
||||
try {
|
||||
doMerge(startPath, files[i])
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -82,6 +82,7 @@ - (void)initContextWithWidth:(CGFloat)width height:(CGFloat)height {
|
||||
}
|
||||
|
||||
- (void)reload:(NSString *)script {
|
||||
self.rootNode.viewId = nil;
|
||||
self.script = script;
|
||||
[self.driver createContext:self.contextId script:script source:self.source];
|
||||
[self callEntity:DORIC_ENTITY_INIT, self.initialParams, nil];
|
||||
|
@ -41,6 +41,7 @@
|
||||
#import "DoricFlowLayoutItemNode.h"
|
||||
#import "DoricFlowLayoutNode.h"
|
||||
#import "DoricPopoverPlugin.h"
|
||||
#import "DoricAnimatePlugin.h"
|
||||
|
||||
@interface DoricRegistry ()
|
||||
|
||||
@ -70,6 +71,7 @@ - (void)innerRegister {
|
||||
[self registerNativePlugin:DoricNavigatorPlugin.class withName:@"navigator"];
|
||||
[self registerNativePlugin:DoricNavBarPlugin.class withName:@"navbar"];
|
||||
[self registerNativePlugin:DoricPopoverPlugin.class withName:@"popover"];
|
||||
[self registerNativePlugin:DoricAnimatePlugin.class withName:@"animate"];
|
||||
|
||||
[self registerViewNode:DoricStackNode.class withName:@"Stack"];
|
||||
[self registerViewNode:DoricVLayoutNode.class withName:@"VLayout"];
|
||||
|
10
iOS/Pod/Classes/Plugin/DoricAnimatePlugin.h
Normal file
10
iOS/Pod/Classes/Plugin/DoricAnimatePlugin.h
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by pengfei.zhou on 2019/11/29.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "DoricNativePlugin.h"
|
||||
|
||||
@interface DoricAnimatePlugin : DoricNativePlugin
|
||||
@end
|
33
iOS/Pod/Classes/Plugin/DoricAnimatePlugin.m
Normal file
33
iOS/Pod/Classes/Plugin/DoricAnimatePlugin.m
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Created by pengfei.zhou on 2019/11/29.
|
||||
//
|
||||
|
||||
#import "DoricAnimatePlugin.h"
|
||||
#import "DoricRootNode.h"
|
||||
|
||||
@implementation DoricAnimatePlugin
|
||||
|
||||
- (void)submit:(NSDictionary *)args withPromise:(DoricPromise *)promise {
|
||||
[promise resolve:nil];
|
||||
}
|
||||
|
||||
- (void)animateRender:(NSDictionary *)args withPromise:(DoricPromise *)promise {
|
||||
NSNumber *duration = args[@"duration"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSString *viewId = args[@"id"];
|
||||
[UIView animateWithDuration:[duration floatValue] / 1000
|
||||
animations:^{
|
||||
if (self.doricContext.rootNode.viewId == nil) {
|
||||
self.doricContext.rootNode.viewId = viewId;
|
||||
[self.doricContext.rootNode blend:args[@"props"]];
|
||||
} else {
|
||||
DoricViewNode *viewNode = [self.doricContext targetViewNode:viewId];
|
||||
[viewNode blend:args[@"props"]];
|
||||
}
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[promise resolve:nil];
|
||||
}];
|
||||
});
|
||||
}
|
||||
@end
|
@ -23,7 +23,6 @@
|
||||
#import "DoricShaderPlugin.h"
|
||||
#import "DoricRootNode.h"
|
||||
#import "DoricUtil.h"
|
||||
#import "Doric.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
|
@ -109,7 +109,7 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
if (scrollView.contentOffset.y <= 0) {
|
||||
[self.swipePullingDelegate setProgressRotation:-scrollView.contentOffset.y / self.headerView.height];
|
||||
[self.swipePullingDelegate setProgressRotation:-scrollView.contentOffset.y / self.headerView.height * 2];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +123,8 @@ - (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];
|
||||
@ -238,13 +240,13 @@ - (void)setRotation:(NSNumber *)rotation {
|
||||
if (rotation.floatValue == 0) {
|
||||
self.view.transform = CGAffineTransformIdentity;
|
||||
} else {
|
||||
self.view.transform = CGAffineTransformMakeRotation(M_PI * rotation.floatValue * 2);
|
||||
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 / 2);
|
||||
float degree = (float) (radius / M_PI);
|
||||
return @(degree);
|
||||
}
|
||||
|
||||
|
48
js-framework/src/native/animate.ts
Normal file
48
js-framework/src/native/animate.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Panel } from "../ui/panel"
|
||||
import { takeLet } from "../pattern/candies"
|
||||
|
||||
export function animate(panel: Panel) {
|
||||
return (args: {
|
||||
animations: () => void,
|
||||
duration: number,
|
||||
}) => {
|
||||
return takeLet(panel.context.animate)(it => {
|
||||
return it.submit().then(() => {
|
||||
args.animations()
|
||||
return takeLet(panel.getRootView())(root => {
|
||||
if (root.isDirty()) {
|
||||
const model = root.toModel();
|
||||
(model as any).duration = args.duration
|
||||
const ret = it.animateRender(model)
|
||||
root.clean()
|
||||
return ret
|
||||
}
|
||||
for (let v of panel.allHeadViews()) {
|
||||
if (v.isDirty()) {
|
||||
const model = v.toModel()
|
||||
const ret = it.animateRender(model)
|
||||
it.clean()
|
||||
return ret
|
||||
}
|
||||
}
|
||||
throw new Error('Cannot find any animated elements')
|
||||
})
|
||||
})
|
||||
}) as Promise<any>
|
||||
}
|
||||
}
|
@ -19,3 +19,4 @@ export * from './navigator'
|
||||
export * from './network'
|
||||
export * from './storage'
|
||||
export * from './popover'
|
||||
export * from './animate'
|
||||
|
154
js-framework/src/ui/animation.ts
Normal file
154
js-framework/src/ui/animation.ts
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export enum RepeatMode {
|
||||
RESTART,
|
||||
REVERSE,
|
||||
}
|
||||
|
||||
export class Animation {
|
||||
duration = 100
|
||||
startDelay = 0
|
||||
repeatCount = 1
|
||||
repeatMode = RepeatMode.RESTART
|
||||
}
|
||||
|
||||
export class AnimationSetBuilder {
|
||||
currentNode: Node
|
||||
group: AnimationSet
|
||||
|
||||
constructor(group: AnimationSet, anim: Animation) {
|
||||
this.currentNode = group.getNodeForAnimation(anim)
|
||||
this.group = group
|
||||
}
|
||||
|
||||
with(animation: Animation) {
|
||||
const node = this.group.getNodeForAnimation(animation)
|
||||
this.currentNode.addSibling(node)
|
||||
return this
|
||||
}
|
||||
|
||||
after(animation: Animation) {
|
||||
const node = this.group.getNodeForAnimation(animation)
|
||||
this.currentNode.addParent(node)
|
||||
return this
|
||||
}
|
||||
|
||||
before(animation: Animation) {
|
||||
const node = this.group.getNodeForAnimation(animation)
|
||||
this.currentNode.addChild(node)
|
||||
return this
|
||||
}
|
||||
}
|
||||
class Node {
|
||||
children: Set<Node> = new Set
|
||||
siblings: Set<Node> = new Set
|
||||
parents: Set<Node> = new Set
|
||||
animation: Animation
|
||||
built = false
|
||||
|
||||
constructor(anim: Animation) {
|
||||
this.animation = anim
|
||||
}
|
||||
|
||||
addParent(node: Node) {
|
||||
if (!this.parents.has(node)) {
|
||||
this.parents.add(node);
|
||||
node.addChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
addSibling(node: Node) {
|
||||
if (!this.siblings.has(node)) {
|
||||
this.siblings.add(node);
|
||||
node.addSibling(this);
|
||||
}
|
||||
}
|
||||
|
||||
addChild(node: Node) {
|
||||
if (!this.children.has(node)) {
|
||||
this.children.add(node)
|
||||
node.addParent(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
export class AnimationSet {
|
||||
nodeMap: Map<Animation, Node> = new Map
|
||||
nodes: Node[] = []
|
||||
|
||||
getNodeForAnimation(anim: Animation) {
|
||||
let node = this.nodeMap.get(anim)
|
||||
if (node === undefined) {
|
||||
node = new Node(anim)
|
||||
this.nodeMap.set(anim, node)
|
||||
this.nodes.push(node)
|
||||
}
|
||||
return node;
|
||||
}
|
||||
play(animation: Animation) {
|
||||
return new AnimationSetBuilder(this, animation)
|
||||
}
|
||||
|
||||
playTogether(animations: Animation[]) {
|
||||
if (animations.length == 1) {
|
||||
this.play(animations[0]);
|
||||
} else {
|
||||
for (let i = 0; i < animations.length - 1; i++) {
|
||||
this.play(animations[i]).with(animations[i + 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playSequentially(animations: Animation[]) {
|
||||
if (animations.length == 1) {
|
||||
this.play(animations[0]);
|
||||
} else {
|
||||
for (let i = 0; i < animations.length - 1; i++) {
|
||||
this.play(animations[i]).before(animations[i + 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
findSiblings(node: Node, siblings: Set<Node>) {
|
||||
if (!siblings.has(node)) {
|
||||
siblings.add(node)
|
||||
node.siblings.forEach(e => {
|
||||
this.findSiblings(e, siblings)
|
||||
})
|
||||
}
|
||||
}
|
||||
createDependencyGraph() {
|
||||
this.nodes.forEach(node => {
|
||||
if (node.built) {
|
||||
return
|
||||
}
|
||||
this.findSiblings(node, node.siblings)
|
||||
node.siblings.delete(node)
|
||||
node.siblings.forEach(e => {
|
||||
e.parents.forEach(p => {
|
||||
node.addParent(p)
|
||||
})
|
||||
})
|
||||
node.built = true
|
||||
|
||||
node.siblings.forEach(s => {
|
||||
node.parents.forEach(p => {
|
||||
s.addParent(p)
|
||||
})
|
||||
s.built = true
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -14,4 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './view'
|
||||
export * from './panel'
|
||||
export * from './panel'
|
||||
export * from './animation'
|
@ -49,7 +49,9 @@ export abstract class Panel {
|
||||
addHeadView(v: View) {
|
||||
this.headviews.set(v.viewId, v)
|
||||
}
|
||||
|
||||
allHeadViews() {
|
||||
return this.headviews.values()
|
||||
}
|
||||
removeHeadView(v: View | string) {
|
||||
if (v instanceof View) {
|
||||
this.headviews.delete(v.viewId)
|
||||
|
@ -61,6 +61,9 @@ 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 }
|
||||
|
||||
|
@ -62,6 +62,10 @@ export class Color implements Modeling {
|
||||
}
|
||||
}
|
||||
|
||||
alpha(v: number) {
|
||||
return new Color((this._value & 0xffffff) | ((v & 0xff) << 24))
|
||||
}
|
||||
|
||||
toModel() {
|
||||
return this._value
|
||||
}
|
||||
|
Reference in New Issue
Block a user