558 lines
18 KiB
558 lines
18 KiB
|
|
/* |
|
* 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. |
|
*/ |
|
|
|
|
|
/** |
|
* AUTO-GENERATED FILE. DO NOT MODIFY. |
|
*/ |
|
|
|
/* |
|
* 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. |
|
*/ |
|
import { makeInner, normalizeToArray } from '../util/model.js'; |
|
import { assert, bind, each, eqNaN, extend, hasOwn, indexOf, isArrayLike, keys, reduce } from 'zrender/lib/core/util.js'; |
|
import { cloneValue } from 'zrender/lib/animation/Animator.js'; |
|
import Displayable from 'zrender/lib/graphic/Displayable.js'; |
|
import { getAnimationConfig } from './basicTransition.js'; |
|
import { Path } from '../util/graphic.js'; |
|
import { warn } from '../util/log.js'; |
|
import { TRANSFORMABLE_PROPS } from 'zrender/lib/core/Transformable.js'; |
|
var LEGACY_TRANSFORM_PROPS_MAP = { |
|
position: ['x', 'y'], |
|
scale: ['scaleX', 'scaleY'], |
|
origin: ['originX', 'originY'] |
|
}; |
|
var LEGACY_TRANSFORM_PROPS = keys(LEGACY_TRANSFORM_PROPS_MAP); |
|
var TRANSFORM_PROPS_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) { |
|
obj[key] = 1; |
|
return obj; |
|
}, {}); |
|
var transformPropNamesStr = TRANSFORMABLE_PROPS.join(', '); // '' means root |
|
|
|
export var ELEMENT_ANIMATABLE_PROPS = ['', 'style', 'shape', 'extra']; |
|
; |
|
var transitionInnerStore = makeInner(); |
|
; |
|
|
|
function getElementAnimationConfig(animationType, el, elOption, parentModel, dataIndex) { |
|
var animationProp = animationType + "Animation"; |
|
var config = getAnimationConfig(animationType, parentModel, dataIndex) || {}; |
|
var userDuring = transitionInnerStore(el).userDuring; // Only set when duration is > 0 and it's need to be animated. |
|
|
|
if (config.duration > 0) { |
|
// For simplicity, if during not specified, the previous during will not work any more. |
|
config.during = userDuring ? bind(duringCall, { |
|
el: el, |
|
userDuring: userDuring |
|
}) : null; |
|
config.setToFinal = true; |
|
config.scope = animationType; |
|
} |
|
|
|
extend(config, elOption[animationProp]); |
|
return config; |
|
} |
|
|
|
export function applyUpdateTransition(el, elOption, animatableModel, opts) { |
|
opts = opts || {}; |
|
var dataIndex = opts.dataIndex, |
|
isInit = opts.isInit, |
|
clearStyle = opts.clearStyle; |
|
var hasAnimation = animatableModel.isAnimationEnabled(); // Save the meta info for further morphing. Like apply on the sub morphing elements. |
|
|
|
var store = transitionInnerStore(el); |
|
var styleOpt = elOption.style; |
|
store.userDuring = elOption.during; |
|
var transFromProps = {}; |
|
var propsToSet = {}; |
|
prepareTransformAllPropsFinal(el, elOption, propsToSet); |
|
prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet); |
|
prepareShapeOrExtraAllPropsFinal('extra', elOption, propsToSet); |
|
|
|
if (!isInit && hasAnimation) { |
|
prepareTransformTransitionFrom(el, elOption, transFromProps); |
|
prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps); |
|
prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps); |
|
prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps); |
|
} |
|
|
|
propsToSet.style = styleOpt; |
|
applyPropsDirectly(el, propsToSet, clearStyle); |
|
applyMiscProps(el, elOption); |
|
|
|
if (hasAnimation) { |
|
if (isInit) { |
|
var enterFromProps_1 = {}; |
|
each(ELEMENT_ANIMATABLE_PROPS, function (propName) { |
|
var prop = propName ? elOption[propName] : elOption; |
|
|
|
if (prop && prop.enterFrom) { |
|
if (propName) { |
|
enterFromProps_1[propName] = enterFromProps_1[propName] || {}; |
|
} |
|
|
|
extend(propName ? enterFromProps_1[propName] : enterFromProps_1, prop.enterFrom); |
|
} |
|
}); |
|
var config = getElementAnimationConfig('enter', el, elOption, animatableModel, dataIndex); |
|
|
|
if (config.duration > 0) { |
|
el.animateFrom(enterFromProps_1, config); |
|
} |
|
} else { |
|
applyPropsTransition(el, elOption, dataIndex || 0, animatableModel, transFromProps); |
|
} |
|
} // Store leave to be used in leave transition. |
|
|
|
|
|
updateLeaveTo(el, elOption); |
|
styleOpt ? el.dirty() : el.markRedraw(); |
|
} |
|
export function updateLeaveTo(el, elOption) { |
|
// Try merge to previous set leaveTo |
|
var leaveToProps = transitionInnerStore(el).leaveToProps; |
|
|
|
for (var i = 0; i < ELEMENT_ANIMATABLE_PROPS.length; i++) { |
|
var propName = ELEMENT_ANIMATABLE_PROPS[i]; |
|
var prop = propName ? elOption[propName] : elOption; |
|
|
|
if (prop && prop.leaveTo) { |
|
if (!leaveToProps) { |
|
leaveToProps = transitionInnerStore(el).leaveToProps = {}; |
|
} |
|
|
|
if (propName) { |
|
leaveToProps[propName] = leaveToProps[propName] || {}; |
|
} |
|
|
|
extend(propName ? leaveToProps[propName] : leaveToProps, prop.leaveTo); |
|
} |
|
} |
|
} |
|
export function applyLeaveTransition(el, elOption, animatableModel, onRemove) { |
|
if (el) { |
|
var parent_1 = el.parent; |
|
var leaveToProps = transitionInnerStore(el).leaveToProps; |
|
|
|
if (leaveToProps) { |
|
// TODO TODO use leave after leaveAnimation in series is introduced |
|
// TODO Data index? |
|
var config = getElementAnimationConfig('update', el, elOption, animatableModel, 0); |
|
|
|
config.done = function () { |
|
parent_1.remove(el); |
|
onRemove && onRemove(); |
|
}; |
|
|
|
el.animateTo(leaveToProps, config); |
|
} else { |
|
parent_1.remove(el); |
|
onRemove && onRemove(); |
|
} |
|
} |
|
} |
|
export function isTransitionAll(transition) { |
|
return transition === 'all'; |
|
} |
|
|
|
function applyPropsDirectly(el, // Can be null/undefined |
|
allPropsFinal, clearStyle) { |
|
var styleOpt = allPropsFinal.style; |
|
|
|
if (!el.isGroup && styleOpt) { |
|
if (clearStyle) { |
|
el.useStyle({}); // When style object changed, how to trade the existing animation? |
|
// It is probably complicated and not needed to cover all the cases. |
|
// But still need consider the case: |
|
// (1) When using init animation on `style.opacity`, and before the animation |
|
// ended users triggers an update by mousewhel. At that time the init |
|
// animation should better be continued rather than terminated. |
|
// So after `useStyle` called, we should change the animation target manually |
|
// to continue the effect of the init animation. |
|
// (2) PENDING: If the previous animation targeted at a `val1`, and currently we need |
|
// to update the value to `val2` and no animation declared, should be terminate |
|
// the previous animation or just modify the target of the animation? |
|
// Therotically That will happen not only on `style` but also on `shape` and |
|
// `transfrom` props. But we haven't handle this case at present yet. |
|
// (3) PENDING: Is it proper to visit `animators` and `targetName`? |
|
|
|
var animators = el.animators; |
|
|
|
for (var i = 0; i < animators.length; i++) { |
|
var animator = animators[i]; // targetName is the "topKey". |
|
|
|
if (animator.targetName === 'style') { |
|
animator.changeTarget(el.style); |
|
} |
|
} |
|
} |
|
|
|
el.setStyle(styleOpt); |
|
} |
|
|
|
if (allPropsFinal) { |
|
// Not set style here. |
|
allPropsFinal.style = null; // Set el to the final state firstly. |
|
|
|
allPropsFinal && el.attr(allPropsFinal); |
|
allPropsFinal.style = styleOpt; |
|
} |
|
} |
|
|
|
function applyPropsTransition(el, elOption, dataIndex, model, // Can be null/undefined |
|
transFromProps) { |
|
if (transFromProps) { |
|
var config = getElementAnimationConfig('update', el, elOption, model, dataIndex); |
|
|
|
if (config.duration > 0) { |
|
el.animateFrom(transFromProps, config); |
|
} |
|
} |
|
} |
|
|
|
function applyMiscProps(el, elOption) { |
|
// Merge by default. |
|
hasOwn(elOption, 'silent') && (el.silent = elOption.silent); |
|
hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore); |
|
|
|
if (el instanceof Displayable) { |
|
hasOwn(elOption, 'invisible') && (el.invisible = elOption.invisible); |
|
} |
|
|
|
if (el instanceof Path) { |
|
hasOwn(elOption, 'autoBatch') && (el.autoBatch = elOption.autoBatch); |
|
} |
|
} // Use it to avoid it be exposed to user. |
|
|
|
|
|
var tmpDuringScope = {}; |
|
var transitionDuringAPI = { |
|
// Usually other props do not need to be changed in animation during. |
|
setTransform: function (key, val) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `setTransform`.'); |
|
} |
|
|
|
tmpDuringScope.el[key] = val; |
|
return this; |
|
}, |
|
getTransform: function (key) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `getTransform`.'); |
|
} |
|
|
|
return tmpDuringScope.el[key]; |
|
}, |
|
setShape: function (key, val) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assertNotReserved(key); |
|
} |
|
|
|
var el = tmpDuringScope.el; |
|
var shape = el.shape || (el.shape = {}); |
|
shape[key] = val; |
|
el.dirtyShape && el.dirtyShape(); |
|
return this; |
|
}, |
|
getShape: function (key) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assertNotReserved(key); |
|
} |
|
|
|
var shape = tmpDuringScope.el.shape; |
|
|
|
if (shape) { |
|
return shape[key]; |
|
} |
|
}, |
|
setStyle: function (key, val) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assertNotReserved(key); |
|
} |
|
|
|
var el = tmpDuringScope.el; |
|
var style = el.style; |
|
|
|
if (style) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (eqNaN(val)) { |
|
warn('style.' + key + ' must not be assigned with NaN.'); |
|
} |
|
} |
|
|
|
style[key] = val; |
|
el.dirtyStyle && el.dirtyStyle(); |
|
} |
|
|
|
return this; |
|
}, |
|
getStyle: function (key) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assertNotReserved(key); |
|
} |
|
|
|
var style = tmpDuringScope.el.style; |
|
|
|
if (style) { |
|
return style[key]; |
|
} |
|
}, |
|
setExtra: function (key, val) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assertNotReserved(key); |
|
} |
|
|
|
var extra = tmpDuringScope.el.extra || (tmpDuringScope.el.extra = {}); |
|
extra[key] = val; |
|
return this; |
|
}, |
|
getExtra: function (key) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assertNotReserved(key); |
|
} |
|
|
|
var extra = tmpDuringScope.el.extra; |
|
|
|
if (extra) { |
|
return extra[key]; |
|
} |
|
} |
|
}; |
|
|
|
function assertNotReserved(key) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (key === 'transition' || key === 'enterFrom' || key === 'leaveTo') { |
|
throw new Error('key must not be "' + key + '"'); |
|
} |
|
} |
|
} |
|
|
|
function duringCall() { |
|
// Do not provide "percent" until some requirements come. |
|
// Because consider thies case: |
|
// enterFrom: {x: 100, y: 30}, transition: 'x'. |
|
// And enter duration is different from update duration. |
|
// Thus it might be confused about the meaning of "percent" in during callback. |
|
var scope = this; |
|
var el = scope.el; |
|
|
|
if (!el) { |
|
return; |
|
} // If el is remove from zr by reason like legend, during still need to called, |
|
// because el will be added back to zr and the prop value should not be incorrect. |
|
|
|
|
|
var latestUserDuring = transitionInnerStore(el).userDuring; |
|
var scopeUserDuring = scope.userDuring; // Ensured a during is only called once in each animation frame. |
|
// If a during is called multiple times in one frame, maybe some users' calculation logic |
|
// might be wrong (not sure whether this usage exists). |
|
// The case of a during might be called twice can be: by default there is a animator for |
|
// 'x', 'y' when init. Before the init animation finished, call `setOption` to start |
|
// another animators for 'style'/'shape'/'extra'. |
|
|
|
if (latestUserDuring !== scopeUserDuring) { |
|
// release |
|
scope.el = scope.userDuring = null; |
|
return; |
|
} |
|
|
|
tmpDuringScope.el = el; // Give no `this` to user in "during" calling. |
|
|
|
scopeUserDuring(transitionDuringAPI); // FIXME: if in future meet the case that some prop will be both modified in `during` and `state`, |
|
// consider the issue that the prop might be incorrect when return to "normal" state. |
|
} |
|
|
|
function prepareShapeOrExtraTransitionFrom(mainAttr, fromEl, elOption, transFromProps) { |
|
var attrOpt = elOption[mainAttr]; |
|
|
|
if (!attrOpt) { |
|
return; |
|
} |
|
|
|
var elPropsInAttr = fromEl[mainAttr]; |
|
var transFromPropsInAttr; |
|
|
|
if (elPropsInAttr) { |
|
var transition = elOption.transition; |
|
var attrTransition = attrOpt.transition; |
|
|
|
if (attrTransition) { |
|
!transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {}); |
|
|
|
if (isTransitionAll(attrTransition)) { |
|
extend(transFromPropsInAttr, elPropsInAttr); |
|
} else { |
|
var transitionKeys = normalizeToArray(attrTransition); |
|
|
|
for (var i = 0; i < transitionKeys.length; i++) { |
|
var key = transitionKeys[i]; |
|
var elVal = elPropsInAttr[key]; |
|
transFromPropsInAttr[key] = elVal; |
|
} |
|
} |
|
} else if (isTransitionAll(transition) || indexOf(transition, mainAttr) >= 0) { |
|
!transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {}); |
|
var elPropsInAttrKeys = keys(elPropsInAttr); |
|
|
|
for (var i = 0; i < elPropsInAttrKeys.length; i++) { |
|
var key = elPropsInAttrKeys[i]; |
|
var elVal = elPropsInAttr[key]; |
|
|
|
if (isNonStyleTransitionEnabled(attrOpt[key], elVal)) { |
|
transFromPropsInAttr[key] = elVal; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
function prepareShapeOrExtraAllPropsFinal(mainAttr, elOption, allProps) { |
|
var attrOpt = elOption[mainAttr]; |
|
|
|
if (!attrOpt) { |
|
return; |
|
} |
|
|
|
var allPropsInAttr = allProps[mainAttr] = {}; |
|
var keysInAttr = keys(attrOpt); |
|
|
|
for (var i = 0; i < keysInAttr.length; i++) { |
|
var key = keysInAttr[i]; // To avoid share one object with different element, and |
|
// to avoid user modify the object inexpectedly, have to clone. |
|
|
|
allPropsInAttr[key] = cloneValue(attrOpt[key]); |
|
} |
|
} |
|
|
|
function prepareTransformTransitionFrom(el, elOption, transFromProps) { |
|
var transition = elOption.transition; |
|
var transitionKeys = isTransitionAll(transition) ? TRANSFORMABLE_PROPS : normalizeToArray(transition || []); |
|
|
|
for (var i = 0; i < transitionKeys.length; i++) { |
|
var key = transitionKeys[i]; |
|
|
|
if (key === 'style' || key === 'shape' || key === 'extra') { |
|
continue; |
|
} |
|
|
|
var elVal = el[key]; |
|
|
|
if (process.env.NODE_ENV !== 'production') { |
|
checkTransformPropRefer(key, 'el.transition'); |
|
} // Do not clone, animator will perform that clone. |
|
|
|
|
|
transFromProps[key] = elVal; |
|
} |
|
} |
|
|
|
function prepareTransformAllPropsFinal(el, elOption, allProps) { |
|
for (var i = 0; i < LEGACY_TRANSFORM_PROPS.length; i++) { |
|
var legacyName = LEGACY_TRANSFORM_PROPS[i]; |
|
var xyName = LEGACY_TRANSFORM_PROPS_MAP[legacyName]; |
|
var legacyArr = elOption[legacyName]; |
|
|
|
if (legacyArr) { |
|
allProps[xyName[0]] = legacyArr[0]; |
|
allProps[xyName[1]] = legacyArr[1]; |
|
} |
|
} |
|
|
|
for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) { |
|
var key = TRANSFORMABLE_PROPS[i]; |
|
|
|
if (elOption[key] != null) { |
|
allProps[key] = elOption[key]; |
|
} |
|
} |
|
} |
|
|
|
function prepareStyleTransitionFrom(fromEl, elOption, styleOpt, transFromProps) { |
|
if (!styleOpt) { |
|
return; |
|
} |
|
|
|
var fromElStyle = fromEl.style; |
|
var transFromStyleProps; |
|
|
|
if (fromElStyle) { |
|
var styleTransition = styleOpt.transition; |
|
var elTransition = elOption.transition; |
|
|
|
if (styleTransition && !isTransitionAll(styleTransition)) { |
|
var transitionKeys = normalizeToArray(styleTransition); |
|
!transFromStyleProps && (transFromStyleProps = transFromProps.style = {}); |
|
|
|
for (var i = 0; i < transitionKeys.length; i++) { |
|
var key = transitionKeys[i]; |
|
var elVal = fromElStyle[key]; // Do not clone, see `checkNonStyleTansitionRefer`. |
|
|
|
transFromStyleProps[key] = elVal; |
|
} |
|
} else if (fromEl.getAnimationStyleProps && (isTransitionAll(elTransition) || isTransitionAll(styleTransition) || indexOf(elTransition, 'style') >= 0)) { |
|
var animationProps = fromEl.getAnimationStyleProps(); |
|
var animationStyleProps = animationProps ? animationProps.style : null; |
|
|
|
if (animationStyleProps) { |
|
!transFromStyleProps && (transFromStyleProps = transFromProps.style = {}); |
|
var styleKeys = keys(styleOpt); |
|
|
|
for (var i = 0; i < styleKeys.length; i++) { |
|
var key = styleKeys[i]; |
|
|
|
if (animationStyleProps[key]) { |
|
var elVal = fromElStyle[key]; |
|
transFromStyleProps[key] = elVal; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
function isNonStyleTransitionEnabled(optVal, elVal) { |
|
// The same as `checkNonStyleTansitionRefer`. |
|
return !isArrayLike(optVal) ? optVal != null && isFinite(optVal) : optVal !== elVal; |
|
} |
|
|
|
var checkTransformPropRefer; |
|
|
|
if (process.env.NODE_ENV !== 'production') { |
|
checkTransformPropRefer = function (key, usedIn) { |
|
if (!hasOwn(TRANSFORM_PROPS_MAP, key)) { |
|
warn('Prop `' + key + '` is not a permitted in `' + usedIn + '`. ' + 'Only `' + keys(TRANSFORM_PROPS_MAP).join('`, `') + '` are permitted.'); |
|
} |
|
}; |
|
} |