Merge branch 'feature/animation' into 'master'

Feature/animation



See merge request !39
This commit is contained in:
pengfeizhou 2019-12-02 19:10:16 +08:00
commit afd0c2eb55
27 changed files with 969 additions and 259 deletions

View File

@ -145,22 +145,24 @@ public class ShaderPlugin extends DoricJavaPlugin {
};
AsyncResult<JavaValue> asyncResult = getDoricContext().getDriver()
.asyncCall(callable, ThreadMode.UI);
asyncResult.setCallback(new AsyncResult.Callback<JavaValue>() {
@Override
public void onResult(JavaValue result) {
doricPromise.resolve(result);
}
if (!method.getReturnType().equals(Void.TYPE)) {
asyncResult.setCallback(new AsyncResult.Callback<JavaValue>() {
@Override
public void onResult(JavaValue result) {
doricPromise.resolve(result);
}
@Override
public void onError(Throwable t) {
doricPromise.resolve(new JavaValue(t.getLocalizedMessage()));
}
@Override
public void onError(Throwable t) {
doricPromise.resolve(new JavaValue(t.getLocalizedMessage()));
}
@Override
public void onFinish() {
@Override
public void onFinish() {
}
});
}
});
}
}
}
} catch (ArchiveException e) {

View File

@ -16,6 +16,8 @@
package pub.doric.shader;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.content.Context;
@ -28,19 +30,23 @@ 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;
import pub.doric.extension.bridge.DoricMethod;
import pub.doric.extension.bridge.DoricPromise;
import pub.doric.utils.DoricContextHolder;
import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricLog;
import pub.doric.utils.DoricMetaInfo;
import pub.doric.utils.DoricUtils;
import com.github.pengfeizhou.jscore.JSArray;
import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSONBuilder;
import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JSValue;
import com.github.pengfeizhou.jscore.JavaValue;
import java.util.LinkedList;
@ -172,17 +178,17 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
setY(prop.asNumber().toFloat());
}
break;
case "bgColor":
case "backgroundColor":
if (isAnimating()) {
ObjectAnimator animator = ObjectAnimator.ofInt(
this,
name,
getBgColor(),
getBackgroundColor(),
prop.asNumber().toInt());
animator.setEvaluator(new ArgbEvaluator());
addAnimator(animator);
} else {
setBgColor(prop.asNumber().toInt());
setBackgroundColor(prop.asNumber().toInt());
}
break;
case "onClick":
@ -462,19 +468,30 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
@DoricMethod
public float getWidth() {
return DoricUtils.px2dp(getNodeView().getWidth());
if (mLayoutParams.width >= 0) {
return DoricUtils.px2dp(mLayoutParams.width);
} else {
return mView.getMeasuredWidth();
}
}
@DoricMethod
public float getHeight() {
return DoricUtils.px2dp(getNodeView().getHeight());
if (mLayoutParams.width >= 0) {
return DoricUtils.px2dp(mLayoutParams.height);
} else {
return mView.getMeasuredHeight();
}
}
@DoricMethod
protected void setWidth(float width) {
if (mLayoutParams.width >= 0) {
mLayoutParams.width = DoricUtils.dp2px(width);
mView.requestLayout();
if (mView.getLayoutParams() != mLayoutParams) {
mView.getLayoutParams().width = mLayoutParams.width;
}
getNodeView().requestLayout();
}
}
@ -482,7 +499,10 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
protected void setHeight(float height) {
if (mLayoutParams.height >= 0) {
mLayoutParams.height = DoricUtils.dp2px(height);
mView.requestLayout();
if (mView.getLayoutParams() != mLayoutParams) {
mView.getLayoutParams().height = mLayoutParams.height;
}
getNodeView().requestLayout();
}
}
@ -490,7 +510,7 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
protected void setX(float x) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x);
mView.requestLayout();
getNodeView().requestLayout();
}
}
@ -498,7 +518,7 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
protected void setY(float y) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y);
mView.requestLayout();
getNodeView().requestLayout();
}
}
@ -519,7 +539,7 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
}
@DoricMethod
public int getBgColor() {
public int getBackgroundColor() {
if (mView.getBackground() instanceof ColorDrawable) {
return ((ColorDrawable) mView.getBackground()).getColor();
}
@ -527,7 +547,7 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
}
@DoricMethod
public void setBgColor(int color) {
public void setBackgroundColor(int color) {
mView.setBackgroundColor(color);
}
@ -621,4 +641,164 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
public float getPivotY() {
return getNodeView().getPivotY() / getNodeView().getHeight();
}
private String[] animatedKeys = {
"translationX",
"translationY",
"scaleX",
"scaleY",
"rotation",
};
@DoricMethod
public void doAnimation(JSValue value, final DoricPromise promise) {
Animator animator = parseAnimator(value);
if (animator != null) {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
JSONBuilder jsonBuilder = new JSONBuilder();
for (String key : animatedKeys) {
jsonBuilder.put(key, getAnimatedValue(key));
}
promise.resolve(new JavaValue(jsonBuilder.toJSONObject()));
}
});
animator.start();
}
}
private Animator parseAnimator(JSValue value) {
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 < animations.asArray().size(); i++) {
animatorSet.play(parseAnimator(animations.asArray().get(i)));
}
JSValue delayJS = value.asObject().getProperty("delay");
if (delayJS.isNumber()) {
animatorSet.setStartDelay(delayJS.asNumber().toLong());
}
return animatorSet;
} else if (value.isObject()) {
JSArray changeables = value.asObject().getProperty("changeables").asArray();
AnimatorSet animatorSet = new AnimatorSet();
JSValue repeatCount = value.asObject().getProperty("repeatCount");
JSValue repeatMode = value.asObject().getProperty("repeatMode");
JSValue fillMode = value.asObject().getProperty("fillMode");
for (int j = 0; j < changeables.size(); j++) {
ObjectAnimator animator = parseChangeable(changeables.get(j).asObject(), fillMode);
if (repeatCount.isNumber()) {
animator.setRepeatCount(repeatCount.asNumber().toInt());
}
if (repeatMode.isNumber()) {
animator.setRepeatMode(repeatMode.asNumber().toInt());
}
animatorSet.play(animator);
}
long duration = value.asObject().getProperty("duration").asNumber().toLong();
animatorSet.setDuration(duration);
JSValue delayJS = value.asObject().getProperty("delay");
if (delayJS.isNumber()) {
animatorSet.setStartDelay(delayJS.asNumber().toLong());
}
return animatorSet;
} else {
return null;
}
}
private ObjectAnimator parseChangeable(JSObject jsObject, JSValue fillMode) {
String key = jsObject.getProperty("key").asString().value();
float startVal = jsObject.getProperty("fromValue").asNumber().toFloat();
float endVal = jsObject.getProperty("toValue").asNumber().toFloat();
ObjectAnimator animator = ObjectAnimator.ofFloat(this,
key,
startVal,
endVal
);
setFillMode(animator, key, startVal, endVal, fillMode);
return animator;
}
private void setFillMode(ObjectAnimator animator,
final String key,
float startVal,
float endVal,
JSValue jsValue) {
int fillMode = 0;
if (jsValue.isNumber()) {
fillMode = jsValue.asNumber().toInt();
}
if ((fillMode & 2) == 2) {
setAnimatedValue(key, startVal);
}
final int finalFillMode = fillMode;
animator.addListener(new AnimatorListenerAdapter() {
private float originVal;
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
originVal = getAnimatedValue(key);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if ((finalFillMode & 1) != 1) {
setAnimatedValue(key, originVal);
}
}
});
}
private void setAnimatedValue(String key, float value) {
switch (key) {
case "translationX":
setTranslationX(value);
break;
case "translationY":
setTranslationY(value);
break;
case "scaleX":
setScaleX(value);
break;
case "scaleY":
setScaleY(value);
break;
case "rotation":
setRotation(value);
break;
default:
break;
}
}
private float getAnimatedValue(String key) {
switch (key) {
case "translationX":
return getTranslationX();
case "translationY":
return getTranslationY();
case "scaleX":
return getScaleX();
case "scaleY":
return getScaleY();
case "rotation":
return getRotation();
default:
return 0;
}
}
}

