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() AsyncResult<JavaValue> asyncResult = getDoricContext().getDriver()
.asyncCall(callable, ThreadMode.UI); .asyncCall(callable, ThreadMode.UI);
asyncResult.setCallback(new AsyncResult.Callback<JavaValue>() { if (!method.getReturnType().equals(Void.TYPE)) {
@Override asyncResult.setCallback(new AsyncResult.Callback<JavaValue>() {
public void onResult(JavaValue result) { @Override
doricPromise.resolve(result); public void onResult(JavaValue result) {
} doricPromise.resolve(result);
}
@Override @Override
public void onError(Throwable t) { public void onError(Throwable t) {
doricPromise.resolve(new JavaValue(t.getLocalizedMessage())); doricPromise.resolve(new JavaValue(t.getLocalizedMessage()));
} }
@Override @Override
public void onFinish() { public void onFinish() {
} }
}); });
}
} }
} }
} catch (ArchiveException e) { } catch (ArchiveException e) {

View File

@ -16,6 +16,8 @@
package pub.doric.shader; package pub.doric.shader;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator; import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.content.Context; import android.content.Context;
@ -28,19 +30,23 @@ import android.widget.LinearLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import pub.doric.Doric;
import pub.doric.DoricContext; import pub.doric.DoricContext;
import pub.doric.DoricRegistry; import pub.doric.DoricRegistry;
import pub.doric.async.AsyncResult; import pub.doric.async.AsyncResult;
import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricMethod;
import pub.doric.extension.bridge.DoricPromise;
import pub.doric.utils.DoricContextHolder; import pub.doric.utils.DoricContextHolder;
import pub.doric.utils.DoricConstant; import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricLog;
import pub.doric.utils.DoricMetaInfo; import pub.doric.utils.DoricMetaInfo;
import pub.doric.utils.DoricUtils; import pub.doric.utils.DoricUtils;
import com.github.pengfeizhou.jscore.JSArray;
import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSONBuilder;
import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JSValue; import com.github.pengfeizhou.jscore.JSValue;
import com.github.pengfeizhou.jscore.JavaValue;
import java.util.LinkedList; import java.util.LinkedList;
@ -172,17 +178,17 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
setY(prop.asNumber().toFloat()); setY(prop.asNumber().toFloat());
} }
break; break;
case "bgColor": case "backgroundColor":
if (isAnimating()) { if (isAnimating()) {
ObjectAnimator animator = ObjectAnimator.ofInt( ObjectAnimator animator = ObjectAnimator.ofInt(
this, this,
name, name,
getBgColor(), getBackgroundColor(),
prop.asNumber().toInt()); prop.asNumber().toInt());
animator.setEvaluator(new ArgbEvaluator()); animator.setEvaluator(new ArgbEvaluator());
addAnimator(animator); addAnimator(animator);
} else { } else {
setBgColor(prop.asNumber().toInt()); setBackgroundColor(prop.asNumber().toInt());
} }
break; break;
case "onClick": case "onClick":
@ -462,19 +468,30 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
@DoricMethod @DoricMethod
public float getWidth() { public float getWidth() {
return DoricUtils.px2dp(getNodeView().getWidth()); if (mLayoutParams.width >= 0) {
return DoricUtils.px2dp(mLayoutParams.width);
} else {
return mView.getMeasuredWidth();
}
} }
@DoricMethod @DoricMethod
public float getHeight() { public float getHeight() {
return DoricUtils.px2dp(getNodeView().getHeight()); if (mLayoutParams.width >= 0) {
return DoricUtils.px2dp(mLayoutParams.height);
} else {
return mView.getMeasuredHeight();
}
} }
@DoricMethod @DoricMethod
protected void setWidth(float width) { protected void setWidth(float width) {
if (mLayoutParams.width >= 0) { if (mLayoutParams.width >= 0) {
mLayoutParams.width = DoricUtils.dp2px(width); 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) { protected void setHeight(float height) {
if (mLayoutParams.height >= 0) { if (mLayoutParams.height >= 0) {
mLayoutParams.height = DoricUtils.dp2px(height); 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) { protected void setX(float x) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x); ((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) { protected void setY(float y) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y); ((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 @DoricMethod
public int getBgColor() { public int getBackgroundColor() {
if (mView.getBackground() instanceof ColorDrawable) { if (mView.getBackground() instanceof ColorDrawable) {
return ((ColorDrawable) mView.getBackground()).getColor(); return ((ColorDrawable) mView.getBackground()).getColor();
} }
@ -527,7 +547,7 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
} }
@DoricMethod @DoricMethod
public void setBgColor(int color) { public void setBackgroundColor(int color) {
mView.setBackgroundColor(color); mView.setBackgroundColor(color);
} }
@ -621,4 +641,164 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
public float getPivotY() { public float getPivotY() {
return getNodeView().getPivotY() / getNodeView().getHeight(); 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/FlowLayoutDemo',
'src/PopoverDemo', 'src/PopoverDemo',
'src/AnimatorDemo', '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"; import { title, colors, box } from "./utils";
function thisLabel(str: string) { function thisLabel(str: string) {
@ -6,7 +6,7 @@ function thisLabel(str: string) {
text: str, text: str,
width: 60, width: 60,
height: 50, height: 50,
bgColor: colors[0], backgroundColor: colors[0],
textSize: 15, textSize: 15,
textColor: Color.WHITE, textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(), layoutConfig: layoutConfig().exactly(),
@ -17,10 +17,13 @@ function thisLabel(str: string) {
class AnimatorDemo extends Panel { class AnimatorDemo extends Panel {
build(rootView: Group): void { build(rootView: Group): void {
const view = box(2) const view = box(2)
view.onClick = () => {
modal(context).toast('Clicked')
}
const view2 = box(3) const view2 = box(3)
let idx = 0 let idx = 0
vlayout([ vlayout([
title("Animator zDemo"), title("Animator Demo"),
vlayout( vlayout(
[ [
hlayout([ hlayout([
@ -31,7 +34,7 @@ class AnimatorDemo extends Panel {
view.width = view.height = 20 view.width = view.height = 20
view.x = view.y = 0 view.x = view.y = 0
view.rotation = 0 view.rotation = 0
view.bgColor = colors[2] view.backgroundColor = colors[2]
view.corners = 0 view.corners = 0
view.scaleX = 1 view.scaleX = 1
view.scaleY = 1 view.scaleY = 1
@ -99,7 +102,7 @@ class AnimatorDemo extends Panel {
onClick: () => { onClick: () => {
animate(this)({ animate(this)({
animations: () => { animations: () => {
view.bgColor = colors[(idx++) % colors.length] view.backgroundColor = colors[(idx++) % colors.length]
}, },
duration: 1000, duration: 1000,
}); });
@ -171,7 +174,7 @@ class AnimatorDemo extends Panel {
view, view,
]).apply({ ]).apply({
layoutConfig: layoutConfig().atmost(), layoutConfig: layoutConfig().atmost(),
bgColor: colors[1].alpha(0.3 * 255), backgroundColor: colors[1].alpha(0.3 * 255),
}), }),
]).apply({ ]).apply({
layoutConfig: layoutConfig().atmost(), 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, offsetY: 10,
} }
it.corners = 20 it.corners = 20
it.bgColor = Color.parse('#ff00ff') it.backgroundColor = Color.parse('#ff00ff')
})) }))
root.addChild((new Image).also(iv => { root.addChild((new Image).also(iv => {

View File

@ -6,7 +6,7 @@ import { colors } from "./utils";
function box(idx = 0) { function box(idx = 0) {
return (new Stack).also(it => { return (new Stack).also(it => {
it.width = it.height = 20 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.width = it.height = 20
it.text = str it.text = str
it.textColor = Color.WHITE 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, rowSpace: 10,
renderItem: (idx) => { renderItem: (idx) => {
return new FlowLayoutItem().apply({ return new FlowLayoutItem().apply({
bgColor: colors[idx % colors.length], backgroundColor: colors[idx % colors.length],
height: 50 + (idx % 3) * 20, height: 50 + (idx % 3) * 20,
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST), layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
}).also(it => { }).also(it => {

View File

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

View File

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

View File

@ -14,7 +14,7 @@ class ListPanel extends Panel {
}, },
textSize: 30, textSize: 30,
textColor: Color.parse("#535c68"), textColor: Color.parse("#535c68"),
bgColor: Color.parse("#dff9fb"), backgroundColor: Color.parse("#dff9fb"),
textAlignment: gravity().center(), textAlignment: gravity().center(),
height: 50, height: 50,
}), }),
@ -64,7 +64,7 @@ class ListPanel extends Panel {
} }
} }
it.gravity = gravity().center() it.gravity = gravity().center()
it.bgColor = colors[(idx + offset) % colors.length] it.backgroundColor = colors[(idx + offset) % colors.length]
let clicked = 0 let clicked = 0
it.onClick = () => { it.onClick = () => {
counter.text = `Item Clicked ${++clicked}` counter.text = `Item Clicked ${++clicked}`
@ -107,8 +107,8 @@ class ListPanel extends Panel {
widthSpec: LayoutSpec.AT_MOST, widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.AT_MOST, heightSpec: LayoutSpec.AT_MOST,
} }
it.bgColor = Color.WHITE it.backgroundColor = Color.WHITE
}).in(rootView) }).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), layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
bgColor: colors[1], backgroundColor: colors[1],
textAlignment: Gravity.Center, textAlignment: Gravity.Center,
height: 50, height: 50,
}), }),
@ -18,7 +18,7 @@ class ModalDemo extends Panel {
label('Click me').apply({ label('Click me').apply({
width: 200, width: 200,
height: 50, height: 50,
bgColor: colors[0], backgroundColor: colors[0],
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(), layoutConfig: layoutConfig().exactly(),
@ -30,7 +30,7 @@ class ModalDemo extends Panel {
label('Click me').apply({ label('Click me').apply({
width: 200, width: 200,
height: 50, height: 50,
bgColor: colors[0], backgroundColor: colors[0],
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(), layoutConfig: layoutConfig().exactly(),
@ -43,7 +43,7 @@ class ModalDemo extends Panel {
label('Click me').apply({ label('Click me').apply({
width: 200, width: 200,
height: 50, height: 50,
bgColor: colors[0], backgroundColor: colors[0],
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(), layoutConfig: layoutConfig().exactly(),
@ -56,14 +56,14 @@ class ModalDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST), layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
bgColor: colors[2], backgroundColor: colors[2],
textAlignment: Gravity.Center, textAlignment: Gravity.Center,
height: 50, height: 50,
}), }),
label('Click me').apply({ label('Click me').apply({
width: 200, width: 200,
height: 50, height: 50,
bgColor: colors[0], backgroundColor: colors[0],
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(), layoutConfig: layoutConfig().exactly(),
@ -82,14 +82,14 @@ class ModalDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST), layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
bgColor: colors[3], backgroundColor: colors[3],
textAlignment: Gravity.Center, textAlignment: Gravity.Center,
height: 50, height: 50,
}), }),
label('Click me').apply({ label('Click me').apply({
width: 200, width: 200,
height: 50, height: 50,
bgColor: colors[0], backgroundColor: colors[0],
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(), layoutConfig: layoutConfig().exactly(),
@ -113,14 +113,14 @@ class ModalDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST), layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
bgColor: colors[4], backgroundColor: colors[4],
textAlignment: Gravity.Center, textAlignment: Gravity.Center,
height: 50, height: 50,
}), }),
label('Click me').apply({ label('Click me').apply({
width: 200, width: 200,
height: 50, height: 50,
bgColor: colors[0], backgroundColor: colors[0],
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(), layoutConfig: layoutConfig().exactly(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ class SliderPanel extends Panel {
}, },
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
bgColor: colors[1], backgroundColor: colors[1],
textAlignment: gravity().center(), textAlignment: gravity().center(),
height: 50, height: 50,
}), }),
@ -38,7 +38,7 @@ class SliderPanel extends Panel {
})).also(it => { })).also(it => {
let start = idx let start = idx
it.onClick = () => { 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 right?: Text
build(root: Group): void { build(root: Group): void {
root.bgColor = Color.parse('#000000') root.backgroundColor = Color.parse('#000000')
vlayout([ vlayout([
text({ text({
text: "Snake", text: "Snake",
@ -160,7 +160,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
}, },
}), }),
(new Stack).also(panel => { (new Stack).also(panel => {
panel.bgColor = Color.parse('#00ff00') panel.backgroundColor = Color.parse('#00ff00')
this.panel = panel this.panel = panel
}), }),
hlayout([ hlayout([
@ -188,7 +188,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "↑", text: "↑",
textSize: 30, textSize: 30,
textAlignment: new Gravity().center(), textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'), backgroundColor: Color.parse('#ffff00'),
layoutConfig: { layoutConfig: {
widthSpec: LayoutSpec.EXACTLY, widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY, heightSpec: LayoutSpec.EXACTLY,
@ -207,7 +207,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "←", text: "←",
textSize: 30, textSize: 30,
textAlignment: new Gravity().center(), textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'), backgroundColor: Color.parse('#ffff00'),
layoutConfig: { layoutConfig: {
widthSpec: LayoutSpec.EXACTLY, widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY, heightSpec: LayoutSpec.EXACTLY,
@ -219,7 +219,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "↓", text: "↓",
textSize: 30, textSize: 30,
textAlignment: new Gravity().center(), textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'), backgroundColor: Color.parse('#ffff00'),
layoutConfig: { layoutConfig: {
widthSpec: LayoutSpec.EXACTLY, widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY, heightSpec: LayoutSpec.EXACTLY,
@ -231,7 +231,7 @@ class SnakeView extends ViewHolder<SnakeModel> {
text: "→", text: "→",
textSize: 30, textSize: 30,
textAlignment: new Gravity().center(), textAlignment: new Gravity().center(),
bgColor: Color.parse('#ffff00'), backgroundColor: Color.parse('#ffff00'),
layoutConfig: { layoutConfig: {
widthSpec: LayoutSpec.EXACTLY, widthSpec: LayoutSpec.EXACTLY,
heightSpec: LayoutSpec.EXACTLY, heightSpec: LayoutSpec.EXACTLY,
@ -292,9 +292,9 @@ class SnakeView extends ViewHolder<SnakeModel> {
this.panel.addChild(item) this.panel.addChild(item)
} }
if (index === nodes.length - 1) { if (index === nodes.length - 1) {
item.bgColor = Color.parse('#ffff00') item.backgroundColor = Color.parse('#ffff00')
} else { } else {
item.bgColor = Color.parse('#ff0000') item.backgroundColor = Color.parse('#ff0000')
} }
item.x = e.x * 10 item.x = e.x * 10
item.y = e.y * 10 item.y = e.y * 10

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@
#import "DoricConstant.h" #import "DoricConstant.h"
#import "DoricSuperNode.h" #import "DoricSuperNode.h"
#import "DoricExtensions.h" #import "DoricExtensions.h"
#import "DoricPromise.h"
void DoricAddEllipticArcPath(CGMutablePathRef path, void DoricAddEllipticArcPath(CGMutablePathRef path,
CGPoint origin, CGPoint origin,
@ -65,6 +66,33 @@ CGPathRef DoricCreateRoundedRectPath(CGRect bounds,
return path; 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 () @interface DoricViewNode ()
@property(nonatomic, strong) NSMutableDictionary *callbackIds; @property(nonatomic, strong) NSMutableDictionary *callbackIds;
@ -148,7 +176,7 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
view.x = [(NSNumber *) prop floatValue]; view.x = [(NSNumber *) prop floatValue];
} else if ([name isEqualToString:@"y"]) { } else if ([name isEqualToString:@"y"]) {
view.y = [(NSNumber *) prop floatValue]; view.y = [(NSNumber *) prop floatValue];
} else if ([name isEqualToString:@"bgColor"]) { } else if ([name isEqualToString:@"backgroundColor"]) {
view.backgroundColor = DoricColor(prop); view.backgroundColor = DoricColor(prop);
} else if ([name isEqualToString:@"alpha"]) { } else if ([name isEqualToString:@"alpha"]) {
view.alpha = [prop floatValue]; 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 @end

View File

@ -1,3 +1,5 @@
import { Modeling, Model } from "../util/types"
/* /*
* Copyright [2019] [Doric.Pub] * Copyright [2019] [Doric.Pub]
* *
@ -14,159 +16,223 @@
* limitations under the License. * limitations under the License.
*/ */
export type AnimatedKey = "translationX" | "translationY" | "scaleX" | "scaleY" | "rotation" | "pivotX" | "pivotY"
export enum RepeatMode { export enum RepeatMode {
RESTART, RESTART = 1,
REVERSE, REVERSE = 2,
} }
export class Animation { export interface IAnimation extends Modeling {
duration: number
translationX?: number delay?: number
translationY?: number
scaleX?: number
scaleY?: number
pivotX?: number
pivotY?: number
rotation?: number
duration = 100
startDelay = 0
repeatCount = 1
repeatMode = RepeatMode.RESTART
} }
export class AnimationSetBuilder { export interface Changeable {
currentNode: Node fromValue: number
group: AnimationSet toValue: number
key: AnimatedKey
constructor(group: AnimationSet, anim: Animation) { repeatCount?: number
this.currentNode = group.getNodeForAnimation(anim) repeatMode?: RepeatMode
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 { export enum FillMode {
children: Set<Node> = new Set /**
siblings: Set<Node> = new Set * The receiver is removed from the presentation when the animation is completed.
parents: Set<Node> = new Set */
animation: Animation Removed = 0,
built = false /**
* The receiver remains visible in its final state when the animation is completed.
constructor(anim: Animation) { */
this.animation = anim Forward = 0x1,
} /**
* The receiver clamps values before zero to zero when the animation is completed.
addParent(node: Node) { */
if (!this.parents.has(node)) { Backward = 0x2,
this.parents.add(node); /**
node.addChild(this); * The receiver clamps values at both ends of the objects time space
} */
} Both = 0x3,
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) { abstract class Animation implements IAnimation {
let node = this.nodeMap.get(anim) changeables: Map<AnimatedKey, Changeable> = new Map
if (node === undefined) { duration = 0
node = new Node(anim) repeatCount?: number
this.nodeMap.set(anim, node) repeatMode?: RepeatMode
this.nodes.push(node) delay?: number
} fillMode = FillMode.Forward
return node; toModel() {
} const changeables = []
play(animation: Animation) { for (let e of this.changeables.values()) {
return new AnimationSetBuilder(this, animation) changeables.push({
} key: e.key,
fromValue: e.fromValue,
playTogether(animations: Animation[]) { toValue: e.toValue,
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)
}) })
} }
} return {
createDependencyGraph() { type: this.constructor.name,
this.nodes.forEach(node => { delay: this.delay,
if (node.built) { duration: this.duration,
return changeables,
} repeatCount: this.repeatCount,
this.findSiblings(node, node.siblings) repeatMode: this.repeatMode,
node.siblings.delete(node) fillMode: this.fillMode,
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
})
})
} }
} }
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 { loge } from "../util/log";
import { BridgeContext } from "../runtime/global"; import { BridgeContext } from "../runtime/global";
import { LayoutConfig } from '../util/layoutconfig' import { LayoutConfig } from '../util/layoutconfig'
import { IAnimation } from "./animation";
export function Property(target: Object, propKey: string) { export function Property(target: Object, propKey: string) {
Reflect.defineMetadata(propKey, true, target) Reflect.defineMetadata(propKey, true, target)
@ -27,7 +28,7 @@ export function Property(target: Object, propKey: string) {
export interface IView { export interface IView {
width?: number width?: number
height?: number height?: number
bgColor?: Color | GradientColor backgroundColor?: Color | GradientColor
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number } corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
border?: { width: number; color: Color; } border?: { width: number; color: Color; }
shadow?: { color: Color; opacity: number; radius: number; offsetX: number; offsetY: number } 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 y: number = 0
@Property @Property
bgColor?: Color | GradientColor backgroundColor?: Color | GradientColor
@Property @Property
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number } corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
@ -333,6 +334,15 @@ export abstract class View implements Modeling, IView {
@Property @Property
rotation?: number rotation?: number
/**----------transform----------*/ /**----------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 { export abstract class Superview extends View {