@aarsteinmedia/dotlottie-player 6.5.3 → 6.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { getAnimationData } from '@aarsteinmedia/lottie-web/dotlottie';
2
- import { isServer, PlayMode, PreserveAspectRatio, RendererType, createElementID, PlayerEvent, download, getFilename, clamp } from '@aarsteinmedia/lottie-web/utils';
2
+ import { isServer, PreserveAspectRatio, PlayMode, RendererType, createElementID, clamp, PlayerEvent, download, getFilename } from '@aarsteinmedia/lottie-web/utils';
3
3
 
4
4
  let modulePromise$1;
5
5
  function loadControlsModule() {
@@ -73,45 +73,6 @@ if (isServer) {
73
73
 
74
74
  var css_248z = "*{box-sizing:border-box}:host{--lottie-player-toolbar-height:35px;--lottie-player-toolbar-background-color:#fff;--lottie-player-toolbar-icon-color:#000;--lottie-player-toolbar-icon-hover-color:#000;--lottie-player-toolbar-icon-active-color:#4285f4;--lottie-player-seeker-track-color:#0003;--lottie-player-seeker-thumb-color:#4285f4;--lottie-player-seeker-display:block;height:100%;width:100%;&:not([hidden]){display:block}.main{display:flex;flex-direction:column;height:100%;margin:0;padding:0;width:100%}.animation{contain:content;display:flex;height:100%;margin:0;padding:0;width:100%;& svg g{will-change:transform}}[data-controls=true] .animation{height:calc(100% - 35px)}.animation-container{position:relative}.popover{animation:fade-in .2s ease-in-out;background-color:var(--lottie-player-toolbar-background-color);border:2px solid var(--lottie-player-toolbar-icon-color);border-radius:5px;bottom:40px;padding:10px 15px;position:absolute;right:5px;&:before{border:7px solid #0000;border-top:7px solid var(--lottie-player-toolbar-icon-color);content:\"\";height:0;margin-right:-7px;pointer-events:none;position:absolute;right:10px;top:100%;width:0}}.error{align-items:center;display:flex;height:100%;justify-content:center;margin:auto;& svg{height:auto;width:100%}}.toolbar{background:var(--lottie-player-toolbar-background-color);border-radius:5px;display:flex;gap:5px;height:35px;margin:0;padding:5px;place-items:center center;&.has-error{opacity:.5;pointer-events:none}& button{fill:var(--lottie-player-toolbar-icon-color);align-items:center;background:none;border:0;color:var(--lottie-player-toolbar-icon-color);cursor:pointer;gap:5px;height:100%;margin:0;opacity:.9;outline:0;padding:0;&:not([hidden]){display:flex}&:hover{opacity:1}&[data-active=true]{fill:var(--lottie-player-toolbar-icon-active-color);opacity:1}&:disabled{opacity:.5}&:focus{outline:0}& svg{pointer-events:none;&>*{fill:inherit}}&.disabled svg{display:none}}}.progress-container{position:relative;width:100%;&.simple{margin-right:12px}}& progress{appearance:none;background-color:var(--lottie-player-seeker-track-color);border:0;border-radius:3px;height:5px;left:0;margin:7.5px 0;outline:none;pointer-events:none;position:absolute;top:0;width:100%;&::-webkit-progress-inner-element{border-radius:3px;overflow:hidden}&::-webkit-slider-runnable-track{background-color:initial}&::-webkit-progress-value{background-color:var(--lottie-player-seeker-thumb-color)}}.seeker{appearance:none;background-color:initial;border:0;border-radius:3px;color:var(--lottie-player-seeker-thumb-color);cursor:pointer;display:var(--lottie-player-seeker-display);height:20px;margin:0;outline:none;padding:7.5px 0;position:relative;width:100%;z-index:1;&::-webkit-slider-runnable-track,&::-webkit-slider-thumb{appearance:none;outline:none}&::-webkit-slider-thumb{background-color:var(--lottie-player-seeker-thumb-color);border:0;border-radius:50%;cursor:pointer;height:15px;transform:scale(0);-webkit-transition:transform .2s ease-in-out;transition:transform .2s ease-in-out;width:15px}&:focus::-webkit-slider-thumb,&:hover::-webkit-slider-thumb{transform:scale(1)}&::-moz-range-progress{background-color:var(--lottie-player-seeker-thumb-color);border-radius:3px;height:5px}&::-moz-range-thumb{background-color:var(--lottie-player-seeker-thumb-color);border:0;border-radius:50%;cursor:pointer;height:15px;transform:scale(0);-moz-transition:transform .2s ease-in-out;transition:transform .2s ease-in-out;width:15px}&:focus::-moz-range-thumb,&:hover::-moz-range-thumb{transform:scale(1)}&::-ms-track{background:#0000;border-color:#0000;color:#0000;cursor:pointer;height:5px;width:100%}&::-ms-fill-upper{background:var(--lottie-player-seeker-track-color);border-radius:3px}&::-ms-fill-lower{background-color:var(--lottie-player-seeker-thumb-color);border-radius:3px}&::-ms-thumb{background:var(--lottie-player-seeker-thumb-color);border:0;border-radius:50%;cursor:pointer;height:15px;transform:scale(0);-ms-transition:transform .2s ease-in-out;transition:transform .2s ease-in-out;width:15px}&:hover::-ms-thumb{transform:scale(1)}&:focus{&::-ms-thumb{transform:scale(1)}&::-ms-fill-lower,&::-ms-fill-upper{background:var(--lottie-player-seeker-track-color)}}}& ::-moz-progress-bar{background-color:var(--lottie-player-seeker-thumb-color)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@media (prefers-color-scheme:dark){:host{--lottie-player-toolbar-background-color:#000;--lottie-player-toolbar-icon-color:#fff;--lottie-player-toolbar-icon-hover-color:#fff;--lottie-player-seeker-track-color:#fff9}}";
75
75
 
76
- let dotlottieTools = null, loadPromise = null;
77
- function loadDotLottieTools() {
78
- if (dotlottieTools) {
79
- return Promise.resolve(dotlottieTools);
80
- }
81
- loadPromise = loadPromise ?? (async ()=>{
82
- const { addAnimation, convert } = await import('@aarsteinmedia/lottie-web/dotlottie');
83
- dotlottieTools = {
84
- addAnimation,
85
- convert
86
- };
87
- return {
88
- addAnimation,
89
- convert
90
- };
91
- })();
92
- return loadPromise;
93
- }
94
-
95
- const boomerangIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m11.8 13.2-.3.3c-.5.5-1.1 1.1-1.7 1.5-.5.4-1 .6-1.5.8-.5.2-1.1.3-1.6.3s-1-.1-1.5-.3c-.6-.2-1-.5-1.4-1-.5-.6-.8-1.2-.9-1.9-.2-.9-.1-1.8.3-2.6.3-.7.8-1.2 1.3-1.6.3-.2.6-.4 1-.5.2-.2.5-.2.8-.3.3 0 .7-.1 1 0 .3 0 .6.1.9.2.9.3 1.7.9 2.4 1.5.4.4.8.7 1.1 1.1l.1.1.4-.4c.6-.6 1.2-1.2 1.9-1.6.5-.3 1-.6 1.5-.7.4-.1.7-.2 1-.2h.9c1 .1 1.9.5 2.6 1.4.4.5.7 1.1.8 1.8.2.9.1 1.7-.2 2.5-.4.9-1 1.5-1.8 2-.4.2-.7.4-1.1.4-.4.1-.8.1-1.2.1-.5 0-.9-.1-1.3-.3-.8-.3-1.5-.9-2.1-1.5-.4-.4-.8-.7-1.1-1.1h-.3zm-1.1-1.1c-.1-.1-.1-.1 0 0-.3-.3-.6-.6-.8-.9-.5-.5-1-.9-1.6-1.2-.4-.3-.8-.4-1.3-.4-.4 0-.8 0-1.1.2-.5.2-.9.6-1.1 1-.2.3-.3.7-.3 1.1 0 .3 0 .6.1.9.1.5.4.9.8 1.2.5.4 1.1.5 1.7.5.5 0 1-.2 1.5-.5.6-.4 1.1-.8 1.6-1.3.1-.3.3-.5.5-.6zM13 12c.5.5 1 1 1.5 1.4.5.5 1.1.9 1.9 1 .4.1.8 0 1.2-.1.3-.1.6-.3.9-.5.4-.4.7-.9.8-1.4.1-.5 0-.9-.1-1.4-.3-.8-.8-1.2-1.7-1.4-.4-.1-.8-.1-1.2 0-.5.1-1 .4-1.4.7-.5.4-1 .8-1.4 1.2-.2.2-.4.3-.5.5z"/></svg>`;
96
-
97
- const convertIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg>`;
98
-
99
- const downloadIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M16.8 10.8 12 15.6l-4.8-4.8h3V3.6h3.6v7.2h3zM12 15.6H3v4.8h18v-4.8h-9zm7.8 2.4h-2.4v-1.2h2.4V18z"/></svg>`;
100
-
101
- const loopIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg>`;
102
-
103
- const nextIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m6.1 5.8 9.8 6.2-9.8 6.2V5.8zM16.4 5.8h1.5v12.4h-1.5z"/></svg>`;
104
-
105
- const pauseIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M14.016 5.016H18v13.969h-3.984V5.016zM6 18.984V5.015h3.984v13.969H6z"/></svg>`;
106
-
107
- const playIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M8.016 5.016L18.985 12 8.016 18.984V5.015z"/></svg>`;
108
-
109
- const prevIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.9 18.2 8.1 12l9.8-6.2v12.4zm-10.3 0H6.1V5.8h1.5v12.4z"/></svg>`;
110
-
111
- const settingsIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><circle cx="12" cy="5.4" r="2.5"/><circle cx="12" cy="12" r="2.5"/><circle cx="12" cy="18.6" r="2.5"/></svg>`;
112
-
113
- const stopIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M6 6h12v12H6V6z"/></svg>`;
114
-
115
76
  var ObjectFit = /*#__PURE__*/ function(ObjectFit) {
116
77
  ObjectFit["Contain"] = "contain";
117
78
  ObjectFit["Cover"] = "cover";
@@ -139,135 +100,6 @@ var PlayerState = /*#__PURE__*/ function(PlayerState) {
139
100
  return PlayerState;
140
101
  }({});
141
102
 
142
- function advancedButtons() {
143
- if (!this.shadow) {
144
- return;
145
- }
146
- const toggleLoop = this.shadow.querySelector('.toggleLoop');
147
- if (toggleLoop instanceof HTMLButtonElement) {
148
- toggleLoop.onclick = this.toggleLoop;
149
- }
150
- const toggleBoomerang = this.shadow.querySelector('.toggleBoomerang');
151
- if (toggleBoomerang instanceof HTMLButtonElement) {
152
- toggleBoomerang.onclick = this.toggleBoomerang;
153
- }
154
- const convertButton = this.shadow.querySelector('.convert');
155
- if (convertButton instanceof HTMLButtonElement) {
156
- convertButton.onclick = async ()=>{
157
- convertButton.disabled = true;
158
- try {
159
- const { convert } = await loadDotLottieTools();
160
- await convert({
161
- isDotLottie: this.isDotLottie,
162
- manifest: this.getManifest(),
163
- src: this.src || this.source
164
- });
165
- } finally{
166
- convertButton.disabled = false;
167
- }
168
- };
169
- }
170
- const snapshot = this.shadow.querySelector('.snapshot');
171
- if (snapshot instanceof HTMLButtonElement) {
172
- snapshot.onclick = ()=>this.snapshot(true);
173
- }
174
- const toggleSettings = this.shadow.querySelector('.toggleSettings');
175
- if (toggleSettings instanceof HTMLButtonElement) {
176
- toggleSettings.onclick = this._handleSettingsClick;
177
- toggleSettings.onblur = this._handleBlur;
178
- }
179
- }
180
- function handleButtons() {
181
- if (!this.shadow) {
182
- return;
183
- }
184
- const togglePlay = this.shadow.querySelector('.togglePlay');
185
- if (togglePlay instanceof HTMLButtonElement) {
186
- togglePlay.onclick = this.togglePlay;
187
- }
188
- const stopButton = this.shadow.querySelector('.stop');
189
- if (stopButton instanceof HTMLButtonElement) {
190
- stopButton.onclick = this.stop;
191
- }
192
- const prevButton = this.shadow.querySelector('.prev');
193
- if (prevButton instanceof HTMLButtonElement) {
194
- if (this.animations.length > 0 && this.currentAnimation) {
195
- prevButton.hidden = false;
196
- }
197
- prevButton.onclick = this.prev;
198
- }
199
- const nextButton = this.shadow.querySelector('.next');
200
- if (nextButton instanceof HTMLButtonElement) {
201
- if (this.animations.length > 0 && this.currentAnimation < this.animations.length - 1) {
202
- nextButton.hidden = false;
203
- }
204
- nextButton.onclick = this.next;
205
- }
206
- const seeker = this.shadow.querySelector('.seeker');
207
- if (seeker instanceof HTMLInputElement) {
208
- seeker.onchange = this._handleSeekChange;
209
- seeker.onmousedown = this._freeze;
210
- }
211
- if (this.simple) {
212
- return;
213
- }
214
- advancedButtons.call(this);
215
- }
216
- /**
217
- * Render Controls.
218
- */ function renderControls() {
219
- if (!this.shadow) {
220
- throw new Error('No Shadow Element');
221
- }
222
- const slot = this.shadow.querySelector('slot[name=controls]');
223
- if (!slot) {
224
- return;
225
- }
226
- if (!this.controls) {
227
- slot.innerHTML = '';
228
- return;
229
- }
230
- const convertLabel = this.isDotLottie ? 'dotLottie animation to JSON format' : 'JSON animation to dotLottie format', convertButtonText = this.isDotLottie ? 'Convert to JSON' : 'Convert to dotLottie';
231
- slot.innerHTML = /* HTML */ `<div class="lottie-controls toolbar ${this.playerState === PlayerState.Error ? 'has-error' : ''}" aria-label="Lottie Animation controls"><button class="togglePlay" data-active="${this.autoplay}" aria-label="Toggle Play/Pause">${playIcon}</button> <button class="stop" data-active="${!this.autoplay}" aria-label="Stop">${stopIcon}</button> <button class="prev" aria-label="Previous animation" hidden="false">${prevIcon}</button> <button class="next" aria-label="Next animation" hidden>${nextIcon}</button><form class="progress-container${this.simple ? ' simple' : ''}"><input type="range" class="seeker" min="0" max="100" step="1" value="${this._seeker.toString()}" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${this._seeker.toString()}" tabindex="0" aria-label="Slider for search"><progress max="100" value="${this._seeker}"></progress></form>${this.simple ? '' : /* HTML */ `<button class="toggleLoop" data-active="${this.loop}" tabindex="0" aria-label="Toggle loop">${loopIcon}</button> <button class="toggleBoomerang" data-active="${this.mode === PlayMode.Bounce}" aria-label="Toggle boomerang" tabindex="0">${boomerangIcon}</button> <button class="toggleSettings" aria-label="Settings" aria-haspopup="true" aria-expanded="${this._isSettingsOpen}" aria-controls="${this._identifier}-settings">${settingsIcon}</button><div id="${this._identifier}-settings" class="popover" hidden><button class="convert" aria-label="Convert ${convertLabel}" hidden>${convertIcon} ${convertButtonText}</button> <button class="snapshot" aria-label="Download still image">${downloadIcon} Download still image</button></div>`}</div>`;
232
- handleButtons.call(this);
233
- }
234
- function updatePlayPauseButton(togglePlay, state) {
235
- const isActive = state === PlayerState.Playing || state === PlayerState.Paused;
236
- togglePlay.dataset.active = isActive.toString();
237
- togglePlay.innerHTML = state === PlayerState.Playing ? pauseIcon : playIcon;
238
- }
239
-
240
- var controls = /*#__PURE__*/Object.freeze({
241
- __proto__: null,
242
- renderControls: renderControls,
243
- updatePlayPauseButton: updatePlayPauseButton
244
- });
245
-
246
- /**
247
- * Render Player.
248
- */ async function renderPlayer() {
249
- if (!this.shadow || !this.template) {
250
- throw new Error('No Shadow Element or Template');
251
- }
252
- let langAttribute = '';
253
- /**
254
- * If player has aria-description, it's safe to assume
255
- * language should be the same as `document.documentElement.lang`.
256
- * Accessible labels on controls are only in English, so with no
257
- * aria tag, and controls enabled, lang should reflect this.
258
- * With neither controls or description, the lang attribute is
259
- * not needed.
260
- */ if (this.description || this.controls) {
261
- const lang = this.description ? document.documentElement.lang : 'en';
262
- langAttribute = `lang="${lang}"`;
263
- }
264
- this.template.innerHTML = /* HTML */ `<div class="animation-container main" data-controls="${this.controls ?? false}" ${langAttribute}><figure class="animation" style="background:${this.background}" ${this.description ? `aria-label="${this.description}"` : ''}></figure><slot name="controls"></slot></div>`;
265
- this.shadow.adoptedStyleSheets = [
266
- await DotLottiePlayerBase.styles()
267
- ];
268
- this.shadow.appendChild(this.template.content.cloneNode(true));
269
- }
270
-
271
103
  /**
272
104
  * Get extension from filename, URL or path.
273
105
  */ const aspectRatio = (objectFit)=>{
@@ -334,42 +166,12 @@ var controls = /*#__PURE__*/Object.freeze({
334
166
 
335
167
  const tagName = 'dotlottie-player', hasReducedMotion = !isServer && matchMedia('(prefers-reduced-motion: reduce)').matches, hasIOSupport = !isServer && 'IntersectionObserver' in window, hasVTSupport = !isServer && 'ViewTimeline' in window;
336
168
 
337
- const notImplemented = 'Method is not implemented', getStyles = async ()=>{
169
+ const getStyles = async ()=>{
338
170
  const styleSheet = new CSSStyleSheet();
339
171
  await styleSheet.replace(css_248z);
340
172
  return styleSheet;
341
- };
342
- /**
343
- * DotLottie Player Web Component.
344
- */ class DotLottiePlayerBase extends PropertyCallbackElement {
345
- /**
346
- * Attributes to observe.
347
- */ static get observedAttributes() {
348
- return [
349
- 'animateOnScroll',
350
- 'autoplay',
351
- 'controls',
352
- 'direction',
353
- 'hover',
354
- 'loop',
355
- 'mode',
356
- 'playOnClick',
357
- 'playOnVisible',
358
- 'selector',
359
- 'speed',
360
- 'src',
361
- 'subframe'
362
- ];
363
- }
364
- static get observedProperties() {
365
- return [
366
- 'playerState',
367
- '_isSettingsOpen',
368
- '_seeker',
369
- '_currentAnimation',
370
- '_animations'
371
- ];
372
- }
173
+ }, notImplemented$1 = 'Method is not implemented';
174
+ class BaseElement extends PropertyCallbackElement {
373
175
  /**
374
176
  * Return the styles for the component.
375
177
  */ static get styles() {
@@ -492,9 +294,6 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
492
294
  }
493
295
  return 0;
494
296
  }
495
- get isDotLottie() {
496
- return this._isDotLottie;
497
- }
498
297
  /**
499
298
  * Loop animation.
500
299
  */ set loop(value) {
@@ -588,99 +387,463 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
588
387
  }
589
388
  return null;
590
389
  }
591
- /**
592
- * Whether to display error screen on load error.
593
- */ set quiet(value) {
594
- this.setAttribute('quiet', Boolean(value).toString());
390
+ /**
391
+ * Whether to display error screen on load error.
392
+ */ set quiet(value) {
393
+ this.setAttribute('quiet', Boolean(value).toString());
394
+ }
395
+ get quiet() {
396
+ const val = this.getAttribute('quiet');
397
+ return parseHTMLBooleans(val);
398
+ }
399
+ /**
400
+ * Renderer to use: svg, canvas or html.
401
+ */ set renderer(value) {
402
+ this.setAttribute('renderer', value);
403
+ }
404
+ get renderer() {
405
+ const val = this.getAttribute('renderer');
406
+ if (val === RendererType.Canvas || val === RendererType.HTML) {
407
+ return val;
408
+ }
409
+ return RendererType.SVG;
410
+ }
411
+ /**
412
+ * Play on clicked element by id attribute, other than animation.
413
+ */ set selector(value) {
414
+ if (value) {
415
+ this.setAttribute('selector', value);
416
+ return;
417
+ }
418
+ this.removeAttribute('selector');
419
+ }
420
+ get selector() {
421
+ return this.getAttribute('selector');
422
+ }
423
+ /**
424
+ * Hide advanced controls.
425
+ */ set simple(value) {
426
+ this.setAttribute('simple', Boolean(value).toString());
427
+ }
428
+ get simple() {
429
+ const val = this.getAttribute('simple');
430
+ return parseHTMLBooleans(val);
431
+ }
432
+ /**
433
+ * Speed.
434
+ */ set speed(value) {
435
+ this.setAttribute('speed', value.toString());
436
+ }
437
+ get speed() {
438
+ const val = this.getAttribute('speed');
439
+ if (val !== null && !isNaN(Number(val))) {
440
+ return Number(val);
441
+ }
442
+ return 1;
443
+ }
444
+ /**
445
+ * Source, either path or JSON string.
446
+ */ set src(value) {
447
+ this.setAttribute('src', value || '');
448
+ }
449
+ get src() {
450
+ return this.getAttribute('src');
451
+ }
452
+ /**
453
+ * Subframe.
454
+ */ set subframe(value) {
455
+ this.setAttribute('subframe', Boolean(value).toString());
456
+ }
457
+ get subframe() {
458
+ const val = this.getAttribute('subframe');
459
+ return parseHTMLBooleans(val);
460
+ }
461
+ clearDevLog() {
462
+ this._toLog = null;
463
+ }
464
+ devLog(toLog, type = 'error') {
465
+ {
466
+ return;
467
+ }
468
+ }
469
+ play() {
470
+ throw new Error(notImplemented$1);
471
+ }
472
+ _freeze() {
473
+ throw new Error(notImplemented$1);
474
+ }
475
+ constructor(...args){
476
+ super(...args), /**
477
+ * Player state.
478
+ */ this.playerState = PlayerState.Loading, /**
479
+ * This is included in watched properties,
480
+ * so that next-button will show up
481
+ * on load, if controls are visible.
482
+ */ this._animations = [], /**
483
+ * Animation Container.
484
+ */ this._container = null, /**
485
+ * Which animation to show, if several.
486
+ */ this._currentAnimation = 0, this._errorMessage = 'Something went wrong', this._identifier = this.id || createElementID(), this._lottieInstance = null, this._playerState = {
487
+ count: 0,
488
+ frame: 0,
489
+ loaded: false,
490
+ playTimeout: null,
491
+ prev: PlayerState.Loading
492
+ }, this._toLog = null;
493
+ }
494
+ }
495
+
496
+ class ScrollElement extends BaseElement {
497
+ applyScrollProgress() {
498
+ if (!this.animateOnScroll || !this._lottieInstance) {
499
+ return;
500
+ }
501
+ const progress = this._getScrollProgress();
502
+ if (progress === null) {
503
+ return;
504
+ }
505
+ const { totalFrames } = this._lottieInstance, frame = progress * (totalFrames - 1), epsilon = this.subframe ? 0.1 : 0.5;
506
+ if (Math.abs(frame - this._playerState.frame) < epsilon) {
507
+ return;
508
+ }
509
+ this._playerState.frame = frame;
510
+ this._lottieInstance.goToAndStop(frame, true);
511
+ }
512
+ cancelIntersectionObserver() {
513
+ // Remove intersection observer for detecting component being out-of-view
514
+ this._intersectionObserver?.disconnect();
515
+ this._intersectionObserver = undefined;
516
+ }
517
+ cancelScrollProbe() {
518
+ this._scrollProbe?.cancel();
519
+ this._scrollProbe = undefined;
520
+ }
521
+ scrollLoop() {
522
+ this._playerState.scrollLoopId = requestAnimationFrame(this.scrollLoop);
523
+ this.applyScrollProgress();
524
+ }
525
+ startScrollLoop() {
526
+ if (isServer) {
527
+ this.devLog('DotLottie: Scroll animations will not work in a Server Side Rendering context. Try to wrap this in a client component.');
528
+ return;
529
+ }
530
+ this._playerState.scrollLoopId ??= requestAnimationFrame(this.scrollLoop);
531
+ }
532
+ stopScrollLoop() {
533
+ if (this._playerState.scrollLoopId === undefined) {
534
+ return;
535
+ }
536
+ cancelAnimationFrame(this._playerState.scrollLoopId);
537
+ this._playerState.scrollLoopId = undefined;
538
+ this.applyScrollProgress();
539
+ }
540
+ /**
541
+ * Add IntersectionObserver.
542
+ */ _addIntersectionObserver() {
543
+ if (!this._container || this._intersectionObserver || !hasIOSupport) {
544
+ return;
545
+ }
546
+ this._intersectionObserver = new IntersectionObserver(([{ isIntersecting }])=>{
547
+ this.isIntersecting = isIntersecting;
548
+ // Prevent animate on scroll for users with who prefers reduces motion.
549
+ if (this.animateOnScroll && !hasReducedMotion) {
550
+ if (isIntersecting) {
551
+ this.startScrollLoop();
552
+ } else {
553
+ this.stopScrollLoop();
554
+ }
555
+ return;
556
+ }
557
+ if (!isIntersecting || document.hidden) {
558
+ if (this.playerState === PlayerState.Playing) {
559
+ this._freeze();
560
+ }
561
+ return;
562
+ }
563
+ if (!this.playOnVisible && this.playerState === PlayerState.Frozen && !(this.autoplay && hasReducedMotion)) {
564
+ this.play();
565
+ }
566
+ if (!this.playOnVisible || hasReducedMotion) {
567
+ return;
568
+ }
569
+ if (this.playerState === PlayerState.Completed && !this.once) {
570
+ this.playerState = PlayerState.Playing;
571
+ this._lottieInstance?.goToAndPlay(this.direction === 1 ? 0 : this._lottieInstance.totalFrames);
572
+ return;
573
+ }
574
+ this._playerState.playTimeout = setTimeout(()=>{
575
+ this.play();
576
+ }, this.delay);
577
+ if (this.playerState === PlayerState.Playing) {
578
+ clearTimeout(this._playerState.playTimeout);
579
+ }
580
+ });
581
+ this._observeIntersectionTarget();
582
+ }
583
+ _canDriveTimeline({ source }) {
584
+ if (!source || source === document.scrollingElement) {
585
+ return true;
586
+ }
587
+ return getComputedStyle(source).overflowY !== 'hidden' && source.scrollHeight > source.clientHeight;
588
+ }
589
+ _getGeometricScrollProgress() {
590
+ if (!this._container) {
591
+ return null;
592
+ }
593
+ const { height, top } = this._container.getBoundingClientRect(), viewport = visualViewport?.height ?? innerHeight;
594
+ return clamp((viewport - top) / (viewport + height), 0, 1);
595
+ }
596
+ _getScrollProgress() {
597
+ if (!this._container) {
598
+ return null;
599
+ }
600
+ if (!hasVTSupport) {
601
+ return this._getGeometricScrollProgress();
602
+ }
603
+ const timeline = new ViewTimeline({
604
+ axis: 'block',
605
+ subject: this._container
606
+ }), canDriveTimeline = this._canDriveTimeline(timeline);
607
+ if (!canDriveTimeline) {
608
+ this.devLog('[dotlottie-player] animateOnScroll: the player\'s nearest scrolling ancestor cannot ' + 'scroll, so its ViewTimeline would never advance. This is almost always an ancestor ' + 'with `overflow: hidden` — use `overflow: clip` instead. Falling back to viewport ' + 'measurement.');
609
+ return this._getGeometricScrollProgress();
610
+ }
611
+ this._scrollProbe ??= this._container.animate({
612
+ '--dotlottie-scroll': [
613
+ 0,
614
+ 1
615
+ ]
616
+ }, {
617
+ fill: 'both',
618
+ rangeEnd: 'cover 100%',
619
+ rangeStart: 'cover 0%',
620
+ timeline
621
+ });
622
+ return this._scrollProbe.effect?.getComputedTiming().progress ?? null;
623
+ }
624
+ _observeIntersectionTarget() {
625
+ if (!this._intersectionObserver || !this._container) {
626
+ return;
627
+ }
628
+ this._intersectionObserver.disconnect();
629
+ // Observing the host is more reliable than inner shadow nodes when ancestors
630
+ // use overflow-* (IO / layout interaction quirks).
631
+ this._intersectionObserver.observe(this._container);
632
+ }
633
+ constructor(...args){
634
+ super(...args), this.isIntersecting = false;
635
+ }
636
+ }
637
+
638
+ let dotlottieTools = null, loadPromise = null;
639
+ function loadDotLottieTools() {
640
+ if (dotlottieTools) {
641
+ return Promise.resolve(dotlottieTools);
642
+ }
643
+ loadPromise = loadPromise ?? (async ()=>{
644
+ const { addAnimation, convert } = await import('@aarsteinmedia/lottie-web/dotlottie');
645
+ dotlottieTools = {
646
+ addAnimation,
647
+ convert
648
+ };
649
+ return {
650
+ addAnimation,
651
+ convert
652
+ };
653
+ })();
654
+ return loadPromise;
655
+ }
656
+
657
+ const boomerangIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m11.8 13.2-.3.3c-.5.5-1.1 1.1-1.7 1.5-.5.4-1 .6-1.5.8-.5.2-1.1.3-1.6.3s-1-.1-1.5-.3c-.6-.2-1-.5-1.4-1-.5-.6-.8-1.2-.9-1.9-.2-.9-.1-1.8.3-2.6.3-.7.8-1.2 1.3-1.6.3-.2.6-.4 1-.5.2-.2.5-.2.8-.3.3 0 .7-.1 1 0 .3 0 .6.1.9.2.9.3 1.7.9 2.4 1.5.4.4.8.7 1.1 1.1l.1.1.4-.4c.6-.6 1.2-1.2 1.9-1.6.5-.3 1-.6 1.5-.7.4-.1.7-.2 1-.2h.9c1 .1 1.9.5 2.6 1.4.4.5.7 1.1.8 1.8.2.9.1 1.7-.2 2.5-.4.9-1 1.5-1.8 2-.4.2-.7.4-1.1.4-.4.1-.8.1-1.2.1-.5 0-.9-.1-1.3-.3-.8-.3-1.5-.9-2.1-1.5-.4-.4-.8-.7-1.1-1.1h-.3zm-1.1-1.1c-.1-.1-.1-.1 0 0-.3-.3-.6-.6-.8-.9-.5-.5-1-.9-1.6-1.2-.4-.3-.8-.4-1.3-.4-.4 0-.8 0-1.1.2-.5.2-.9.6-1.1 1-.2.3-.3.7-.3 1.1 0 .3 0 .6.1.9.1.5.4.9.8 1.2.5.4 1.1.5 1.7.5.5 0 1-.2 1.5-.5.6-.4 1.1-.8 1.6-1.3.1-.3.3-.5.5-.6zM13 12c.5.5 1 1 1.5 1.4.5.5 1.1.9 1.9 1 .4.1.8 0 1.2-.1.3-.1.6-.3.9-.5.4-.4.7-.9.8-1.4.1-.5 0-.9-.1-1.4-.3-.8-.8-1.2-1.7-1.4-.4-.1-.8-.1-1.2 0-.5.1-1 .4-1.4.7-.5.4-1 .8-1.4 1.2-.2.2-.4.3-.5.5z"/></svg>`;
658
+
659
+ const convertIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg>`;
660
+
661
+ const downloadIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M16.8 10.8 12 15.6l-4.8-4.8h3V3.6h3.6v7.2h3zM12 15.6H3v4.8h18v-4.8h-9zm7.8 2.4h-2.4v-1.2h2.4V18z"/></svg>`;
662
+
663
+ const loopIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg>`;
664
+
665
+ const nextIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m6.1 5.8 9.8 6.2-9.8 6.2V5.8zM16.4 5.8h1.5v12.4h-1.5z"/></svg>`;
666
+
667
+ const pauseIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M14.016 5.016H18v13.969h-3.984V5.016zM6 18.984V5.015h3.984v13.969H6z"/></svg>`;
668
+
669
+ const playIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M8.016 5.016L18.985 12 8.016 18.984V5.015z"/></svg>`;
670
+
671
+ const prevIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.9 18.2 8.1 12l9.8-6.2v12.4zm-10.3 0H6.1V5.8h1.5v12.4z"/></svg>`;
672
+
673
+ const settingsIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><circle cx="12" cy="5.4" r="2.5"/><circle cx="12" cy="12" r="2.5"/><circle cx="12" cy="18.6" r="2.5"/></svg>`;
674
+
675
+ const stopIcon = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M6 6h12v12H6V6z"/></svg>`;
676
+
677
+ function advancedButtons() {
678
+ if (!this.shadow) {
679
+ return;
680
+ }
681
+ const toggleLoop = this.shadow.querySelector('.toggleLoop');
682
+ if (toggleLoop instanceof HTMLButtonElement) {
683
+ toggleLoop.onclick = this.toggleLoop;
684
+ }
685
+ const toggleBoomerang = this.shadow.querySelector('.toggleBoomerang');
686
+ if (toggleBoomerang instanceof HTMLButtonElement) {
687
+ toggleBoomerang.onclick = this.toggleBoomerang;
688
+ }
689
+ const convertButton = this.shadow.querySelector('.convert');
690
+ if (convertButton instanceof HTMLButtonElement) {
691
+ convertButton.onclick = async ()=>{
692
+ convertButton.disabled = true;
693
+ try {
694
+ const { convert } = await loadDotLottieTools();
695
+ await convert({
696
+ isDotLottie: this.isDotLottie,
697
+ manifest: this.getManifest(),
698
+ src: this.src || this.source
699
+ });
700
+ } finally{
701
+ convertButton.disabled = false;
702
+ }
703
+ };
704
+ }
705
+ const snapshot = this.shadow.querySelector('.snapshot');
706
+ if (snapshot instanceof HTMLButtonElement) {
707
+ snapshot.onclick = ()=>this.snapshot(true);
708
+ }
709
+ const toggleSettings = this.shadow.querySelector('.toggleSettings');
710
+ if (toggleSettings instanceof HTMLButtonElement) {
711
+ toggleSettings.onclick = this._handleSettingsClick;
712
+ toggleSettings.onblur = this._handleBlur;
713
+ }
714
+ }
715
+ function handleButtons() {
716
+ if (!this.shadow) {
717
+ return;
595
718
  }
596
- get quiet() {
597
- const val = this.getAttribute('quiet');
598
- return parseHTMLBooleans(val);
719
+ const togglePlay = this.shadow.querySelector('.togglePlay');
720
+ if (togglePlay instanceof HTMLButtonElement) {
721
+ togglePlay.onclick = this.togglePlay;
599
722
  }
600
- /**
601
- * Renderer to use: svg, canvas or html.
602
- */ set renderer(value) {
603
- this.setAttribute('renderer', value);
723
+ const stopButton = this.shadow.querySelector('.stop');
724
+ if (stopButton instanceof HTMLButtonElement) {
725
+ stopButton.onclick = this.stop;
604
726
  }
605
- get renderer() {
606
- const val = this.getAttribute('renderer');
607
- if (val === RendererType.Canvas || val === RendererType.HTML) {
608
- return val;
727
+ const prevButton = this.shadow.querySelector('.prev');
728
+ if (prevButton instanceof HTMLButtonElement) {
729
+ if (this.animations.length > 0 && this.currentAnimation) {
730
+ prevButton.hidden = false;
609
731
  }
610
- return RendererType.SVG;
732
+ prevButton.onclick = this.prev;
611
733
  }
612
- /**
613
- * Play on clicked element by id attribute, other than animation.
614
- */ set selector(value) {
615
- if (value) {
616
- this.setAttribute('selector', value);
617
- return;
734
+ const nextButton = this.shadow.querySelector('.next');
735
+ if (nextButton instanceof HTMLButtonElement) {
736
+ if (this.animations.length > 0 && this.currentAnimation < this.animations.length - 1) {
737
+ nextButton.hidden = false;
618
738
  }
619
- this.removeAttribute('selector');
739
+ nextButton.onclick = this.next;
620
740
  }
621
- get selector() {
622
- return this.getAttribute('selector');
741
+ const seeker = this.shadow.querySelector('.seeker');
742
+ if (seeker instanceof HTMLInputElement) {
743
+ seeker.onchange = this._handleSeekChange;
744
+ seeker.onmousedown = this._freeze;
623
745
  }
624
- /**
625
- * Hide advanced controls.
626
- */ set simple(value) {
627
- this.setAttribute('simple', Boolean(value).toString());
746
+ if (this.simple) {
747
+ return;
628
748
  }
629
- get simple() {
630
- const val = this.getAttribute('simple');
631
- return parseHTMLBooleans(val);
749
+ advancedButtons.call(this);
750
+ }
751
+ /**
752
+ * Render Controls.
753
+ */ function renderControls() {
754
+ if (!this.shadow) {
755
+ throw new Error('No Shadow Element');
632
756
  }
633
- /**
634
- * Speed.
635
- */ set speed(value) {
636
- this.setAttribute('speed', value.toString());
757
+ const slot = this.shadow.querySelector('slot[name=controls]');
758
+ if (!slot) {
759
+ return;
637
760
  }
638
- get speed() {
639
- const val = this.getAttribute('speed');
640
- if (val !== null && !isNaN(Number(val))) {
641
- return Number(val);
642
- }
643
- return 1;
761
+ if (!this.controls) {
762
+ slot.innerHTML = '';
763
+ return;
644
764
  }
645
- /**
646
- * Source, either path or JSON string.
647
- */ set src(value) {
648
- this.setAttribute('src', value || '');
765
+ const convertLabel = this.isDotLottie ? 'dotLottie animation to JSON format' : 'JSON animation to dotLottie format', convertButtonText = this.isDotLottie ? 'Convert to JSON' : 'Convert to dotLottie';
766
+ slot.innerHTML = /* HTML */ `<div class="lottie-controls toolbar ${this.playerState === PlayerState.Error ? 'has-error' : ''}" aria-label="Lottie Animation controls"><button class="togglePlay" data-active="${this.autoplay}" aria-label="Toggle Play/Pause">${playIcon}</button> <button class="stop" data-active="${!this.autoplay}" aria-label="Stop">${stopIcon}</button> <button class="prev" aria-label="Previous animation" hidden="false">${prevIcon}</button> <button class="next" aria-label="Next animation" hidden>${nextIcon}</button><form class="progress-container${this.simple ? ' simple' : ''}"><input type="range" class="seeker" min="0" max="100" step="1" value="${this._seeker.toString()}" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${this._seeker.toString()}" tabindex="0" aria-label="Slider for search"><progress max="100" value="${this._seeker}"></progress></form>${this.simple ? '' : /* HTML */ `<button class="toggleLoop" data-active="${this.loop}" tabindex="0" aria-label="Toggle loop">${loopIcon}</button> <button class="toggleBoomerang" data-active="${this.mode === PlayMode.Bounce}" aria-label="Toggle boomerang" tabindex="0">${boomerangIcon}</button> <button class="toggleSettings" aria-label="Settings" aria-haspopup="true" aria-expanded="${this._isSettingsOpen}" aria-controls="${this._identifier}-settings">${settingsIcon}</button><div id="${this._identifier}-settings" class="popover" hidden><button class="convert" aria-label="Convert ${convertLabel}" hidden>${convertIcon} ${convertButtonText}</button> <button class="snapshot" aria-label="Download still image">${downloadIcon} Download still image</button></div>`}</div>`;
767
+ handleButtons.call(this);
768
+ }
769
+ function updatePlayPauseButton(togglePlay, state) {
770
+ const isActive = state === PlayerState.Playing || state === PlayerState.Paused;
771
+ togglePlay.dataset.active = isActive.toString();
772
+ togglePlay.innerHTML = state === PlayerState.Playing ? pauseIcon : playIcon;
773
+ }
774
+
775
+ var controls = /*#__PURE__*/Object.freeze({
776
+ __proto__: null,
777
+ renderControls: renderControls,
778
+ updatePlayPauseButton: updatePlayPauseButton
779
+ });
780
+
781
+ /**
782
+ * Render Player.
783
+ */ async function renderPlayer() {
784
+ if (!this.shadow || !this.template) {
785
+ throw new Error('No Shadow Element or Template');
649
786
  }
650
- get src() {
651
- return this.getAttribute('src');
787
+ let langAttribute = '';
788
+ /**
789
+ * If player has aria-description, it's safe to assume
790
+ * language should be the same as `document.documentElement.lang`.
791
+ * Accessible labels on controls are only in English, so with no
792
+ * aria tag, and controls enabled, lang should reflect this.
793
+ * With neither controls or description, the lang attribute is
794
+ * not needed.
795
+ */ if (this.description || this.controls) {
796
+ const lang = this.description ? document.documentElement.lang : 'en';
797
+ langAttribute = `lang="${lang}"`;
652
798
  }
799
+ this.template.innerHTML = /* HTML */ `<div class="animation-container main" data-controls="${this.controls ?? false}" ${langAttribute}><figure class="animation" style="background:${this.background}" ${this.description ? `aria-label="${this.description}"` : ''}></figure><slot name="controls"></slot></div>`;
800
+ this.shadow.adoptedStyleSheets = [
801
+ await DotLottiePlayerBase.styles()
802
+ ];
803
+ this.shadow.appendChild(this.template.content.cloneNode(true));
804
+ }
805
+
806
+ const notImplemented = 'Method is not implemented';
807
+ /**
808
+ * DotLottie Player Web Component.
809
+ */ class DotLottiePlayerBase extends ScrollElement {
653
810
  /**
654
- * Subframe.
655
- */ set subframe(value) {
656
- this.setAttribute('subframe', Boolean(value).toString());
811
+ * Attributes to observe.
812
+ */ static get observedAttributes() {
813
+ return [
814
+ 'animateOnScroll',
815
+ 'autoplay',
816
+ 'controls',
817
+ 'direction',
818
+ 'hover',
819
+ 'loop',
820
+ 'mode',
821
+ 'playOnClick',
822
+ 'playOnVisible',
823
+ 'selector',
824
+ 'speed',
825
+ 'src',
826
+ 'subframe'
827
+ ];
657
828
  }
658
- get subframe() {
659
- const val = this.getAttribute('subframe');
660
- return parseHTMLBooleans(val);
829
+ static get observedProperties() {
830
+ return [
831
+ 'playerState',
832
+ '_isSettingsOpen',
833
+ '_seeker',
834
+ '_currentAnimation',
835
+ '_animations'
836
+ ];
837
+ }
838
+ get isDotLottie() {
839
+ return this._isDotLottie;
661
840
  }
662
841
  constructor(){
663
842
  super(), this.isLight = false, /**
664
- * Player state.
665
- */ this.playerState = PlayerState.Loading, /**
666
- * Animation Container.
667
- */ this._container = null, this._errorMessage = 'Something went wrong', this._identifier = this.id || createElementID(), /**
668
843
  * Whether settings toolbar is open.
669
- */ this._isSettingsOpen = false, this._playerState = {
670
- count: 0,
671
- frame: 0,
672
- loaded: false,
673
- playTimeout: null,
674
- prev: PlayerState.Loading
675
- }, this._render = renderPlayer, /**
844
+ */ this._isSettingsOpen = false, this._render = renderPlayer, /**
676
845
  * Seeker.
677
- */ this._seeker = 0, /**
678
- * This is included in watched properties,
679
- * so that next-button will show up
680
- * on load, if controls are visible.
681
- */ this._animations = [], this._controlsLoadId = 0, /**
682
- * Which animation to show, if several.
683
- */ this._currentAnimation = 0, this._errorLoadId = 0, this._isBounce = false, this._isDotLottie = false, this._isIntersecting = false, this._lottieInstance = null, /**
846
+ */ this._seeker = 0, this._controlsLoadId = 0, this._errorLoadId = 0, this._isBounce = false, this._isDotLottie = false, /**
684
847
  * Multi-animation settings.
685
848
  */ this._multiAnimationSettings = [], this._renderControls = async ()=>{
686
849
  const slot = this.shadow?.querySelector('slot[name=controls]');
@@ -711,7 +874,8 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
711
874
  this._mouseEnter = this._mouseEnter.bind(this);
712
875
  this._mouseLeave = this._mouseLeave.bind(this);
713
876
  this._onVisibilityChange = this._onVisibilityChange.bind(this);
714
- this._scrollLoop = this._scrollLoop.bind(this);
877
+ this.scrollLoop = this.scrollLoop.bind(this);
878
+ // this.startScrollLoop = this.startScrollLoop.bind(this)
715
879
  this._switchInstance = this._switchInstance.bind(this);
716
880
  this._handleSettingsClick = this._handleSettingsClick.bind(this);
717
881
  this.togglePlay = this.togglePlay.bind(this);
@@ -737,17 +901,18 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
737
901
  if (!this._lottieInstance || !this.shadow || !this._container) {
738
902
  return;
739
903
  }
904
+ this.clearDevLog();
740
905
  switch(name){
741
906
  case 'animateOnScroll':
742
907
  {
743
908
  if (value === '' || Boolean(value)) {
744
909
  this._lottieInstance.autoplay = false;
745
- if (this._isIntersecting) {
746
- this._startScrollLoop();
910
+ if (this.isIntersecting) {
911
+ this.startScrollLoop();
747
912
  }
748
913
  return;
749
914
  }
750
- this._stopScrollLoop();
915
+ this.stopScrollLoop();
751
916
  break;
752
917
  }
753
918
  case 'autoplay':
@@ -885,11 +1050,11 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
885
1050
  if (!this._lottieInstance?.destroy) {
886
1051
  return;
887
1052
  }
888
- this._cancelScrollProbe();
1053
+ this.cancelScrollProbe();
889
1054
  this.playerState = PlayerState.Destroyed;
890
1055
  this._lottieInstance.destroy();
891
1056
  this._lottieInstance = null;
892
- this._stopScrollLoop();
1057
+ this.stopScrollLoop();
893
1058
  this.dispatchEvent(new CustomEvent(PlayerEvent.Destroyed));
894
1059
  this.remove();
895
1060
  document.removeEventListener('visibilitychange', this._onVisibilityChange);
@@ -897,10 +1062,8 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
897
1062
  /**
898
1063
  * Cleanup on component destroy.
899
1064
  */ disconnectedCallback() {
900
- // Remove intersection observer for detecting component being out-of-view
901
- this._intersectionObserver?.disconnect();
902
- this._intersectionObserver = undefined;
903
- this._cancelScrollProbe();
1065
+ this.cancelIntersectionObserver();
1066
+ this.cancelScrollProbe();
904
1067
  if (this._playerState.scrollLoopId) {
905
1068
  cancelAnimationFrame(this._playerState.scrollLoopId);
906
1069
  }
@@ -911,6 +1074,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
911
1074
  document.removeEventListener('visibilitychange', this._onVisibilityChange);
912
1075
  // Destroy the animation instance
913
1076
  this.destroy();
1077
+ this.clearDevLog();
914
1078
  }
915
1079
  /**
916
1080
  * Returns the lottie-web instance used in the component.
@@ -990,7 +1154,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
990
1154
  this._lottieInstance.setSubframe(Boolean(this.subframe));
991
1155
  // Set progress on load if AnimateOnScroll
992
1156
  if (this.animateOnScroll) {
993
- this._applyScrollProgress();
1157
+ this.applyScrollProgress();
994
1158
  }
995
1159
  // Start playing if autoplay is enabled
996
1160
  if ((this.autoplay || this.animateOnScroll || this.playOnVisible) && this.direction === -1) {
@@ -1023,7 +1187,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1023
1187
  this.dispatchEvent(new CustomEvent(PlayerEvent.Pause));
1024
1188
  } catch (error) {
1025
1189
  hasError = true;
1026
- console.error(error);
1190
+ this.devLog(error);
1027
1191
  } finally{
1028
1192
  this.playerState = hasError ? PlayerState.Error : PlayerState.Paused;
1029
1193
  }
@@ -1041,7 +1205,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1041
1205
  this.dispatchEvent(new CustomEvent(PlayerEvent.Play));
1042
1206
  } catch (error) {
1043
1207
  hasError = true;
1044
- console.error(error);
1208
+ this.devLog(error);
1045
1209
  } finally{
1046
1210
  this.playerState = hasError ? PlayerState.Error : PlayerState.Playing;
1047
1211
  }
@@ -1197,7 +1361,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1197
1361
  }
1198
1362
  return data;
1199
1363
  } catch (error) {
1200
- console.error(error);
1364
+ this.devLog(error);
1201
1365
  return null;
1202
1366
  }
1203
1367
  }
@@ -1350,68 +1514,6 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1350
1514
  */ _addEventListeners() {
1351
1515
  this._toggleEventListeners('add');
1352
1516
  }
1353
- /**
1354
- * Add IntersectionObserver.
1355
- */ _addIntersectionObserver() {
1356
- if (!this._container || this._intersectionObserver || !hasIOSupport) {
1357
- return;
1358
- }
1359
- this._intersectionObserver = new IntersectionObserver(([{ isIntersecting }])=>{
1360
- this._isIntersecting = isIntersecting;
1361
- // Prevent animate on scroll for users with who prefers reduces motion.
1362
- if (this.animateOnScroll && !hasReducedMotion) {
1363
- if (isIntersecting) {
1364
- this._startScrollLoop();
1365
- } else {
1366
- this._stopScrollLoop();
1367
- }
1368
- return;
1369
- }
1370
- if (!isIntersecting || document.hidden) {
1371
- if (this.playerState === PlayerState.Playing) {
1372
- this._freeze();
1373
- }
1374
- return;
1375
- }
1376
- if (!this.playOnVisible && this.playerState === PlayerState.Frozen && !(this.autoplay && hasReducedMotion)) {
1377
- this.play();
1378
- }
1379
- if (!this.playOnVisible || hasReducedMotion) {
1380
- return;
1381
- }
1382
- if (this.playerState === PlayerState.Completed && !this.once) {
1383
- this.playerState = PlayerState.Playing;
1384
- this._lottieInstance?.goToAndPlay(this.direction === 1 ? 0 : this._lottieInstance.totalFrames);
1385
- return;
1386
- }
1387
- this._playerState.playTimeout = setTimeout(()=>{
1388
- this.play();
1389
- }, this.delay);
1390
- if (this.playerState === PlayerState.Playing) {
1391
- clearTimeout(this._playerState.playTimeout);
1392
- }
1393
- });
1394
- this._intersectionObserver.observe(this._container);
1395
- }
1396
- _applyScrollProgress() {
1397
- if (!this.animateOnScroll || !this._lottieInstance) {
1398
- return;
1399
- }
1400
- const progress = this._getScrollProgress();
1401
- if (progress === null) {
1402
- return;
1403
- }
1404
- const { totalFrames } = this._lottieInstance, frame = progress * (totalFrames - 1), epsilon = this.subframe ? 0.1 : 0.5;
1405
- if (Math.abs(frame - this._playerState.frame) < epsilon) {
1406
- return;
1407
- }
1408
- this._playerState.frame = frame;
1409
- this._lottieInstance.goToAndStop(frame, true);
1410
- }
1411
- _cancelScrollProbe() {
1412
- this._scrollProbe?.cancel();
1413
- this._scrollProbe = undefined;
1414
- }
1415
1517
  _clearPrevious() {
1416
1518
  this._lottieInstance?.destroy();
1417
1519
  const figure = this.shadow?.querySelector('.animation');
@@ -1514,30 +1616,6 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1514
1616
  rendererType: this.renderer
1515
1617
  });
1516
1618
  }
1517
- _getScrollProgress() {
1518
- if (!this._container) {
1519
- return null;
1520
- }
1521
- if (hasVTSupport) {
1522
- this._scrollProbe ??= this._container.animate({
1523
- '--dotlottie-scroll': [
1524
- 0,
1525
- 1
1526
- ]
1527
- }, {
1528
- fill: 'both',
1529
- rangeEnd: 'cover 100%',
1530
- rangeStart: 'cover 0%',
1531
- timeline: new ViewTimeline({
1532
- axis: 'block',
1533
- subject: this._container
1534
- })
1535
- });
1536
- return this._scrollProbe.effect?.getComputedTiming().progress ?? null;
1537
- }
1538
- const { height, top } = this._container.getBoundingClientRect(), viewport = visualViewport?.height ?? innerHeight;
1539
- return clamp((viewport - top) / (viewport + height), 0, 1);
1540
- }
1541
1619
  async _handleError(error) {
1542
1620
  this.playerState = PlayerState.Error;
1543
1621
  if (!this.quiet) {
@@ -1545,7 +1623,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1545
1623
  await this._showError();
1546
1624
  }
1547
1625
  this.dispatchEvent(new CustomEvent(PlayerEvent.Error));
1548
- console.error(error);
1626
+ this.devLog(error);
1549
1627
  }
1550
1628
  _handleLdScript() {
1551
1629
  const ldScript = this.parentElement?.querySelector('script[type="application/ld+json"]');
@@ -1691,25 +1769,6 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1691
1769
  */ _removeEventListeners() {
1692
1770
  this._toggleEventListeners('remove');
1693
1771
  }
1694
- _scrollLoop() {
1695
- this._playerState.scrollLoopId = requestAnimationFrame(this._scrollLoop);
1696
- this._applyScrollProgress();
1697
- }
1698
- _startScrollLoop() {
1699
- if (isServer) {
1700
- console.warn('DotLottie: Scroll animations will not work in a Server Side Rendering context. Try to wrap this in a client component.');
1701
- return;
1702
- }
1703
- this._playerState.scrollLoopId ??= requestAnimationFrame(this._scrollLoop);
1704
- }
1705
- _stopScrollLoop() {
1706
- if (this._playerState.scrollLoopId === undefined) {
1707
- return;
1708
- }
1709
- cancelAnimationFrame(this._playerState.scrollLoopId);
1710
- this._playerState.scrollLoopId = undefined;
1711
- this._applyScrollProgress();
1712
- }
1713
1772
  _switchInstance(isPrevious = false) {
1714
1773
  // Bail early if there is not animation to play
1715
1774
  if (!this._animations[this._currentAnimation]) {
@@ -1718,7 +1777,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1718
1777
  try {
1719
1778
  // Clear previous animation
1720
1779
  this._lottieInstance?.destroy();
1721
- this._cancelScrollProbe();
1780
+ this.cancelScrollProbe();
1722
1781
  // Re-initialize lottie player
1723
1782
  this._lottieInstance = this.loadAnimation({
1724
1783
  ...this._getOptions(),
@@ -1735,7 +1794,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1735
1794
  if (this._multiAnimationSettings[this._currentAnimation]?.autoplay ?? this.autoplay) {
1736
1795
  if (this.animateOnScroll) {
1737
1796
  // Get instant scroll position
1738
- this._applyScrollProgress();
1797
+ this.applyScrollProgress();
1739
1798
  this.playerState = PlayerState.Paused;
1740
1799
  return;
1741
1800
  }