blob: e59c4f767d1018b263ee80e221e4678e0f77cb54 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.taobao.weex.dom.transition;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.animation.PathInterpolatorCompat;
import android.text.TextUtils;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.bridge.WXBridgeManager;
import com.taobao.weex.common.Constants;
//import com.taobao.weex.dom.DOMActionContext;
//import com.taobao.weex.dom.WXDomHandler;
//import com.taobao.weex.dom.WXDomObject;
//import com.taobao.weex.dom.flex.Spacing;
import com.taobao.weex.dom.CSSShorthand;
import com.taobao.weex.ui.animation.BackgroundColorProperty;
import com.taobao.weex.ui.animation.TransformParser;
import com.taobao.weex.ui.component.WXComponent;
import com.taobao.weex.utils.SingleFunctionParser;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.WXResourceUtils;
import com.taobao.weex.utils.WXUtils;
import com.taobao.weex.utils.WXViewUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import static com.taobao.weex.common.Constants.TimeFunction.CUBIC_BEZIER;
import static com.taobao.weex.common.Constants.TimeFunction.EASE;
import static com.taobao.weex.common.Constants.TimeFunction.EASE_IN;
import static com.taobao.weex.common.Constants.TimeFunction.EASE_IN_OUT;
import static com.taobao.weex.common.Constants.TimeFunction.EASE_OUT;
import static com.taobao.weex.common.Constants.TimeFunction.LINEAR;
/**
* transition on dom thread
* transition-property: height;
*  transition-duration: .3s;
*  transition-delay: .05s;
*  transition-timing-function: ease-in-out;
*
* Created by furture on 2017/10/18.
*/
public class WXTransition {
public static final String TRANSITION_PROPERTY = "transitionProperty";
public static final String TRANSITION_DURATION = "transitionDuration";
public static final String TRANSITION_DELAY = "transitionDelay";
public static final String TRANSITION_TIMING_FUNCTION = "transitionTimingFunction";
public static final Pattern PROPERTY_SPLIT_PATTERN = Pattern.compile("\\||,");
/**
* layout animation property
* */
private static final Set<String> LAYOUT_PROPERTIES = new HashSet<>();
static {
LAYOUT_PROPERTIES.add(Constants.Name.WIDTH);
LAYOUT_PROPERTIES.add(Constants.Name.HEIGHT);
LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_TOP);
LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_BOTTOM);
LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_LEFT);
LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_RIGHT);
LAYOUT_PROPERTIES.add(Constants.Name.LEFT);
LAYOUT_PROPERTIES.add(Constants.Name.RIGHT);
LAYOUT_PROPERTIES.add(Constants.Name.TOP);
LAYOUT_PROPERTIES.add(Constants.Name.BOTTOM);
LAYOUT_PROPERTIES.add(Constants.Name.PADDING_LEFT);
LAYOUT_PROPERTIES.add(Constants.Name.PADDING_RIGHT);
LAYOUT_PROPERTIES.add(Constants.Name.PADDING_TOP);
LAYOUT_PROPERTIES.add(Constants.Name.PADDING_BOTTOM);
}
/**
* transform animation property, use android system animaton ability
* */
private static final Set<String> TRANSFORM_PROPERTIES = new HashSet<>();
static {
TRANSFORM_PROPERTIES.add(Constants.Name.OPACITY);
TRANSFORM_PROPERTIES.add(Constants.Name.BACKGROUND_COLOR);
TRANSFORM_PROPERTIES.add(Constants.Name.TRANSFORM);
}
private List<String> properties;
private Interpolator interpolator;
private long duration;
private long delay;
private WXComponent mWXComponent;
private Handler handler;
private ValueAnimator layoutValueAnimator;
private Map<String, Object> layoutPendingUpdates;
private ObjectAnimator transformAnimator;
private Map<String, Object> transformPendingUpdates;
private Runnable transitionEndEvent;
private Map<String, Object> targetStyles;
private Runnable animationRunnable;
private Runnable transformAnimationRunnable;
private volatile AtomicInteger lockToken = new AtomicInteger(0);
public WXTransition() {
this.properties = new ArrayList<>(4);
this.handler = new Handler();
this.layoutPendingUpdates = new ArrayMap<>();
this.transformPendingUpdates = new ArrayMap<>();
this.targetStyles = new ArrayMap<>();
}
/**
* create transition from map styles if style contains transitionProperty
* */
public static WXTransition fromMap(Map<String, Object> style, WXComponent component){
if(style.get(TRANSITION_PROPERTY) == null){
return null;
}
String propertyString = WXUtils.getString(style.get(TRANSITION_PROPERTY), null);
if(propertyString == null){
return null;
}
WXTransition transition = new WXTransition();
updateTransitionProperties(transition, propertyString);
if(transition.properties.isEmpty()){
return null;
}
transition.duration = parseTimeMillis(style, TRANSITION_DURATION, 0);
transition.delay = parseTimeMillis(style, TRANSITION_DELAY, 0);
transition.interpolator = createTimeInterpolator(WXUtils.getString(style.get(TRANSITION_TIMING_FUNCTION), null));
transition.mWXComponent = component;
return transition;
}
/**
* check updates has transition property
* */
public boolean hasTransitionProperty(Map<String, Object> styles){
for(String property : properties){
if(styles.containsKey(property)){
return true;
}
}
return false;
}
public void updateTranstionParams(Map<String, Object> updates){
if(updates.containsKey(TRANSITION_DELAY)){
mWXComponent.getStyles().put(TRANSITION_DELAY, updates.remove(TRANSITION_DELAY));
this.delay = parseTimeMillis(mWXComponent.getStyles(), TRANSITION_DELAY, 0);
}
if(updates.containsKey(TRANSITION_TIMING_FUNCTION) && updates.get(TRANSITION_TIMING_FUNCTION) != null){
mWXComponent.getStyles().put(TRANSITION_TIMING_FUNCTION, updates.remove(TRANSITION_TIMING_FUNCTION));
this.interpolator = createTimeInterpolator(mWXComponent.getStyles().get(TRANSITION_TIMING_FUNCTION).toString());
}
if(updates.containsKey(TRANSITION_DURATION)){
mWXComponent.getStyles().put(TRANSITION_DURATION, updates.remove(TRANSITION_DURATION));
this.duration = parseTimeMillis(mWXComponent.getStyles(), TRANSITION_DURATION, 0);
}
if(updates.containsKey(TRANSITION_PROPERTY)){
mWXComponent.getStyles().put(TRANSITION_PROPERTY, updates.remove(TRANSITION_PROPERTY));
updateTransitionProperties(this, WXUtils.getString(mWXComponent.getStyles().get(TRANSITION_PROPERTY), null));
}
}
/**
* start transition animation, updates maybe split two different updates,
* because javascript will send multi update on same transition, we assume that updates in 8ms is one transition
* */
public void startTransition(Map<String, Object> updates){
synchronized (lockToken){
final View taregtView = getTargetView();
if(taregtView == null){
return;
}
final int token = lockToken.incrementAndGet();
for(String property : properties){
if(updates.containsKey(property)){
Object targetValue = updates.remove(property);
if(LAYOUT_PROPERTIES.contains(property)) {
layoutPendingUpdates.put(property, targetValue);
}else if(TRANSFORM_PROPERTIES.contains(property)){
transformPendingUpdates.put(property, targetValue);
}
}
}
int delay = WXUtils.getNumberInt(mWXComponent.getAttrs().get("actionDelay"), 16);
if(delay > duration){
delay = (int) duration;
}
if(animationRunnable != null) {
handler.removeCallbacks(animationRunnable);
}
animationRunnable = new Runnable() {
@Override
public void run() {
if(token == lockToken.get()) {
doTransitionAnimation(token);
}
animationRunnable = null;
}
};
if(delay > 0){
handler.postDelayed(animationRunnable, delay);
}else{
animationRunnable.run();
}
}
}
/**
* doTransitionAnimation include transform and layout animation.
* 1. put pre transition updates from target style to dom style
* 2. do transform animation and layout animation
* */
private void doTransitionAnimation(final int token){
final View taregtView = getTargetView();
if(taregtView == null){
return;
}
if(targetStyles.size() > 0){
for(String property : properties){
if(!(LAYOUT_PROPERTIES.contains(property) || TRANSFORM_PROPERTIES.contains(property))){
continue;
}
if(layoutPendingUpdates.containsKey(property)){
continue;
}
if(transformPendingUpdates.containsKey(property)){
continue;
}
synchronized (targetStyles){
if(targetStyles.containsKey(property)){
//reset pre transition style
Object targetValue = targetStyles.remove(property);
mWXComponent.getStyles().put(property, targetValue);
// domObject.getStyles().put(property, targetValue);
// WXComponent component = getComponent();
// if(component != null && component.getDomObject() != null){
// component.getDomObject().getStyles().put(property, targetValue);
// }
}
}
}
}
if(transitionEndEvent != null){
taregtView.removeCallbacks(transitionEndEvent);
}
if(transitionEndEvent == null && duration > Float.MIN_NORMAL){
transitionEndEvent = new Runnable(){
@Override
public void run() {
transitionEndEvent = null;
if(duration < Float.MIN_NORMAL){
return;
}
if(mWXComponent != null && mWXComponent.getEvents().contains(Constants.Event.ON_TRANSITION_END)){
mWXComponent.fireEvent(Constants.Event.ON_TRANSITION_END);
}
}
};
}
if(transformAnimationRunnable != null) {
taregtView.removeCallbacks(transformAnimationRunnable);
}
transformAnimationRunnable = new Runnable() {
@Override
public void run() {
synchronized (lockToken) {
if(token == lockToken.get()) {
doPendingTransformAnimation(token);
}
}
}
};
taregtView.post(transformAnimationRunnable);
doPendingLayoutAnimation();
}
/**
* transform, opacity, backgroundcolor which not effect layout use android system animation in main thread.
* */
private void doPendingTransformAnimation(int token) {
if(transformAnimator != null){
transformAnimator.cancel();
transformAnimator = null;
}
if(transformPendingUpdates.size() == 0){
return;
}
final View taregtView = getTargetView();
if(taregtView == null){
return;
}
List<PropertyValuesHolder> holders = new ArrayList<>(8);
String transform = WXUtils.getString(transformPendingUpdates.remove(Constants.Name.TRANSFORM), null);
if(!TextUtils.isEmpty(transform)){
Map<Property<View,Float>, Float> properties = TransformParser.parseTransForm(transform, (int)mWXComponent.getLayoutWidth(), (int)mWXComponent.getLayoutHeight(), mWXComponent.getViewPortWidth());
PropertyValuesHolder[] transformHolders = TransformParser.toHolders(properties);
for(PropertyValuesHolder holder : transformHolders){
holders.add(holder);
}
synchronized (targetStyles) {
targetStyles.put(Constants.Name.TRANSFORM, transform);
}
}
for(String property : properties){
if(!TRANSFORM_PROPERTIES.contains(property)){
continue;
}
if(!transformPendingUpdates.containsKey(property)){
continue;
}
Object value = transformPendingUpdates.remove(property);
synchronized (targetStyles) {
targetStyles.put(property, value);
}
switch (property){
case Constants.Name.OPACITY:{
holders.add(PropertyValuesHolder.ofFloat(View.ALPHA, taregtView.getAlpha(), WXUtils.getFloat(value, 1.0f)));
taregtView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); //hardware or none has bug on some platform
}
break;
case Constants.Name.BACKGROUND_COLOR:{
int fromColor = WXResourceUtils.getColor(WXUtils.getString(mWXComponent.getStyles().getBackgroundColor(), null), 0);
int toColor = WXResourceUtils.getColor(WXUtils.getString(value, null), 0);
if(WXViewUtils.getBorderDrawable(taregtView) != null){
fromColor = WXViewUtils.getBorderDrawable(taregtView).getColor();
}else if (taregtView.getBackground() instanceof ColorDrawable) {
fromColor = ((ColorDrawable) taregtView.getBackground()).getColor();
}
holders.add(PropertyValuesHolder.ofObject(new BackgroundColorProperty(), new ArgbEvaluator(), fromColor,toColor));
}
break;
default:break;
}
}
if(token == lockToken.get()) {
transformPendingUpdates.clear();
}
transformAnimator = ObjectAnimator.ofPropertyValuesHolder(taregtView, holders.toArray(new PropertyValuesHolder[holders.size()]));
transformAnimator.setDuration((long) duration);
if((long) delay > 0) {
transformAnimator.setStartDelay((long) delay);
}
if(interpolator != null) {
transformAnimator.setInterpolator(interpolator);
}
transformAnimator.addListener(new AnimatorListenerAdapter() {
boolean hasCancel = false;
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
hasCancel = true;
}
@Override
public void onAnimationEnd(Animator animation) {
if(hasCancel){
return;
}
super.onAnimationEnd(animation);
WXTransition.this.onTransitionAnimationEnd();
}
});
transformAnimator.start();
}
public void doPendingLayoutAnimation(){
if(layoutValueAnimator != null){
layoutValueAnimator.cancel();
layoutValueAnimator = null;
}
if(layoutPendingUpdates.size() == 0){
return;
}
PropertyValuesHolder[] holders = new PropertyValuesHolder[layoutPendingUpdates.size()];
int index = 0;
for(String property : properties){
if(!LAYOUT_PROPERTIES.contains(property)){
continue;
}
if(layoutPendingUpdates.containsKey(property)){
Object targetValue = layoutPendingUpdates.remove(property);
synchronized (targetStyles) {
targetStyles.put(property, targetValue);
}
holders[index] = createLayoutPropertyValueHolder(property, targetValue);
index++;
}
}
layoutPendingUpdates.clear();
doLayoutPropertyValuesHolderAnimation(holders);
}
private PropertyValuesHolder createLayoutPropertyValueHolder(String property, Object value){
PropertyValuesHolder holder = null;
switch (property){
case Constants.Name.WIDTH:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.WIDTH, mWXComponent.getLayoutWidth(),
WXViewUtils.getRealPxByWidth(WXUtils.getFloat(value, 0.0f), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.HEIGHT:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.HEIGHT, mWXComponent.getLayoutHeight(),
WXViewUtils.getRealPxByWidth(WXUtils.getFloat(value, 0.0f), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.MARGIN_TOP:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_TOP, mWXComponent.getMargin().get(CSSShorthand.EDGE.TOP),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.MARGIN_LEFT:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_LEFT, mWXComponent.getMargin().get(CSSShorthand.EDGE.LEFT),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.MARGIN_RIGHT:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_RIGHT, mWXComponent.getMargin().get(CSSShorthand.EDGE.RIGHT),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.MARGIN_BOTTOM:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_BOTTOM, mWXComponent.getMargin().get(CSSShorthand.EDGE.BOTTOM),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.LEFT:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.LEFT, mWXComponent.getPadding().get(CSSShorthand.EDGE.LEFT),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.RIGHT:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.RIGHT, mWXComponent.getPadding().get(CSSShorthand.EDGE.RIGHT),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.BOTTOM:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.BOTTOM, mWXComponent.getPadding().get(CSSShorthand.EDGE.BOTTOM),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.TOP:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.TOP, mWXComponent.getPadding().get(CSSShorthand.EDGE.TOP),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.PADDING_TOP:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_TOP, mWXComponent.getPadding().get(CSSShorthand.EDGE.TOP),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.PADDING_BOTTOM:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_BOTTOM, mWXComponent.getPadding().get(CSSShorthand.EDGE.BOTTOM),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.PADDING_LEFT:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_LEFT, mWXComponent.getPadding().get(CSSShorthand.EDGE.LEFT),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
case Constants.Name.PADDING_RIGHT:{
holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_RIGHT, mWXComponent.getPadding().get(CSSShorthand.EDGE.RIGHT),
WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
}
break;
default:
break;
}
if(holder == null){
holder = PropertyValuesHolder.ofFloat(property, 1, 1);
}
return holder;
}
private void doLayoutPropertyValuesHolderAnimation(PropertyValuesHolder[] holders){
layoutValueAnimator = ValueAnimator.ofPropertyValuesHolder(holders);
layoutValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(final ValueAnimator animation) {
PropertyValuesHolder holders[] = animation.getValues();
for(PropertyValuesHolder holder : holders){
final String property = holder.getPropertyName();
asynchronouslyUpdateLayout(mWXComponent, property, (Float) animation.getAnimatedValue(property));
}
// WXBridgeManager.getInstance().calculateLayoutPostToJSThread(mWXComponent.getInstanceId(), mWXComponent.getRef(), false);
}
});
layoutValueAnimator.addListener(new AnimatorListenerAdapter() {
boolean hasCancel = false;
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
hasCancel = true;
}
@Override
public void onAnimationEnd(Animator animation) {
if(hasCancel){
return;
}
super.onAnimationEnd(animation);
WXTransition.this.onTransitionAnimationEnd();
}
});
if(interpolator != null) {
layoutValueAnimator.setInterpolator(interpolator);
}
layoutValueAnimator.setStartDelay((long) (delay));
layoutValueAnimator.setDuration((long) (duration));
layoutValueAnimator.start();
}
@SuppressWarnings("unused")
public static void asynchronouslyUpdateLayout(WXComponent component, final String propertyName, final float propertyValue) {
if(component == null) {
return;
}
final String ref = component.getRef();
final String instanceId = component.getInstanceId();
if(TextUtils.isEmpty(ref) || TextUtils.isEmpty(instanceId)) {
return;
}
WXSDKManager.getInstance().getWXBridgeManager().post(new Runnable() {
@Override
public void run() {
switch (propertyName){
case Constants.Name.WIDTH:{
WXBridgeManager.getInstance().setStyleWidth(instanceId, ref, propertyValue);
}
break;
case Constants.Name.HEIGHT:{
WXBridgeManager.getInstance().setStyleHeight(instanceId, ref, propertyValue);
}
break;
case Constants.Name.MARGIN_TOP:{
WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.TOP, propertyValue);
}
break;
case Constants.Name.MARGIN_LEFT:{
WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.LEFT, propertyValue);
}
break;
case Constants.Name.MARGIN_RIGHT:{
WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.RIGHT, propertyValue);
}
break;
case Constants.Name.MARGIN_BOTTOM:{
WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.BOTTOM, propertyValue);
}
break;
case Constants.Name.LEFT:{
WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.LEFT, propertyValue);
}
break;
case Constants.Name.RIGHT:{
WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.RIGHT, (propertyValue));
}
break;
case Constants.Name.BOTTOM:{
WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.BOTTOM, propertyValue);
}
break;
case Constants.Name.TOP:{
WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.TOP, propertyValue);
}
break;
case Constants.Name.PADDING_TOP:{
WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.TOP, propertyValue);
}
break;
case Constants.Name.PADDING_BOTTOM:{
WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.BOTTOM, propertyValue);
}
break;
case Constants.Name.PADDING_LEFT:{
WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.LEFT, propertyValue);
}
break;
case Constants.Name.PADDING_RIGHT:{
WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.RIGHT, propertyValue);
}
break;
default:
break;
}
}
});
}
private synchronized void onTransitionAnimationEnd(){
if(duration > 0){
if(transitionEndEvent != null){
View view = getTargetView();
if(view != null && transitionEndEvent != null){
view.post(transitionEndEvent);
}
transitionEndEvent = null;
}
}
synchronized (targetStyles){
if(targetStyles.size() > 0){
for(String property : properties) {
if(targetStyles.containsKey(property)){
Object targetValue = targetStyles.remove(property);
mWXComponent.getStyles().put(property, targetValue);
}
}
targetStyles.clear();
}
}
}
private View getTargetView(){
return null != mWXComponent ? mWXComponent.getHostView() : null;
}
/**
* get time millis
* */
private static long parseTimeMillis(Map<String, Object> style, String key, long defaultValue){
String duration = WXUtils.getString(style.get(key), null);
if(duration != null){
duration = duration.replaceAll("ms", "");
}
if(TextUtils.isEmpty(duration)){
return defaultValue;
}
try{
return (long)Float.parseFloat(duration);
}catch (NumberFormatException e){
return defaultValue;
}
}
/**
* create interpolcator same with web
* http://www.w3school.com.cn/cssref/pr_transition-timing-function.asp
* */
private static Interpolator createTimeInterpolator(String interpolator) {
if (!TextUtils.isEmpty(interpolator)) {
switch (interpolator) {
case EASE_IN:
return PathInterpolatorCompat.create(0.42f,0f, 1f,1f);
case EASE_OUT:
return PathInterpolatorCompat.create(0f,0f, 0.58f,1f);
case EASE_IN_OUT:
return PathInterpolatorCompat.create(0.42f,0f, 0.58f,1f);
case EASE:
return PathInterpolatorCompat.create(0.25f,0.1f, 0.25f,1f);
case LINEAR:
return PathInterpolatorCompat.create(0.0f,0f, 1f,1f);
default:
try {
//Parse cubic-bezier
SingleFunctionParser<Float> parser = new SingleFunctionParser<>(
interpolator,
new SingleFunctionParser.FlatMapper<Float>() {
@Override
public Float map(String raw) {
return Float.parseFloat(raw);
}
});
List<Float> params = parser.parse(CUBIC_BEZIER);
if (params != null && params.size() == 4) {
return PathInterpolatorCompat.create(
params.get(0), params.get(1), params.get(2), params.get(3));
}
} catch (RuntimeException e) {
if(WXEnvironment.isApkDebugable()) {
WXLogUtils.e("WXTransition", e);
}
}
}
}
return PathInterpolatorCompat.create(0.25f,0.1f, 0.25f,1f);
}
private static void updateTransitionProperties(WXTransition transition, String transtionProperty){
if(transtionProperty == null){
return;
}
transition.properties.clear();
String[] propertiesArray = PROPERTY_SPLIT_PATTERN.split(transtionProperty);
for(String property : propertiesArray){
String trim = property.trim();
if(TextUtils.isEmpty(trim)){
continue;
}
if(!(LAYOUT_PROPERTIES.contains(trim) || TRANSFORM_PROPERTIES.contains(trim))){
if(WXEnvironment.isApkDebugable()){
WXLogUtils.e("WXTransition Property Not Supported" + trim + " in " + transtionProperty);
}
continue;
}
transition.properties.add(trim);
}
}
public List<String> getProperties() {
return properties;
}
}