You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
10 KiB
275 lines
10 KiB
import { copyTransform } from '../core/Transformable.js'; |
|
import { createBrushScope } from './core.js'; |
|
import SVGPathRebuilder from './SVGPathRebuilder.js'; |
|
import PathProxy from '../core/PathProxy.js'; |
|
import { getPathPrecision, getSRTTransformString } from './helper.js'; |
|
import { each, extend, filter, isNumber, isString, keys } from '../core/util.js'; |
|
import CompoundPath from '../graphic/CompoundPath.js'; |
|
import { createCubicEasingFunc } from '../animation/cubicEasing.js'; |
|
export var EASING_MAP = { |
|
cubicIn: '0.32,0,0.67,0', |
|
cubicOut: '0.33,1,0.68,1', |
|
cubicInOut: '0.65,0,0.35,1', |
|
quadraticIn: '0.11,0,0.5,0', |
|
quadraticOut: '0.5,1,0.89,1', |
|
quadraticInOut: '0.45,0,0.55,1', |
|
quarticIn: '0.5,0,0.75,0', |
|
quarticOut: '0.25,1,0.5,1', |
|
quarticInOut: '0.76,0,0.24,1', |
|
quinticIn: '0.64,0,0.78,0', |
|
quinticOut: '0.22,1,0.36,1', |
|
quinticInOut: '0.83,0,0.17,1', |
|
sinusoidalIn: '0.12,0,0.39,0', |
|
sinusoidalOut: '0.61,1,0.88,1', |
|
sinusoidalInOut: '0.37,0,0.63,1', |
|
exponentialIn: '0.7,0,0.84,0', |
|
exponentialOut: '0.16,1,0.3,1', |
|
exponentialInOut: '0.87,0,0.13,1', |
|
circularIn: '0.55,0,1,0.45', |
|
circularOut: '0,0.55,0.45,1', |
|
circularInOut: '0.85,0,0.15,1' |
|
}; |
|
var transformOriginKey = 'transform-origin'; |
|
function buildPathString(el, kfShape, path) { |
|
var shape = extend({}, el.shape); |
|
extend(shape, kfShape); |
|
el.buildPath(path, shape); |
|
var svgPathBuilder = new SVGPathRebuilder(); |
|
svgPathBuilder.reset(getPathPrecision(el)); |
|
path.rebuildPath(svgPathBuilder, 1); |
|
svgPathBuilder.generateStr(); |
|
return svgPathBuilder.getStr(); |
|
} |
|
function setTransformOrigin(target, transform) { |
|
var originX = transform.originX, originY = transform.originY; |
|
if (originX || originY) { |
|
target[transformOriginKey] = originX + "px " + originY + "px"; |
|
} |
|
} |
|
export var ANIMATE_STYLE_MAP = { |
|
fill: 'fill', |
|
opacity: 'opacity', |
|
lineWidth: 'stroke-width', |
|
lineDashOffset: 'stroke-dashoffset' |
|
}; |
|
function addAnimation(cssAnim, scope) { |
|
var animationName = scope.zrId + '-ani-' + scope.cssAnimIdx++; |
|
scope.cssAnims[animationName] = cssAnim; |
|
return animationName; |
|
} |
|
function createCompoundPathCSSAnimation(el, attrs, scope) { |
|
var paths = el.shape.paths; |
|
var composedAnim = {}; |
|
var cssAnimationCfg; |
|
var cssAnimationName; |
|
each(paths, function (path) { |
|
var subScope = createBrushScope(scope.zrId); |
|
subScope.animation = true; |
|
createCSSAnimation(path, {}, subScope, true); |
|
var cssAnims = subScope.cssAnims; |
|
var cssNodes = subScope.cssNodes; |
|
var animNames = keys(cssAnims); |
|
var len = animNames.length; |
|
if (!len) { |
|
return; |
|
} |
|
cssAnimationName = animNames[len - 1]; |
|
var lastAnim = cssAnims[cssAnimationName]; |
|
for (var percent in lastAnim) { |
|
var kf = lastAnim[percent]; |
|
composedAnim[percent] = composedAnim[percent] || { d: '' }; |
|
composedAnim[percent].d += kf.d || ''; |
|
} |
|
for (var className in cssNodes) { |
|
var val = cssNodes[className].animation; |
|
if (val.indexOf(cssAnimationName) >= 0) { |
|
cssAnimationCfg = val; |
|
} |
|
} |
|
}); |
|
if (!cssAnimationCfg) { |
|
return; |
|
} |
|
attrs.d = false; |
|
var animationName = addAnimation(composedAnim, scope); |
|
return cssAnimationCfg.replace(cssAnimationName, animationName); |
|
} |
|
function getEasingFunc(easing) { |
|
return isString(easing) |
|
? EASING_MAP[easing] |
|
? "cubic-bezier(" + EASING_MAP[easing] + ")" |
|
: createCubicEasingFunc(easing) ? easing : '' |
|
: ''; |
|
} |
|
export function createCSSAnimation(el, attrs, scope, onlyShape) { |
|
var animators = el.animators; |
|
var len = animators.length; |
|
var cssAnimations = []; |
|
if (el instanceof CompoundPath) { |
|
var animationCfg = createCompoundPathCSSAnimation(el, attrs, scope); |
|
if (animationCfg) { |
|
cssAnimations.push(animationCfg); |
|
} |
|
else if (!len) { |
|
return; |
|
} |
|
} |
|
else if (!len) { |
|
return; |
|
} |
|
var groupAnimators = {}; |
|
for (var i = 0; i < len; i++) { |
|
var animator = animators[i]; |
|
var cfgArr = [animator.getMaxTime() / 1000 + 's']; |
|
var easing = getEasingFunc(animator.getClip().easing); |
|
var delay = animator.getDelay(); |
|
if (easing) { |
|
cfgArr.push(easing); |
|
} |
|
else { |
|
cfgArr.push('linear'); |
|
} |
|
if (delay) { |
|
cfgArr.push(delay / 1000 + 's'); |
|
} |
|
if (animator.getLoop()) { |
|
cfgArr.push('infinite'); |
|
} |
|
var cfg = cfgArr.join(' '); |
|
groupAnimators[cfg] = groupAnimators[cfg] || [cfg, []]; |
|
groupAnimators[cfg][1].push(animator); |
|
} |
|
function createSingleCSSAnimation(groupAnimator) { |
|
var animators = groupAnimator[1]; |
|
var len = animators.length; |
|
var transformKfs = {}; |
|
var shapeKfs = {}; |
|
var finalKfs = {}; |
|
var animationTimingFunctionAttrName = 'animation-timing-function'; |
|
function saveAnimatorTrackToCssKfs(animator, cssKfs, toCssAttrName) { |
|
var tracks = animator.getTracks(); |
|
var maxTime = animator.getMaxTime(); |
|
for (var k = 0; k < tracks.length; k++) { |
|
var track = tracks[k]; |
|
if (track.needsAnimate()) { |
|
var kfs = track.keyframes; |
|
var attrName = track.propName; |
|
toCssAttrName && (attrName = toCssAttrName(attrName)); |
|
if (attrName) { |
|
for (var i = 0; i < kfs.length; i++) { |
|
var kf = kfs[i]; |
|
var percent = Math.round(kf.time / maxTime * 100) + '%'; |
|
var kfEasing = getEasingFunc(kf.easing); |
|
var rawValue = kf.rawValue; |
|
if (isString(rawValue) || isNumber(rawValue)) { |
|
cssKfs[percent] = cssKfs[percent] || {}; |
|
cssKfs[percent][attrName] = kf.rawValue; |
|
if (kfEasing) { |
|
cssKfs[percent][animationTimingFunctionAttrName] = kfEasing; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
for (var i = 0; i < len; i++) { |
|
var animator = animators[i]; |
|
var targetProp = animator.targetName; |
|
if (!targetProp) { |
|
!onlyShape && saveAnimatorTrackToCssKfs(animator, transformKfs); |
|
} |
|
else if (targetProp === 'shape') { |
|
saveAnimatorTrackToCssKfs(animator, shapeKfs); |
|
} |
|
} |
|
for (var percent in transformKfs) { |
|
var transform = {}; |
|
copyTransform(transform, el); |
|
extend(transform, transformKfs[percent]); |
|
var str = getSRTTransformString(transform); |
|
var timingFunction = transformKfs[percent][animationTimingFunctionAttrName]; |
|
finalKfs[percent] = str ? { |
|
transform: str |
|
} : {}; |
|
setTransformOrigin(finalKfs[percent], transform); |
|
if (timingFunction) { |
|
finalKfs[percent][animationTimingFunctionAttrName] = timingFunction; |
|
} |
|
} |
|
; |
|
var path; |
|
var canAnimateShape = true; |
|
for (var percent in shapeKfs) { |
|
finalKfs[percent] = finalKfs[percent] || {}; |
|
var isFirst = !path; |
|
var timingFunction = shapeKfs[percent][animationTimingFunctionAttrName]; |
|
if (isFirst) { |
|
path = new PathProxy(); |
|
} |
|
var len_1 = path.len(); |
|
path.reset(); |
|
finalKfs[percent].d = buildPathString(el, shapeKfs[percent], path); |
|
var newLen = path.len(); |
|
if (!isFirst && len_1 !== newLen) { |
|
canAnimateShape = false; |
|
break; |
|
} |
|
if (timingFunction) { |
|
finalKfs[percent][animationTimingFunctionAttrName] = timingFunction; |
|
} |
|
} |
|
; |
|
if (!canAnimateShape) { |
|
for (var percent in finalKfs) { |
|
delete finalKfs[percent].d; |
|
} |
|
} |
|
if (!onlyShape) { |
|
for (var i = 0; i < len; i++) { |
|
var animator = animators[i]; |
|
var targetProp = animator.targetName; |
|
if (targetProp === 'style') { |
|
saveAnimatorTrackToCssKfs(animator, finalKfs, function (propName) { return ANIMATE_STYLE_MAP[propName]; }); |
|
} |
|
} |
|
} |
|
var percents = keys(finalKfs); |
|
var allTransformOriginSame = true; |
|
var transformOrigin; |
|
for (var i = 1; i < percents.length; i++) { |
|
var p0 = percents[i - 1]; |
|
var p1 = percents[i]; |
|
if (finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) { |
|
allTransformOriginSame = false; |
|
break; |
|
} |
|
transformOrigin = finalKfs[p0][transformOriginKey]; |
|
} |
|
if (allTransformOriginSame && transformOrigin) { |
|
for (var percent in finalKfs) { |
|
if (finalKfs[percent][transformOriginKey]) { |
|
delete finalKfs[percent][transformOriginKey]; |
|
} |
|
} |
|
attrs[transformOriginKey] = transformOrigin; |
|
} |
|
if (filter(percents, function (percent) { return keys(finalKfs[percent]).length > 0; }).length) { |
|
var animationName = addAnimation(finalKfs, scope); |
|
return animationName + " " + groupAnimator[0] + " both"; |
|
} |
|
} |
|
for (var key in groupAnimators) { |
|
var animationCfg = createSingleCSSAnimation(groupAnimators[key]); |
|
if (animationCfg) { |
|
cssAnimations.push(animationCfg); |
|
} |
|
} |
|
if (cssAnimations.length) { |
|
var className = scope.zrId + '-cls-' + scope.cssClassIdx++; |
|
scope.cssNodes['.' + className] = { |
|
animation: cssAnimations.join(',') |
|
}; |
|
attrs["class"] = className; |
|
} |
|
}
|
|
|