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.