UNPKG

@visactor/vrender-core

Version:
295 lines (276 loc) 16.6 kB
import { splitArc, splitCircle, splitLine, splitRect, splitPolygon, splitArea, splitPath } from "./../common/split-path"; import { CustomPath2D } from "../common/custom-path2d"; import { ACustomAnimate } from "./animate"; import { alignBezierCurves, applyTransformOnBezierCurves, findBestMorphingRotation, pathToBezierCurves } from "../common/morphing-utils"; import { application } from "../application"; import { isNil } from "@visactor/vutils"; import { interpolateColor } from "../color-string/interpolate"; import { ColorStore, ColorType } from "../color-string"; import { DefaultMorphingAnimateConfig } from "./config"; import { isTransformKey } from "../common/utils"; import { AttributeUpdateType } from "../common/enums"; const interpolateOtherAttrs = (attrs, out, ratio) => { attrs.forEach((entry => { if (Number.isFinite(entry.to)) out[entry.key] = entry.from + (entry.to - entry.from) * ratio; else if ("fill" === entry.key || "stroke" === entry.key) { const color = interpolateColor(entry.from, entry.to, ratio, !1); color && (out[entry.key] = color); } })); }, interpolateMorphingData = (morphingData, path, ratio) => { const tmpArr = [], newCp = []; path.clear(); for (let i = 0; i < morphingData.length; i++) { const item = morphingData[i], from = item.from, to = item.to, angle = item.rotation * ratio, fromCp = item.fromCp, toCp = item.toCp, sa = Math.sin(angle), ca = Math.cos(angle); newCp[0] = fromCp[0] + (toCp[0] - fromCp[0]) * ratio, newCp[1] = fromCp[1] + (toCp[1] - fromCp[1]) * ratio; for (let m = 0; m < from.length; m += 2) { const x0 = from[m], y0 = from[m + 1], x = x0 * (1 - ratio) + to[m] * ratio, y = y0 * (1 - ratio) + to[m + 1] * ratio; tmpArr[m] = x * ca - y * sa + newCp[0], tmpArr[m + 1] = x * sa + y * ca + newCp[1]; } let x0 = tmpArr[0], y0 = tmpArr[1]; path.moveTo(x0, y0); for (let m = 2; m < from.length; m += 6) { const x1 = tmpArr[m], y1 = tmpArr[m + 1], x2 = tmpArr[m + 2], y2 = tmpArr[m + 3], x3 = tmpArr[m + 4], y3 = tmpArr[m + 5]; x0 === x1 && y0 === y1 && x2 === x3 && y2 === y3 ? path.lineTo(x3, y3) : path.bezierCurveTo(x1, y1, x2, y2, x3, y3), x0 = x3, y0 = y3; } } }, parseMorphingData = (fromPath, toPath, config) => { const fromBezier = fromPath ? pathToBezierCurves(fromPath) : [], toBezier = pathToBezierCurves(toPath); config && fromBezier && (config.fromTransform && applyTransformOnBezierCurves(fromBezier, config.fromTransform.clone().getInverse()), applyTransformOnBezierCurves(fromBezier, config.toTransfrom)); const [fromBezierCurves, toBezierCurves] = alignBezierCurves(fromBezier, toBezier); return fromPath ? findBestMorphingRotation(fromBezierCurves, toBezierCurves, 10, Math.PI) : toBezierCurves.map(((to, index) => ({ from: fromBezierCurves[index], to: to, fromCp: [ 0, 0 ], toCp: [ 0, 0 ], rotation: 0 }))); }, validateOtherAttrs = [ "fill", "fillOpacity", "shadowBlur", "shadowColor", "shadowOffsetX", "shadowOffsetY", "stroke", "strokeOpacity", "lineDashOffset" ], parseOtherAnimateAttrs = (fromAttrs, toAttrs) => { if (!fromAttrs || !toAttrs) return null; const res = []; let hasAttr = !1; return Object.keys(fromAttrs).forEach((fromKey => { if (!validateOtherAttrs.includes(fromKey)) return; const toValue = toAttrs[fromKey]; isNil(toValue) || isNil(fromAttrs[fromKey]) || toValue === fromAttrs[fromKey] || ("fill" === fromKey || "stroke" === fromKey ? res.push({ from: "string" == typeof fromAttrs[fromKey] ? ColorStore.Get(fromAttrs[fromKey], ColorType.Color255) : fromAttrs[fromKey], to: "string" == typeof toValue ? ColorStore.Get(toValue, ColorType.Color255) : toValue, key: fromKey }) : res.push({ from: fromAttrs[fromKey], to: toValue, key: fromKey }), hasAttr = !0); })), hasAttr ? res : null; }; export class MorphingPath extends ACustomAnimate { constructor(config, duration, easing) { super(0, 1, duration, easing), this.morphingData = config.morphingData, this.otherAttrs = config.otherAttrs, this.saveOnEnd = config.saveOnEnd; } getEndProps() { return {}; } onBind() { this.target.createPathProxy(), this.onUpdate(!1, 0, this.target.attribute); } onEnd() {} onUpdate(end, ratio, out) { const target = this.target, pathProxy = "function" == typeof target.pathProxy ? target.pathProxy(target.attribute) : target.pathProxy; interpolateMorphingData(this.morphingData, pathProxy, ratio), this.otherAttrs && this.otherAttrs.length && interpolateOtherAttrs(this.otherAttrs, out, ratio), end && !this.saveOnEnd && (this.target.pathProxy = null); } } export const morphPath = (fromGraphic, toGraphic, animationConfig, fromGraphicTransform) => { var _a, _b, _c; if (fromGraphic && (!fromGraphic.valid || !fromGraphic.toCustomPath)) return __DEV__ && console.error(fromGraphic, " is not validate"), null; if (!toGraphic.valid || !toGraphic.toCustomPath) return __DEV__ && console.error(toGraphic, " is not validate"), null; let fromTransform = null == fromGraphic ? void 0 : fromGraphic.globalTransMatrix; fromGraphicTransform && fromTransform && (fromTransform = fromGraphicTransform.clone().multiply(fromTransform.a, fromTransform.b, fromTransform.c, fromTransform.d, fromTransform.e, fromTransform.f)); const morphingData = parseMorphingData(null === (_a = null == fromGraphic ? void 0 : fromGraphic.toCustomPath) || void 0 === _a ? void 0 : _a.call(fromGraphic), toGraphic.toCustomPath(), { fromTransform: fromTransform, toTransfrom: toGraphic.globalTransMatrix }), attrs = parseOtherAnimateAttrs(null == fromGraphic ? void 0 : fromGraphic.attribute, toGraphic.attribute), animate = toGraphic.animate(animationConfig); return (null == animationConfig ? void 0 : animationConfig.delay) && animate.wait(animationConfig.delay), animate.play(new MorphingPath({ morphingData: morphingData, otherAttrs: attrs }, null !== (_b = null == animationConfig ? void 0 : animationConfig.duration) && void 0 !== _b ? _b : DefaultMorphingAnimateConfig.duration, null !== (_c = null == animationConfig ? void 0 : animationConfig.easing) && void 0 !== _c ? _c : DefaultMorphingAnimateConfig.easing)), animate; }; export const oneToMultiMorph = (fromGraphic, toGraphics, animationConfig) => { var _a; const validateToGraphics = toGraphics.filter((graphic => graphic && graphic.toCustomPath && graphic.valid)); validateToGraphics.length || __DEV__ && console.error(validateToGraphics, " is not validate"), fromGraphic.valid && fromGraphic.toCustomPath || __DEV__ && console.error(fromGraphic, " is not validate"); const childGraphics = ("clone" === (null == animationConfig ? void 0 : animationConfig.splitPath) ? cloneGraphic : null !== (_a = null == animationConfig ? void 0 : animationConfig.splitPath) && void 0 !== _a ? _a : splitGraphic)(fromGraphic, validateToGraphics.length, !1), oldOnEnd = null == animationConfig ? void 0 : animationConfig.onEnd; let count = validateToGraphics.length; const onEachEnd = () => { count--, 0 === count && oldOnEnd && oldOnEnd(); }; validateToGraphics.forEach(((toChild, index) => { var _a; const fromChild = childGraphics[index], delay = (null !== (_a = null == animationConfig ? void 0 : animationConfig.delay) && void 0 !== _a ? _a : 0) + ((null == animationConfig ? void 0 : animationConfig.individualDelay) ? animationConfig.individualDelay(index, validateToGraphics.length, fromChild, toChild) : 0); morphPath(fromChild, toChild, Object.assign({}, animationConfig, { onEnd: onEachEnd, delay: delay }), fromGraphic.globalTransMatrix); })); }; export class MultiToOneMorphingPath extends ACustomAnimate { constructor(config, duration, easing) { super(0, 1, duration, easing), this.morphingData = config.morphingData, this.otherAttrs = config.otherAttrs; } getEndProps() { return {}; } onBind() { this.addPathProxy(); } addPathProxy() { this.target.shadowRoot.forEachChildren((child => { child.createPathProxy(); })), this.onUpdate(!1, 0, this.target.attribute); } clearPathProxy() { this.target.shadowRoot.forEachChildren((child => { child.pathProxy = null; })); } onEnd() {} onUpdate(end, ratio, out) { this.target.shadowRoot.forEachChildren(((child, index) => { var _a; interpolateMorphingData(this.morphingData[index], "function" == typeof child.pathProxy ? child.pathProxy(child.attribute) : child.pathProxy, ratio), (null === (_a = this.otherAttrs) || void 0 === _a ? void 0 : _a[index]) && this.otherAttrs[index].length && interpolateOtherAttrs(this.otherAttrs[index], child.attribute, ratio); })), end && (this.clearPathProxy(), this.morphingData = null); } } const parseShadowChildAttrs = graphicAttrs => { const attrs = {}; return Object.keys(graphicAttrs).forEach((key => { isTransformKey(key) || (attrs[key] = graphicAttrs[key]); })), attrs; }, appendShadowChildrenToGraphic = (graphic, children, count) => { const childAttrs = parseShadowChildAttrs(graphic.attribute), shadowRoot = graphic.attachShadow(); if (children.length) shadowRoot.setTheme({ [children[0].type]: childAttrs }), children.forEach((element => { element.setAttributes({ pickable: !1 }), shadowRoot.appendChild(element); })); else { const box = graphic.AABBBounds, width = box.width(), height = box.height(); shadowRoot.setTheme({ rect: childAttrs }), new Array(count).fill(0).forEach((el => { const child = application.graphicService.creator.rect({ x: 0, y: 0, width: width, height: height, pickable: !1 }); shadowRoot.appendChild(child), children.push(child); })); } }; export const cloneGraphic = (graphic, count, needAppend) => { const children = [], childAttrs = needAppend ? null : parseShadowChildAttrs(graphic.attribute), path = graphic.toCustomPath(); for (let i = 0; i < count; i++) { const element = { path: (new CustomPath2D).fromCustomPath2D(path) }; children.push(application.graphicService.creator.path(needAppend ? element : Object.assign({}, childAttrs, element))); } return needAppend && appendShadowChildrenToGraphic(graphic, children, count), children; }; export const splitGraphic = (graphic, count, needAppend) => { const children = [], childAttrs = needAppend ? null : parseShadowChildAttrs(graphic.attribute); if ("rect" === graphic.type) { splitRect(graphic, count).forEach((element => { children.push(application.graphicService.creator.rect(needAppend ? element : Object.assign({}, childAttrs, element))); })); } else if ("arc" === graphic.type) { splitArc(graphic, count).forEach((element => { children.push(application.graphicService.creator.arc(needAppend ? element : Object.assign({}, childAttrs, element))); })); } else if ("circle" === graphic.type) { splitCircle(graphic, count).forEach((element => { children.push(application.graphicService.creator.arc(needAppend ? element : Object.assign({}, childAttrs, element))); })); } else if ("line" === graphic.type) { const childrenAttrs = splitLine(graphic, count), defaultSymbol = { size: 10, symbolType: "circle" }; childrenAttrs.forEach((element => { children.push(application.graphicService.creator.symbol(needAppend ? Object.assign({}, element, defaultSymbol) : Object.assign({}, childAttrs, element, defaultSymbol))); })); } else if ("polygon" === graphic.type) { splitPolygon(graphic, count).forEach((element => { children.push(application.graphicService.creator.polygon(needAppend ? element : Object.assign({}, childAttrs, element))); })); } else if ("area" === graphic.type) { splitArea(graphic, count).forEach((element => { children.push(application.graphicService.creator.polygon(needAppend ? element : Object.assign({}, childAttrs, element))); })); } else if ("path" === graphic.type) { splitPath(graphic, count).forEach((element => { "path" in element ? children.push(application.graphicService.creator.path(needAppend ? element : Object.assign({}, childAttrs, element))) : children.push(application.graphicService.creator.polygon(needAppend ? element : Object.assign({}, childAttrs, element))); })); } return needAppend && appendShadowChildrenToGraphic(graphic, children, count), children; }; export const multiToOneMorph = (fromGraphics, toGraphic, animationConfig) => { var _a, _b, _c; const validateFromGraphics = fromGraphics.filter((graphic => graphic.toCustomPath && graphic.valid)); validateFromGraphics.length || __DEV__ && console.error(fromGraphics, " is not validate"), toGraphic.valid && toGraphic.toCustomPath || __DEV__ && console.error(toGraphic, " is not validate"); const childGraphics = ("clone" === (null == animationConfig ? void 0 : animationConfig.splitPath) ? cloneGraphic : null !== (_a = null == animationConfig ? void 0 : animationConfig.splitPath) && void 0 !== _a ? _a : splitGraphic)(toGraphic, validateFromGraphics.length, !0), toAttrs = toGraphic.attribute; toGraphic.setAttribute("visible", !1); const morphingData = validateFromGraphics.map(((graphic, index) => parseMorphingData(graphic.toCustomPath(), childGraphics[index].toCustomPath(), { fromTransform: graphic.globalTransMatrix, toTransfrom: childGraphics[index].globalTransMatrix }))), otherAttrs = validateFromGraphics.map(((graphic, index) => parseOtherAnimateAttrs(graphic.attribute, toAttrs))); if (null == animationConfig ? void 0 : animationConfig.individualDelay) { const oldOnEnd = animationConfig.onEnd; let count = validateFromGraphics.length; const onEachEnd = () => { count--, 0 === count && (toGraphic.setAttributes({ visible: !0, ratio: null }, !1, { type: AttributeUpdateType.ANIMATE_END }), toGraphic.detachShadow(), oldOnEnd && oldOnEnd()); }; childGraphics.forEach(((to, index) => { var _a, _b, _c; const delay = (null !== (_a = animationConfig.delay) && void 0 !== _a ? _a : 0) + animationConfig.individualDelay(index, validateFromGraphics.length, fromGraphics[index], to), animate = to.animate(Object.assign({}, animationConfig, { onEnd: onEachEnd })); animate.wait(delay), animate.play(new MorphingPath({ morphingData: morphingData[index], saveOnEnd: !0, otherAttrs: otherAttrs[index] }, null !== (_b = animationConfig.duration) && void 0 !== _b ? _b : DefaultMorphingAnimateConfig.duration, null !== (_c = animationConfig.easing) && void 0 !== _c ? _c : DefaultMorphingAnimateConfig.easing)); })); } else { const oldOnEnd = null == animationConfig ? void 0 : animationConfig.onEnd, config = animationConfig ? Object.assign({}, animationConfig) : {}; config.onEnd = () => { toGraphic.setAttribute("visible", !0, !1, { type: AttributeUpdateType.ANIMATE_END }), toGraphic.detachShadow(), oldOnEnd && oldOnEnd(); }; const animate = toGraphic.animate(config); (null == animationConfig ? void 0 : animationConfig.delay) && animate.wait(animationConfig.delay), animate.play(new MultiToOneMorphingPath({ morphingData: morphingData, otherAttrs: otherAttrs }, null !== (_b = null == animationConfig ? void 0 : animationConfig.duration) && void 0 !== _b ? _b : DefaultMorphingAnimateConfig.duration, null !== (_c = null == animationConfig ? void 0 : animationConfig.easing) && void 0 !== _c ? _c : DefaultMorphingAnimateConfig.easing)); } }; //# sourceMappingURL=morphing.js.map