PageSourceSearch

https://www.visiterouen.com/app/dist/rouen/addons/woody-library/st…/code/es-modules/Core/Animation/Fx.js

js visiterouen.com collected 2026-10-01 13:05:00 UTC 12,886 bytes, 388 lines download raw bytes

1/* *
2 *
3 *  (c) 2010-2024 Torstein Honsi
4 *
5 *  License: www.highcharts.com/license
6 *
7 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
8 *
9 * */
10'use strict';
11import Color from '../Color/Color.js';
12const { parse: color } = Color;
13import H from '../Globals.js';
14const { win } = H;
15import U from '../Utilities.js';
16const { isNumber, objectEach } = U;
17/* eslint-disable no-invalid-this, valid-jsdoc */
18/* *
19 *
20 *  Class
21 *
22 * */
23/**
24 * An animator object used internally. One instance applies to one property
25 * (attribute or style prop) on one element. Animation is always initiated
26 * through {@link SVGElement#animate}.
27 *
28 * @example
29 * let rect = renderer.rect(0, 0, 10, 10).add();
30 * rect.animate({ width: 100 });
31 *
32 * @private
33 * @class
34 * @name Highcharts.Fx
35 *
36 * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
37 * The element to animate.
38 *
39 * @param {Partial<Highcharts.AnimationOptionsObject>} options
40 * Animation options.
41 *
42 * @param {string} prop
43 * The single attribute or CSS property to animate.
44 */
45class Fx {
46    /* *
47     *
48     *  Constructors
49     *
50     * */
51    constructor(elem, options, prop) {
52        this.pos = NaN;
53        this.options = options;
54        this.elem = elem;
55        this.prop = prop;
56    }
57    /* *
58     *
59     *  Functions
60     *
61     * */
62    /**
63     * Set the current step of a path definition on SVGElement.
64     *
65     * @function Highcharts.Fx#dSetter
66     *
67     */
68    dSetter() {
69        const paths = this.paths, start = paths && paths[0], end = paths && paths[1], now = this.now || 0;
70        let path = [];
71        // Land on the final path without adjustment points appended in the ends
72        if (now === 1 || !start || !end) {
73            path = this.toD || [];
74        }
75        else if (start.length === end.length && now < 1) {
76            for (let i = 0; i < end.length; i++) {
77                // Tween between the start segment and the end segment. Start
78                // with a copy of the end segment and tween the appropriate
79                // numerics
80                const startSeg = start[i];
81                const endSeg = end[i];
82                const tweenSeg = [];
83                for (let j = 0; j < endSeg.length; j++) {
84                    const startItem = startSeg[j];
85                    const endItem = endSeg[j];
86                    // Tween numbers
87                    if (isNumber(startItem) &&
88                        isNumber(endItem) &&
89                        // Arc boolean flags
90                        !(endSeg[0] === 'A' && (j === 4 || j === 5))) {
91                        tweenSeg[j] = startItem + now * (endItem - startItem);
92                        // Strings, take directly from the end segment
93                    }
94                    else {
95                        tweenSeg[j] = endItem;
96                    }
97                }
98                path.push(tweenSeg);
99            }
100            // If animation is finished or length not matching, land on right value
101        }
102        else {
103            path = end;
104        }
105        this.elem.attr('d', path, void 0, true);
106    }
107    /**
108     * Update the element with the current animation step.
109     *
110     * @function Highcharts.Fx#update
111     *
112     */
113    update() {
114        const elem = this.elem, prop = this.prop, // If destroyed, it is null
115        now = this.now, step = this.options.step;
116        // Animation setter defined from outside
117        if (this[prop + 'Setter']) {
118            this[prop + 'Setter']();
119            // Other animations on SVGElement
120        }
121        else if (elem.attr) {
122            if (elem.element) {
123                elem.attr(prop, now, null, true);
124            }
125            // HTML styles, raw HTML content like container size
126        }
127        else {
128            elem.style[prop] = now + this.unit;
129        }
130        if (step) {
131            step.call(elem, now, this);
132        }
133    }
134    /**
135     * Run an animation.
136     *
137     * @function Highcharts.Fx#run
138     *
139     * @param {number} from
140     *        The current value, value to start from.
141     *
142     * @param {number} to
143     *        The end value, value to land on.
144     *
145     * @param {string} unit
146     *        The property unit, for example `px`.
147     *
148     */
149    run(from, to, unit) {
150        const self = this, options = self.options, timer = function (gotoEnd) {
151            return timer.stopped ? false : self.step(gotoEnd);
152        }, requestAnimationFrame = win.requestAnimationFrame ||
153            function (step) {
154                setTimeout(step, 13);
155            }, step = function () {
156            for (let i = 0; i < Fx.timers.length; i++) {
157                if (!Fx.timers[i]()) {
158                    Fx.timers.splice(i--, 1);
159                }
160            }
161            if (Fx.timers.length) {
162                requestAnimationFrame(step);
163            }
164        };
165        if (from === to && !this.elem['forceAnimate:' + this.prop]) {
166            delete options.curAnim[this.prop];
167            if (options.complete &&
168                Object.keys(options.curAnim).length === 0) {
169                options.complete.call(this.elem);
170            }
171        }
172        else { // #7166
173            this.startTime = +new Date();
174            this.start = from;
175            this.end = to;
176            this.unit = unit;
177            this.now = this.start;
178            this.pos = 0;
179            timer.elem = this.elem;
180            timer.prop = this.prop;
181            if (timer() && Fx.timers.push(timer) === 1) {
182                requestAnimationFrame(step);
183            }
184        }
185    }
186    /**
187     * Run a single step in the animation.
188     *
189     * @function Highcharts.Fx#step
190     *
191     * @param {boolean} [gotoEnd]
192     *        Whether to go to the endpoint of the animation after abort.
193     *
194     * @return {boolean}
195     *         Returns `true` if animation continues.
196     */
197    step(gotoEnd) {
198        const t = +new Date(), options = this.options, elem = this.elem, complete = options.complete, duration = options.duration, curAnim = options.curAnim;
199        let ret, done;
200        if (!!elem.attr && !elem.element) { // #2616, element is destroyed
201            ret = false;
vendor: 6,689 bytes, lines 202-388
202        }
203        else if (gotoEnd || t >= duration + this.startTime) {
204            this.now = this.end;
205            this.pos = 1;
206            this.update();
207            curAnim[this.prop] = true;
208            done = true;
209            objectEach(curAnim, function (val) {
210                if (val !== true) {
211                    done = false;
212                }
213            });
214            if (done && complete) {
215                complete.call(elem);
216            }
217            ret = false;
218        }
219        else {
220            this.pos = options.easing((t - this.startTime) / duration);
221            this.now = this.start + ((this.end -
222                this.start) * this.pos);
223            this.update();
224            ret = true;
225        }
226        return ret;
227    }
228    /**
229     * Prepare start and end values so that the path can be animated one to one.
230     *
231     * @function Highcharts.Fx#initPath
232     *
233     * @param {Highcharts.SVGElement} elem
234     *        The SVGElement item.
235     *
236     * @param {Highcharts.SVGPathArray|undefined} fromD
237     *        Starting path definition.
238     *
239     * @param {Highcharts.SVGPathArray} toD
240     *        Ending path definition.
241     *
242     * @return {Array<Highcharts.SVGPathArray,Highcharts.SVGPathArray>}
243     *         An array containing start and end paths in array form so that
244     *         they can be animated in parallel.
245     */
246    initPath(elem, fromD, toD) {
247        const startX = elem.startX, endX = elem.endX, end = toD.slice(), // Copy
248        isArea = elem.isArea, positionFactor = isArea ? 2 : 1, disableAnimation = fromD &&
249            toD.length > fromD.length &&
250            toD.hasStackedCliffs; // #16925
251        let shift, fullLength, i, reverse, start = fromD && fromD.slice(); // Copy
252        if (!start || disableAnimation) {
253            return [end, end];
254        }
255        /**
256         * If shifting points, prepend a dummy point to the end path.
257         * @private
258         */
259        function prepend(arr, other) {
260            while (arr.length < fullLength) {
261                // Move to, line to or curve to?
262                const moveSegment = arr[0], otherSegment = other[fullLength - arr.length];
263                if (otherSegment && moveSegment[0] === 'M') {
264                    if (otherSegment[0] === 'C') {
265                        arr[0] = [
266                            'C',
267                            moveSegment[1],
268                            moveSegment[2],
269                            moveSegment[1],
270                            moveSegment[2],
271                            moveSegment[1],
272                            moveSegment[2]
273                        ];
274                    }
275                    else {
276                        arr[0] = ['L', moveSegment[1], moveSegment[2]];
277                    }
278                }
279                // Prepend a copy of the first point
280                arr.unshift(moveSegment);
281                // For areas, the bottom path goes back again to the left, so we
282                // need to append a copy of the last point.
283                if (isArea) {
284                    const z = arr.pop();
285                    arr.push(arr[arr.length - 1], z); // Append point and the Z
286                }
287            }
288        }
289        /**
290         * Copy and append last point until the length matches the end length.
291         * @private
292         */
293        function append(arr) {
294            while (arr.length < fullLength) {
295                // Pull out the slice that is going to be appended or inserted.
296                // In a line graph, the positionFactor is 1, and the last point
297                // is sliced out. In an area graph, the positionFactor is 2,
298                // causing the middle two points to be sliced out, since an area
299                // path starts at left, follows the upper path then turns and
300                // follows the bottom back.
301                const segmentToAdd = arr[Math.floor(arr.length / positionFactor) - 1].slice();
302                // Disable the first control point of curve segments
303                if (segmentToAdd[0] === 'C') {
304                    segmentToAdd[1] = segmentToAdd[5];
305                    segmentToAdd[2] = segmentToAdd[6];
306                }
307                if (!isArea) {
308                    arr.push(segmentToAdd);
309                }
310                else {
311                    const lowerSegmentToAdd = arr[Math.floor(arr.length / positionFactor)].slice();
312                    arr.splice(arr.length / 2, 0, segmentToAdd, lowerSegmentToAdd);
313                }
314            }
315        }
316        // For sideways animation, find out how much we need to shift to get the
317        // start path Xs to match the end path Xs.
318        if (startX && endX && endX.length) {
319            for (i = 0; i < startX.length; i++) {
320                // Moving left, new points coming in on right
321                if (startX[i] === endX[0]) {
322                    shift = i;
323                    break;
324                    // Moving right
325                }
326                else if (startX[0] ===
327                    endX[endX.length - startX.length + i]) {
328                    shift = i;
329                    reverse = true;
330                    break;
331                    // Fixed from the right side, "scaling" left
332                }
333                else if (startX[startX.length - 1] ===
334                    endX[endX.length - startX.length + i]) {
335                    shift = startX.length - i;
336                    break;
337                }
338            }
339            if (typeof shift === 'undefined') {
340                start = [];
341            }
342        }
343        if (start.length && isNumber(shift)) {
344            // The common target length for the start and end array, where both
345            // arrays are padded in opposite ends
346            fullLength = end.length + shift * positionFactor;
347            if (!reverse) {
348                prepend(end, start);
349                append(start);
350            }
351            else {
352                prepend(start, end);
353                append(end);
354            }
355        }
356        return [start, end];
357    }
358    /**
359     * Handle animation of the color attributes directly.
360     *
361     * @function Highcharts.Fx#fillSetter
362     *
363     */
364    fillSetter() {
365        Fx.prototype.strokeSetter.apply(this, arguments);
366    }
367    /**
368     * Handle animation of the color attributes directly.
369     *
370     * @function Highcharts.Fx#strokeSetter
371     *
372     */
373    strokeSetter() {
374        this.elem.attr(this.prop, color(this.start).tweenTo(color(this.end), this.pos), void 0, true);
375    }
376}
377/* *
378 *
379 *  Static Properties
380 *
381 * */
382Fx.timers = [];
383/* *
384 *
385 *  Default Export
386 *
387 * */
388export default Fx;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.