View File

@ -16,4 +16,5 @@ export default [
'src/FlowLayoutDemo',
'src/PopoverDemo',
'src/AnimatorDemo',
'src/ComplicatedAnimations',
]

View File

@ -1,4 +1,4 @@
import { animate, Group, Panel, gravity, Color, LayoutSpec, vlayout, scroller, layoutConfig, IVLayout, modal, IText, network, View, stack, IHLayout, hlayout, IView, text } 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) {
@ -6,7 +6,7 @@ function thisLabel(str: string) {
text: str,
width: 60,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 15,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -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([
@ -31,7 +34,7 @@ class AnimatorDemo extends Panel {
view.width = view.height = 20
view.x = view.y = 0
view.rotation = 0
view.bgColor = colors[2]
view.backgroundColor = colors[2]
view.corners = 0
view.scaleX = 1
view.scaleY = 1
@ -99,7 +102,7 @@ class AnimatorDemo extends Panel {
onClick: () => {
animate(this)({
animations: () => {
view.bgColor = colors[(idx++) % colors.length]
view.backgroundColor = colors[(idx++) % colors.length]
},
duration: 1000,
});
@ -171,7 +174,7 @@ class AnimatorDemo extends Panel {
view,
]).apply({
layoutConfig: layoutConfig().atmost(),
bgColor: colors[1].alpha(0.3 * 255),
backgroundColor: colors[1].alpha(0.3 * 255),
}),
]).apply({
layoutConfig: layoutConfig().atmost(),

View File

@ -0,0 +1,139 @@
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) {
return text({
text: str,
width: 80,
height: 30,
backgroundColor: colors[0],
textSize: 10,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
})
}
@Entry
class AnimationDemo extends Panel {
build(rootView: Group): void {
const view = box(2)
view.onClick = () => {
modal(context).toast('Clicked')
}
vlayout([
title("Complicated Animation"),
vlayout(
[
hlayout([
thisLabel('reset').apply({
onClick: () => {
const rotation = new RotationAnimation
rotation.duration = 1000
rotation.fromRotation = view.rotation || 0
rotation.toRotation = 0
const translation = new TranslationAnimation
translation.duration = 1000
translation.fromTranslationX = view.translationX || 0
translation.toTranslationX = 0
translation.fromTranslationY = view.translationY || 0
translation.toTranslationY = 0
const scale = new ScaleAnimation
scale.duration = 1000
scale.fromScaleX = view.scaleX || 1
scale.toScaleX = 1
scale.fromScaleY = view.scaleY || 1
scale.toScaleY = 1
const animationSet = new AnimationSet
animationSet.addAnimation(rotation)
animationSet.addAnimation(translation)
animationSet.addAnimation(scale)
view.doAnimation(context, animationSet).then(() => {
modal(context).toast('Resetd')
})
}
}),
]).apply({ space: 10 } as IHLayout),
hlayout([
thisLabel('TranslationX').apply({
onClick: () => {
const animation = new TranslationAnimation
animation.duration = 1000
animation.fromTranslationX = view.translationX || 0
animation.toTranslationX = animation.fromTranslationX + 100
animation.fromTranslationY = view.translationY || 0
animation.toTranslationY = view.translationY || 0
view.doAnimation(context, animation)
}
}),
thisLabel('TranslationY').apply({
onClick: () => {
const animation = new TranslationAnimation
animation.duration = 1000
animation.fromTranslationX = view.translationX || 0
animation.toTranslationX = view.translationX || 0
animation.fromTranslationY = view.translationY || 0
animation.toTranslationY = animation.fromTranslationY + 100
view.doAnimation(context, animation)
}
}),
thisLabel('ScaleX').apply({
onClick: () => {
const animation = new ScaleAnimation
animation.duration = 1000
animation.fromScaleX = view.scaleX || 1
animation.toScaleX = animation.fromScaleX + 1
view.doAnimation(context, animation)
}
}),
thisLabel('ScaleY').apply({
onClick: () => {
const animation = new ScaleAnimation
animation.duration = 1000
animation.fromScaleY = view.scaleY || 1
animation.toScaleY = animation.fromScaleY + 1
view.doAnimation(context, animation)
}
}),
thisLabel('rotation').apply({
onClick: () => {
const animation = new RotationAnimation
animation.duration = 1000
animation.fromRotation = view.rotation || 0
animation.toRotation = animation.fromRotation + 0.25
view.doAnimation(context, animation)
}
}),
]).apply({ space: 10 } as IHLayout),
hlayout([
thisLabel('group').apply({
onClick: () => {
const rotation = new RotationAnimation
rotation.duration = 1000
rotation.fromRotation = 0
rotation.toRotation = 4
const translation = new TranslationAnimation
translation.duration = 1000
translation.fromTranslationX = view.translationX || 0
translation.toTranslationX = 100
const animationSet = new AnimationSet
animationSet.addAnimation(rotation)
animationSet.addAnimation(translation)
view.doAnimation(context, animationSet)
}
}),
]).apply({ space: 10 } as IHLayout),
]
).apply({ space: 10 } as IVLayout),
stack([
view,
]).apply({
layoutConfig: layoutConfig().atmost(),
backgroundColor: colors[1].alpha(0.3 * 255),
}),
]).apply({
layoutConfig: layoutConfig().atmost(),
gravity: gravity().center(),
space: 10,
} as IVLayout).in(rootView)
}
}

View File

@ -59,7 +59,7 @@ class CounterView extends ViewHolder<CountModel> {
offsetY: 10,
}
it.corners = 20
it.bgColor = Color.parse('#ff00ff')
it.backgroundColor = Color.parse('#ff00ff')
}))
root.addChild((new Image).also(iv => {

View File

@ -6,7 +6,7 @@ import { colors } from "./utils";
function box(idx = 0) {
return (new Stack).also(it => {
it.width = it.height = 20
it.bgColor = colors[idx || 0]
it.backgroundColor = colors[idx || 0]
})
}
@ -15,7 +15,7 @@ function boxStr(str: string, idx = 0) {
it.width = it.height = 20
it.text = str
it.textColor = Color.WHITE
it.bgColor = colors[idx || 0]
it.backgroundColor = colors[idx || 0]
})
}

View File

@ -23,7 +23,7 @@ class FlowDemo extends Panel {
rowSpace: 10,
renderItem: (idx) => {
return new FlowLayoutItem().apply({
bgColor: colors[idx % colors.length],
backgroundColor: colors[idx % colors.length],
height: 50 + (idx % 3) * 20,
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
}).also(it => {

View File

@ -13,7 +13,7 @@ class ImageDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[5],
backgroundColor: colors[5],
textAlignment: gravity().center(),
height: 50,
}),

View File

@ -14,7 +14,7 @@ const colors = [
function box(idx = 0) {
return (new Stack).also(it => {
it.width = it.height = 20
it.bgColor = Color.parse(colors[idx || 0])
it.backgroundColor = Color.parse(colors[idx || 0])
})
}
function boxStr(str: string, idx = 0) {
@ -22,7 +22,7 @@ function boxStr(str: string, idx = 0) {
it.width = it.height = 20
it.text = str
it.textColor = Color.parse('#ffffff')
it.bgColor = Color.parse(colors[idx || 0])
it.backgroundColor = Color.parse(colors[idx || 0])
})
}
function label(str: string) {
@ -117,7 +117,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IHLayout),
hlayout([
@ -137,7 +137,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IHLayout),
hlayout([
@ -157,7 +157,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IHLayout),
hlayout([
@ -183,7 +183,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IHLayout),
hlayout([
@ -215,7 +215,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IHLayout),
]).also(it => {
@ -315,7 +315,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IVLayout),
vlayout([
@ -345,7 +345,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IVLayout),
vlayout([
@ -375,7 +375,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IVLayout),
vlayout([
@ -406,7 +406,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IVLayout),
vlayout([
@ -438,7 +438,7 @@ class LayoutDemo extends Panel {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
},
bgColor: Color.parse('#eeeeee'),
backgroundColor: Color.parse('#eeeeee'),
gravity: gravity().center(),
} as IVLayout),
]).also(it => {

View File

@ -14,7 +14,7 @@ class ListPanel extends Panel {
},
textSize: 30,
textColor: Color.parse("#535c68"),
bgColor: Color.parse("#dff9fb"),
backgroundColor: Color.parse("#dff9fb"),
textAlignment: gravity().center(),
height: 50,
}),
@ -64,7 +64,7 @@ class ListPanel extends Panel {
}
}
it.gravity = gravity().center()
it.bgColor = colors[(idx + offset) % colors.length]
it.backgroundColor = colors[(idx + offset) % colors.length]
let clicked = 0
it.onClick = () => {
counter.text = `Item Clicked ${++clicked}`
@ -107,8 +107,8 @@ class ListPanel extends Panel {
widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.AT_MOST,
}
it.bgColor = Color.WHITE
it.backgroundColor = Color.WHITE
}).in(rootView)
refreshView.bgColor = Color.YELLOW
refreshView.backgroundColor = Color.YELLOW
}
}

View File

@ -10,7 +10,7 @@ class ModalDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[1],
backgroundColor: colors[1],
textAlignment: Gravity.Center,
height: 50,
}),
@ -18,7 +18,7 @@ class ModalDemo extends Panel {
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -30,7 +30,7 @@ class ModalDemo extends Panel {
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -43,7 +43,7 @@ class ModalDemo extends Panel {
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -56,14 +56,14 @@ class ModalDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[2],
backgroundColor: colors[2],
textAlignment: Gravity.Center,
height: 50,
}),
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -82,14 +82,14 @@ class ModalDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[3],
backgroundColor: colors[3],
textAlignment: Gravity.Center,
height: 50,
}),
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -113,14 +113,14 @@ class ModalDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[4],
backgroundColor: colors[4],
textAlignment: Gravity.Center,
height: 50,
}),
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),

