Spaces:
Running
Running
| /* | |
| * 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 { | |
| separateMorph, | |
| combineMorph, | |
| morphPath, | |
| DividePath, | |
| isCombineMorphing, | |
| SeparateConfig | |
| } from 'zrender/src/tool/morphPath'; | |
| import { Path } from '../util/graphic'; | |
| import SeriesModel from '../model/Series'; | |
| import Element, { ElementAnimateConfig } from 'zrender/src/Element'; | |
| import { defaults, isArray} from 'zrender/src/core/util'; | |
| import { getAnimationConfig } from './basicTransition'; | |
| import { ECElement, UniversalTransitionOption } from '../util/types'; | |
| import { clonePath } from 'zrender/src/tool/path'; | |
| import Model from '../model/Model'; | |
| type DescendentElements = Element[]; | |
| type DescendentPaths = Path[]; | |
| function isMultiple(elements: DescendentElements | DescendentElements[]): elements is DescendentElements[] { | |
| return isArray(elements[0]); | |
| } | |
| interface MorphingBatch { | |
| one: Path; | |
| many: Path[]; | |
| } | |
| function prepareMorphBatches(one: DescendentPaths, many: DescendentPaths[]) { | |
| const batches: MorphingBatch[] = []; | |
| const batchCount = one.length; | |
| for (let i = 0; i < batchCount; i++) { | |
| batches.push({ | |
| one: one[i], | |
| many: [] | |
| }); | |
| } | |
| for (let i = 0; i < many.length; i++) { | |
| const len = many[i].length; | |
| let k; | |
| for (k = 0; k < len; k++) { | |
| batches[k % batchCount].many.push(many[i][k]); | |
| } | |
| } | |
| let off = 0; | |
| // If one has more paths than each one of many. average them. | |
| for (let i = batchCount - 1; i >= 0; i--) { | |
| if (!batches[i].many.length) { | |
| const moveFrom = batches[off].many; | |
| if (moveFrom.length <= 1) { // Not enough | |
| // Start from the first one. | |
| if (off) { | |
| off = 0; | |
| } | |
| else { | |
| return batches; | |
| } | |
| } | |
| const len = moveFrom.length; | |
| const mid = Math.ceil(len / 2); | |
| batches[i].many = moveFrom.slice(mid, len); | |
| batches[off].many = moveFrom.slice(0, mid); | |
| off++; | |
| } | |
| } | |
| return batches; | |
| } | |
| const pathDividers: Record<UniversalTransitionOption['divideShape'], DividePath> = { | |
| clone(params) { | |
| const ret: Path[] = []; | |
| // Fitting the alpha | |
| const approxOpacity = 1 - Math.pow(1 - params.path.style.opacity, 1 / params.count); | |
| for (let i = 0; i < params.count; i++) { | |
| const cloned = clonePath(params.path); | |
| cloned.setStyle('opacity', approxOpacity); | |
| ret.push(cloned); | |
| } | |
| return ret; | |
| }, | |
| // Use the default divider | |
| split: null | |
| }; | |
| export function applyMorphAnimation( | |
| from: DescendentPaths | DescendentPaths[], | |
| to: DescendentPaths | DescendentPaths[], | |
| divideShape: UniversalTransitionOption['divideShape'], | |
| seriesModel: SeriesModel, | |
| dataIndex: number, | |
| animateOtherProps: ( | |
| fromIndividual: Path, | |
| toIndividual: Path, | |
| rawFrom: Path, | |
| rawTo: Path, | |
| animationCfg: ElementAnimateConfig | |
| ) => void | |
| ) { | |
| if (!from.length || !to.length) { | |
| return; | |
| } | |
| const updateAnimationCfg = getAnimationConfig('update', seriesModel, dataIndex); | |
| if (!(updateAnimationCfg && updateAnimationCfg.duration > 0)) { | |
| return; | |
| } | |
| const animationDelay = (seriesModel.getModel('universalTransition') as Model<UniversalTransitionOption>) | |
| .get('delay'); | |
| const animationCfg = Object.assign({ | |
| // Need to setToFinal so the further calculation based on the style can be correct. | |
| // Like emphasis color. | |
| setToFinal: true | |
| } as SeparateConfig, updateAnimationCfg); | |
| let many: DescendentPaths[]; | |
| let one: DescendentPaths; | |
| if (isMultiple(from)) { // manyToOne | |
| many = from; | |
| one = to as DescendentPaths; | |
| } | |
| if (isMultiple(to)) { // oneToMany | |
| many = to; | |
| one = from as DescendentPaths; | |
| } | |
| function morphOneBatch( | |
| batch: MorphingBatch, | |
| fromIsMany: boolean, | |
| animateIndex: number, | |
| animateCount: number, | |
| forceManyOne?: boolean | |
| ) { | |
| const batchMany = batch.many; | |
| const batchOne = batch.one; | |
| if (batchMany.length === 1 && !forceManyOne) { | |
| // Is one to one | |
| const batchFrom: Path = fromIsMany ? batchMany[0] : batchOne; | |
| const batchTo: Path = fromIsMany ? batchOne : batchMany[0]; | |
| if (isCombineMorphing(batchFrom as Path)) { | |
| // Keep doing combine animation. | |
| morphOneBatch({ | |
| many: [batchFrom as Path], | |
| one: batchTo as Path | |
| }, true, animateIndex, animateCount, true); | |
| } | |
| else { | |
| const individualAnimationCfg = animationDelay ? defaults({ | |
| delay: animationDelay(animateIndex, animateCount) | |
| } as ElementAnimateConfig, animationCfg) : animationCfg; | |
| morphPath(batchFrom, batchTo, individualAnimationCfg); | |
| animateOtherProps(batchFrom, batchTo, batchFrom, batchTo, individualAnimationCfg); | |
| } | |
| } | |
| else { | |
| const separateAnimationCfg = defaults({ | |
| dividePath: pathDividers[divideShape], | |
| individualDelay: animationDelay && function (idx, count, fromPath, toPath) { | |
| return animationDelay(idx + animateIndex, animateCount); | |
| } | |
| } as SeparateConfig, animationCfg); | |
| const { | |
| fromIndividuals, | |
| toIndividuals | |
| } = fromIsMany | |
| ? combineMorph(batchMany, batchOne, separateAnimationCfg) | |
| : separateMorph(batchOne, batchMany, separateAnimationCfg); | |
| const count = fromIndividuals.length; | |
| for (let k = 0; k < count; k++) { | |
| const individualAnimationCfg = animationDelay ? defaults({ | |
| delay: animationDelay(k, count) | |
| } as ElementAnimateConfig, animationCfg) : animationCfg; | |
| animateOtherProps( | |
| fromIndividuals[k], | |
| toIndividuals[k], | |
| fromIsMany ? batchMany[k] : batch.one, | |
| fromIsMany ? batch.one : batchMany[k], | |
| individualAnimationCfg | |
| ); | |
| } | |
| } | |
| } | |
| const fromIsMany = many | |
| ? many === from | |
| // Is one to one. If the path number not match. also needs do merge and separate morphing. | |
| : from.length > to.length; | |
| const morphBatches = many | |
| ? prepareMorphBatches(one, many) | |
| : prepareMorphBatches( | |
| (fromIsMany ? to : from) as DescendentPaths, | |
| [(fromIsMany ? from : to) as DescendentPaths] | |
| ); | |
| let animateCount = 0; | |
| for (let i = 0; i < morphBatches.length; i++) { | |
| animateCount += morphBatches[i].many.length; | |
| } | |
| let animateIndex = 0; | |
| for (let i = 0; i < morphBatches.length; i++) { | |
| morphOneBatch(morphBatches[i], fromIsMany, animateIndex, animateCount); | |
| animateIndex += morphBatches[i].many.length; | |
| } | |
| } | |
| export function getPathList( | |
| elements: Element | |
| ): DescendentPaths; | |
| export function getPathList( | |
| elements: Element[] | |
| ): DescendentPaths[]; | |
| export function getPathList( | |
| elements: Element | Element[] | |
| ): DescendentPaths | DescendentPaths[] { | |
| if (!elements) { | |
| return []; | |
| } | |
| if (isArray(elements)) { | |
| const pathList = []; | |
| for (let i = 0; i < elements.length; i++) { | |
| pathList.push(getPathList(elements[i])); | |
| } | |
| return pathList as DescendentPaths[]; | |
| } | |
| const pathList: DescendentPaths = []; | |
| elements.traverse(el => { | |
| if ((el instanceof Path) && !(el as ECElement).disableMorphing && !el.invisible && !el.ignore) { | |
| pathList.push(el); | |
| } | |
| }); | |
| return pathList; | |
| } |