View File

@ -9,7 +9,7 @@ class NavbarDemo extends Panel {
label('isHidden').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -22,7 +22,7 @@ class NavbarDemo extends Panel {
label('setHidden').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -37,7 +37,7 @@ class NavbarDemo extends Panel {
label('setTitle').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -51,7 +51,7 @@ class NavbarDemo extends Panel {
label('setBgColor').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -65,7 +65,7 @@ class NavbarDemo extends Panel {
label('Pop').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),

View File

@ -9,7 +9,7 @@ class NaivgatorDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[1],
backgroundColor: colors[1],
textAlignment: gravity().center(),
height: 50,
}),
@ -20,7 +20,7 @@ class NaivgatorDemo extends Panel {
'NetworkDemo', 'ScrollerDemo', 'SliderDemo', 'Snake', 'StorageDemo'].map(e =>
label(e).apply({
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly().w(LayoutSpec.AT_MOST),
@ -32,7 +32,7 @@ class NaivgatorDemo extends Panel {
label('POP').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),

View File

@ -9,7 +9,7 @@ class NetworkDemo extends Panel {
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),

View File

@ -9,7 +9,7 @@ class PopoverDemo extends Panel {
label('Popover').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -17,14 +17,14 @@ class PopoverDemo extends Panel {
popover(context).show(text({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly().a(Gravity.Center),
text: "This is PopOver Window",
}).also(v => {
let idx = 0
v.onClick = () => {
v.bgColor = colors[(++idx) % colors.length]
v.backgroundColor = colors[(++idx) % colors.length]
}
modal(context).toast('Dismissed after 3 seconds')
setTimeout(() => {

View File

@ -40,7 +40,7 @@ class RefreshableDemo extends Panel {
label('start Refresh').apply({
width: 300,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -51,7 +51,7 @@ class RefreshableDemo extends Panel {
label('stop Refresh').apply({
width: 300,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -63,7 +63,7 @@ class RefreshableDemo extends Panel {
label('Enable Refresh').apply({
width: 300,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -75,7 +75,7 @@ class RefreshableDemo extends Panel {
label('Disable Refresh').apply({
width: 300,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -86,7 +86,7 @@ class RefreshableDemo extends Panel {
label('Rotate self').apply({
width: 300,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -101,7 +101,7 @@ class RefreshableDemo extends Panel {
space: 10,
} as IVLayout))
}).apply({
bgColor: Color.YELLOW
backgroundColor: Color.YELLOW
}).in(rootView)
}
}

View File

@ -12,7 +12,7 @@ class ScrollerPanel extends Panel {
layoutConfig: layoutConfig().exactly(),
width: 300,
height: 500,
bgColor: Color.RED,
backgroundColor: Color.RED,
}),
scroller(
vlayout(new Array(100).fill(1).map(e => label('Scroll Content')))
@ -20,14 +20,14 @@ class ScrollerPanel extends Panel {
layoutConfig: layoutConfig().exactly(),
width: 300,
height: 500,
bgColor: Color.BLUE,
backgroundColor: Color.BLUE,
})
])
)
.apply({
layoutConfig: layoutConfig().atmost().h(LayoutSpec.EXACTLY),
height: 500,
bgColor: Color.YELLOW,
backgroundColor: Color.YELLOW,
})
.in(rootView)
}

View File

@ -24,7 +24,7 @@ class SliderPanel extends Panel {
},
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[1],
backgroundColor: colors[1],
textAlignment: gravity().center(),
height: 50,
}),
@ -38,7 +38,7 @@ class SliderPanel extends Panel {
})).also(it => {
let start = idx
it.onClick = () => {
it.bgColor = (colors[++start % colors.length])
it.backgroundColor = (colors[++start % colors.length])
}
})
},

View File

@ -144,7 +144,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
right?: Text
build(root: Group): void {
root.bgColor = Color.parse('#000000')
root.backgroundColor = Color.parse('#000000')
vlayout([
text({
text: "Snake",
@ -160,7 +160,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
},
}),
(new Stack).also(panel => {
panel.bgColor = Color.parse('#00ff00')
panel.backgroundColor = Color.parse('#00ff00')
this.panel = panel
}),
hlayout([
@ -188,7 +188,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "↑",
textSize: 30,
textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'),
backgroundColor: Color.parse('#ffff00'),
layoutConfig: {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
@ -207,7 +207,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "←",
textSize: 30,
textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'),
backgroundColor: Color.parse('#ffff00'),
layoutConfig: {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
@ -219,7 +219,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "↓",
textSize: 30,
textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'),
backgroundColor: Color.parse('#ffff00'),
layoutConfig: {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
@ -231,7 +231,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "→",
textSize: 30,
textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'),
backgroundColor: Color.parse('#ffff00'),
layoutConfig: {
widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY,
@ -292,9 +292,9 @@ class SnakeView extends ViewHolder<SnakeModel> {
this.panel.addChild(item)
}
if (index === nodes.length - 1) {
item.bgColor = Color.parse('#ffff00')
item.backgroundColor = Color.parse('#ffff00')
} else {
item.bgColor = Color.parse('#ff0000')
item.backgroundColor = Color.parse('#ff0000')
}
item.x = e.x * 10
item.y = e.y * 10

View File

@ -20,7 +20,7 @@ class StorageDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[1],
backgroundColor: colors[1],
textAlignment: gravity().center(),
height: 50,
}),
@ -29,14 +29,14 @@ class StorageDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 20,
textColor: Color.WHITE,
bgColor: colors[3],
backgroundColor: colors[3],
textAlignment: gravity().center(),
height: 50,
}).also(it => this.stored = it),
label('store a value').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -57,7 +57,7 @@ class StorageDemo extends Panel {
label('remove value').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
@ -70,7 +70,7 @@ class StorageDemo extends Panel {
label('clear values').apply({
width: 200,
height: 50,
bgColor: colors[0],
backgroundColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),

View File

@ -24,7 +24,7 @@ export function label(str: string) {
export function box(idx = 0) {
return (new Stack).also(it => {
it.width = it.height = 20
it.bgColor = colors[idx || 0]
it.backgroundColor = colors[idx || 0]
})
}
export function boxStr(str: string, idx = 0) {
@ -32,7 +32,7 @@ export function boxStr(str: string, idx = 0) {
it.width = it.height = 20
it.text = str
it.textColor = Color.WHITE
it.bgColor = colors[idx || 0]
it.backgroundColor = colors[idx || 0]
})
}
@ -42,7 +42,7 @@ export function title(str: string) {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[1],
backgroundColor: colors[1],
textAlignment: gravity().center(),
height: 50,
})

View File

@ -26,9 +26,7 @@ function fromDir(startPath, filter) {
};
};
function doMerge(startPath, fileName) {
// console.log('-- found: ', startPath, fileName);
const filePath = fileName ? path.join(startPath, fileName) : startPath
// console.log('-- merge: ', filePath.replace(/bundle\//, 'build/'), filePath)
const mergedMap = SourceMapMerger.createMergedSourceMapFromFiles([
filePath.replace(/bundle\//, 'build/'),
filePath,

View File

@ -16,15 +16,19 @@ setTimeout(() => {
fs.readFile(path, 'utf-8', (error, data) => {
if (!path.endsWith('.map')) {
console.log('File change:', path)
const sourceMap = doMerge(path + ".map")
ws.connections.forEach(e => {
e.sendText(JSON.stringify({
cmd: 'RELOAD',
script: data,
source: path.match(/[^/\\]*$/)[0],
sourceMap,
}))
})
try {
const sourceMap = doMerge(path + ".map")
ws.connections.forEach(e => {
e.sendText(JSON.stringify({
cmd: 'RELOAD',
script: data,
source: path.match(/[^/\\]*$/)[0],
sourceMap,
}))
})
} catch (e) {
console.error(e)
}
}
})
});

View File

@ -27,6 +27,7 @@
#import "DoricConstant.h"
#import "DoricSuperNode.h"
#import "DoricExtensions.h"
#import "DoricPromise.h"
void DoricAddEllipticArcPath(CGMutablePathRef path,
CGPoint origin,
@ -65,6 +66,33 @@ CGPathRef DoricCreateRoundedRectPath(CGRect bounds,
return path;
}
@interface AnimationCallback : NSObject <CAAnimationDelegate>
@property(nonatomic, strong) NSMutableDictionary *dictionary;
@property(nonatomic, strong) void (^startBlock)(AnimationCallback *callback);
@property(nonatomic, strong) void (^endBlock)(AnimationCallback *callback);
@end
@implementation AnimationCallback
- (instancetype)init {
if (self = [super init]) {
_dictionary = [NSMutableDictionary new];
}
return self;
}
- (void)animationDidStart:(CAAnimation *)anim {
if (self.startBlock) {
self.startBlock(self);
}
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (self.endBlock) {
self.endBlock(self);
}
}
@end
@interface DoricViewNode ()
@property(nonatomic, strong) NSMutableDictionary *callbackIds;
@ -148,7 +176,7 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
view.x = [(NSNumber *) prop floatValue];
} else if ([name isEqualToString:@"y"]) {
view.y = [(NSNumber *) prop floatValue];
} else if ([name isEqualToString:@"bgColor"]) {
} else if ([name isEqualToString:@"backgroundColor"]) {
view.backgroundColor = DoricColor(prop);
} else if ([name isEqualToString:@"alpha"]) {
view.alpha = [prop floatValue];
@ -309,4 +337,283 @@ - (void)blendLayoutConfig:(NSDictionary *)params {
}
}
- (NSDictionary *)transformation {
NSMutableDictionary *dictionary = [NSMutableDictionary new];
if (self.translationX) {
dictionary[@"translationX"] = self.translationX;
}
if (self.translationY) {
dictionary[@"translationY"] = self.translationY;
}
if (self.scaleX) {
dictionary[@"scaleX"] = self.scaleX;
}
if (self.scaleY) {
dictionary[@"scaleY"] = self.scaleY;
}
if (self.rotation) {
dictionary[@"rotation"] = self.rotation;
}
return dictionary;
}
- (void)doAnimation:(id)params withPromise:(DoricPromise *)promise {
CAAnimation *animation = [self parseAnimation:params];
AnimationCallback *originDelegate = animation.delegate;
AnimationCallback *animationCallback = [[AnimationCallback new] also:^(AnimationCallback *it) {
it.startBlock = ^(AnimationCallback *callback) {
if (originDelegate) {
originDelegate.startBlock(callback);
}
[self transformProperties];
};
it.endBlock = ^(AnimationCallback *callback) {
if (originDelegate) {
originDelegate.endBlock(callback);
}
[self transformProperties];
[promise resolve:self.transformation];
};
}];
animation.delegate = animationCallback;
if (params[@"delay"]) {
animation.beginTime = CACurrentMediaTime() + [params[@"delay"] floatValue] / 1000;
}
[self.view.layer addAnimation:animation forKey:nil];
}
- (CFTimeInterval)computeDurationOfAnimations:(NSArray<CAAnimation *> *)animations {
__block CFTimeInterval interval = 0;
[animations forEach:^(CAAnimation *obj) {
interval = MAX(interval, obj.beginTime + obj.duration * (1 + obj.repeatCount));
}];
return interval;
}
- (CAAnimation *)parseAnimation:(id)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.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);
}
}];
};
}];
if (params[@"delay"]) {
animationGroup.beginTime = [params[@"delay"] floatValue] / 1000;
}
return animationGroup;
} else if ([params isKindOfClass:[NSDictionary class]]) {
NSArray<NSDictionary *> *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"]];
}
}];
animation.fromValue = [NSValue valueWithCGPoint:from];
animation.toValue = [NSValue valueWithCGPoint:to];
[self setAnimation:animation params:params];
return animation;
} else {
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
NSMutableArray <CABasicAnimation *> *animations = [NSMutableArray new];
[changeables forEach:^(NSDictionary *obj) {
CABasicAnimation *animation = [self parseChangeable:obj fillMode:params[@"fillMode"]];
[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;
}
- (void)setAnimation:(CAAnimation *)animation params:(NSDictionary *)params {
if (params[@"repeatCount"]) {
NSInteger repeatCount = [params[@"repeatCount"] integerValue];
if (repeatCount < 0) {
repeatCount = NSNotFound;
}
animation.repeatCount = repeatCount;
}
if (params[@"repeatMode"]) {
NSInteger repeatMode = [params[@"repeatMode"] integerValue];
animation.autoreverses = repeatMode == 2;
}
if (params[@"delay"]) {
animation.beginTime = [params[@"delay"] floatValue] / 1000;
}
animation.duration = [params[@"duration"] floatValue] / 1000;
}
- (void)setFillMode:(CAAnimation *)animation
key:(NSString *)key
startValue:(NSNumber *)startValue
endValue:(NSNumber *)endValue
fillMode:(NSNumber *)fillMode {
NSUInteger fillModeInt = fillMode.unsignedIntegerValue;
if ((fillModeInt & 2) == 2) {
[self setAnimatedValue:key value:startValue];
}
AnimationCallback *callback = [AnimationCallback new];
AnimationCallback *originCallback = animation.delegate;
__weak typeof(self) _self = self;
callback.startBlock = ^(AnimationCallback *callback) {
__strong typeof(_self) self = _self;
callback.dictionary[key] = [self getAnimatedValue:key];
if (originCallback) {
originCallback.startBlock(callback);
}
};
callback.endBlock = ^(AnimationCallback *callback) {
__strong typeof(_self) self = _self;
if ((fillModeInt & 1) == 1) {
[self setAnimatedValue:key value:endValue];
}
if (originCallback) {
originCallback.endBlock(callback);
}
};
animation.delegate = callback;
}
- (NSNumber *)getAnimatedValue:(NSString *)key {
if ([@"translationX" isEqualToString:key]) {
return self.translationX;
}
if ([@"translationY" isEqualToString:key]) {
return self.translationY;
}
if ([@"scaleX" isEqualToString:key]) {
return self.scaleX;
}
if ([@"scaleY" isEqualToString:key]) {
return self.scaleY;
}
if ([@"rotation" isEqualToString:key]) {
return self.rotation;
}
return nil;
}
- (void)setAnimatedValue:(NSString *)key value:(NSNumber *)value {
if ([@"translationX" isEqualToString:key]) {
self.translationX = value;
} else if ([@"translationY" isEqualToString:key]) {
self.translationY = value;
} else if ([@"scaleX" isEqualToString:key]) {
self.scaleX = value;
} else if ([@"scaleY" isEqualToString:key]) {
self.scaleY = value;
} else if ([@"rotation" isEqualToString:key]) {
self.rotation = value;
}
}
- (CABasicAnimation *)parseChangeable:(NSDictionary *)params fillMode:(NSNumber *)fillMode {
NSString *key = params[@"key"];
CABasicAnimation *animation = [CABasicAnimation animation];
if ([@"scaleX" isEqualToString:key]) {
animation.keyPath = @"transform.scale.x";
animation.fromValue = params[@"fromValue"];
animation.toValue = params[@"toValue"];
} else if ([@"scaleY" isEqualToString:key]) {
animation.keyPath = @"transform.scale.y";
animation.fromValue = params[@"fromValue"];
animation.toValue = params[@"toValue"];
} else if ([@"rotation" isEqualToString:key]) {
animation.keyPath = @"transform.rotation.z";
animation.fromValue = @([params[@"fromValue"] floatValue] * M_PI);
animation.toValue = @([params[@"toValue"] floatValue] * M_PI);
} else if ([@"backgroundColor" isEqualToString:key]) {
animation.keyPath = @"backgroundColor";
animation.fromValue = params[@"fromValue"];
animation.toValue = params[@"toValue"];
}
[self setFillMode:animation
key:key
startValue:params[@"fromValue"]
endValue:params[@"toValue"]
fillMode:fillMode];
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

View File

@ -1,3 +1,5 @@
import { Modeling, Model } from "../util/types"
/*
* Copyright [2019] [Doric.Pub]
*
@ -14,159 +16,223 @@
* limitations under the License.
*/
export type AnimatedKey = "translationX" | "translationY" | "scaleX" | "scaleY" | "rotation" | "pivotX" | "pivotY"
export enum RepeatMode {
RESTART,
REVERSE,
RESTART = 1,
REVERSE = 2,
}
export class Animation {
translationX?: number
translationY?: number
scaleX?: number
scaleY?: number
pivotX?: number
pivotY?: number
rotation?: number
duration = 100
startDelay = 0
repeatCount = 1
repeatMode = RepeatMode.RESTART
export interface IAnimation extends Modeling {
duration: number
delay?: number
}
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
}
export interface Changeable {
fromValue: number
toValue: number
key: AnimatedKey
repeatCount?: number
repeatMode?: RepeatMode
}
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 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 = 0x1,
/**
* The receiver clamps values before zero to zero when the animation is completed.
*/
Backward = 0x2,
/**
* The receiver clamps values at both ends of the objects time space
*/
Both = 0x3,
}
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)
abstract class Animation implements IAnimation {
changeables: Map<AnimatedKey, Changeable> = new Map
duration = 0
repeatCount?: number
repeatMode?: RepeatMode
delay?: number
fillMode = FillMode.Forward
toModel() {
const changeables = []
for (let e of this.changeables.values()) {
changeables.push({
key: e.key,
fromValue: e.fromValue,
toValue: e.toValue,
})
}
}
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
})
})
return {
type: this.constructor.name,
delay: this.delay,
duration: this.duration,
changeables,
repeatCount: this.repeatCount,
repeatMode: this.repeatMode,
fillMode: this.fillMode,
}
}
}
export class ScaleAnimation extends Animation {
private scaleXChangeable: Changeable = {
key: "scaleX",
fromValue: 1,
toValue: 1,
}
private scaleYChangeable: Changeable = {
key: "scaleY",
fromValue: 1,
toValue: 1,
}
constructor() {
super()
this.changeables.set("scaleX", this.scaleXChangeable)
this.changeables.set("scaleY", this.scaleYChangeable)
}
set fromScaleX(v: number) {
this.scaleXChangeable.fromValue = v
}
get fromScaleX() {
return this.scaleXChangeable.fromValue
}
set toScaleX(v: number) {
this.scaleXChangeable.toValue = v
}
get toScaleX() {
return this.scaleXChangeable.toValue
}
set fromScaleY(v: number) {
this.scaleYChangeable.fromValue = v
}
get fromScaleY() {
return this.scaleYChangeable.fromValue
}
set toScaleY(v: number) {
this.scaleYChangeable.toValue = v
}
get toScaleY() {
return this.scaleYChangeable.toValue
}
}
export class TranslationAnimation extends Animation {
private translationXChangeable: Changeable = {
key: "translationX",
fromValue: 1,
toValue: 1,
}
private translationYChangeable: Changeable = {
key: "translationY",
fromValue: 1,
toValue: 1,
}
constructor() {
super()
this.changeables.set("translationX", this.translationXChangeable)
this.changeables.set("translationY", this.translationYChangeable)
}
set fromTranslationX(v: number) {
this.translationXChangeable.fromValue = v
}
get fromTranslationX() {
return this.translationXChangeable.fromValue
}
set toTranslationX(v: number) {
this.translationXChangeable.toValue = v
}
get toTranslationX() {
return this.translationXChangeable.toValue
}
set fromTranslationY(v: number) {
this.translationYChangeable.fromValue = v
}
get fromTranslationY() {
return this.translationYChangeable.fromValue
}
set toTranslationY(v: number) {
this.translationYChangeable.toValue = v
}
get toTranslationY() {
return this.translationYChangeable.toValue
}
}
export class RotationAnimation extends Animation {
private rotationChaneable: Changeable = {
key: "rotation",
fromValue: 1,
toValue: 1,
}
constructor() {
super()
this.changeables.set("rotation", this.rotationChaneable)
}
set fromRotation(v: number) {
this.rotationChaneable.fromValue = v
}
get fromRotation() {
return this.rotationChaneable.fromValue
}
set toRotation(v: number) {
this.rotationChaneable.toValue = v
}
get toRotation() {
return this.rotationChaneable.toValue
}
}
export class AnimationSet implements IAnimation {
private animations: IAnimation[] = []
_duration = 0
delay?: number
addAnimation(anim: IAnimation) {
this.animations.push(anim)
}
get duration() {
return this._duration
}
set duration(v: number) {
this._duration = v
this.animations.forEach(e => e.duration = v)
}
toModel() {
return {
animations: this.animations.map(e => {
return e.toModel()
}) as Model,
delay: this.delay,
}
}
}

View File

@ -19,6 +19,7 @@ import { uniqueId } from "../util/uniqueId";
import { loge } from "../util/log";
import { BridgeContext } from "../runtime/global";
import { LayoutConfig } from '../util/layoutconfig'
import { IAnimation } from "./animation";
export function Property(target: Object, propKey: string) {
Reflect.defineMetadata(propKey, true, target)
@ -27,7 +28,7 @@ export function Property(target: Object, propKey: string) {
export interface IView {
width?: number
height?: number
bgColor?: Color | GradientColor
backgroundColor?: Color | GradientColor
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
border?: { width: number; color: Color; }
shadow?: { color: Color; opacity: number; radius: number; offsetX: number; offsetY: number }
@ -84,7 +85,7 @@ export abstract class View implements Modeling, IView {
y: number = 0
@Property
bgColor?: Color | GradientColor
backgroundColor?: Color | GradientColor
@Property
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
@ -333,6 +334,15 @@ export abstract class View implements Modeling, IView {
@Property
rotation?: number
/**----------transform----------*/
doAnimation(context: BridgeContext, animation: IAnimation) {
return this.nativeChannel(context, "doAnimation")(animation.toModel()).then((args) => {
for (let key in args) {
Reflect.set(this, key, Reflect.get(args, key, args), this)
Reflect.deleteProperty(this.__dirty_props__, key)
}
})
}
}
export abstract class Superview extends View {