@aarsteinmedia/dotlottie-player 6.4.10 → 6.5.0

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.
@@ -71,7 +71,7 @@ if (isServer) {
71
71
  }
72
72
  }
73
73
 
74
- var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-toolbar-height: 35px;\n --lottie-player-toolbar-background-color: #fff;\n --lottie-player-toolbar-icon-color: #000;\n --lottie-player-toolbar-icon-hover-color: #000;\n --lottie-player-toolbar-icon-active-color: #4285f4;\n --lottie-player-seeker-track-color: rgb(0 0 0 / 20%);\n --lottie-player-seeker-thumb-color: #4285f4;\n --lottie-player-seeker-display: block;\n\n width: 100%;\n height: 100%;\n\n &:not([hidden]) {\n display: block;\n }\n\n .main {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n margin: 0;\n padding: 0;\n }\n\n .animation {\n width: 100%;\n height: 100%;\n display: flex;\n margin: 0;\n padding: 0;\n }\n\n [data-controls='true'] .animation {\n height: calc(100% - 35px);\n }\n\n .animation-container {\n position: relative;\n }\n\n .popover {\n position: absolute;\n right: 5px;\n bottom: 40px;\n background-color: var(--lottie-player-toolbar-background-color);\n border-radius: 5px;\n padding: 10px 15px;\n border: solid 2px var(--lottie-player-toolbar-icon-color);\n animation: fade-in 0.2s ease-in-out;\n\n &::before {\n content: '';\n right: 10px;\n border: 7px solid transparent;\n margin-right: -7px;\n height: 0;\n width: 0;\n position: absolute;\n pointer-events: none;\n top: 100%;\n border-top-color: var(--lottie-player-toolbar-icon-color);\n }\n }\n\n .error {\n display: flex;\n margin: auto;\n justify-content: center;\n height: 100%;\n align-items: center;\n\n & svg {\n width: 100%;\n height: auto;\n }\n }\n\n .toolbar {\n display: flex;\n place-items: center center;\n background: var(--lottie-player-toolbar-background-color);\n margin: 0;\n height: 35px;\n padding: 5px;\n border-radius: 5px;\n gap: 5px;\n\n &.has-error {\n pointer-events: none;\n opacity: 0.5;\n }\n\n & button {\n cursor: pointer;\n fill: var(--lottie-player-toolbar-icon-color);\n color: var(--lottie-player-toolbar-icon-color);\n background: none;\n border: 0;\n padding: 0;\n outline: 0;\n height: 100%;\n margin: 0;\n align-items: center;\n gap: 5px;\n opacity: 0.9;\n\n &:not([hidden]) {\n display: flex;\n }\n\n &:hover {\n opacity: 1;\n }\n\n &[data-active='true'] {\n opacity: 1;\n fill: var(--lottie-player-toolbar-icon-active-color);\n }\n\n &:disabled {\n opacity: 0.5;\n }\n\n &:focus {\n outline: 0;\n }\n\n & svg {\n pointer-events: none;\n\n & > * {\n fill: inherit;\n }\n }\n\n &.disabled svg {\n display: none;\n }\n }\n }\n\n .progress-container {\n position: relative;\n width: 100%;\n\n &.simple {\n margin-right: 12px;\n }\n }\n\n & progress {\n appearance: none;\n outline: none;\n position: absolute;\n width: 100%;\n height: 5px;\n border-radius: 3px;\n border: 0;\n top: 0;\n left: 0;\n margin: 7.5px 0;\n background-color: var(--lottie-player-seeker-track-color);\n pointer-events: none;\n\n &::-webkit-progress-inner-element {\n border-radius: 3px;\n overflow: hidden;\n }\n\n &::-webkit-slider-runnable-track {\n background-color: transparent;\n }\n\n &::-webkit-progress-value {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n }\n\n .seeker {\n appearance: none;\n outline: none;\n width: 100%;\n height: 20px;\n border-radius: 3px;\n border: 0;\n cursor: pointer;\n background-color: transparent;\n\n display: var(--lottie-player-seeker-display);\n color: var(--lottie-player-seeker-thumb-color);\n margin: 0;\n padding: 7.5px 0;\n position: relative;\n z-index: 1;\n\n &::-webkit-slider-runnable-track,\n &::-webkit-slider-thumb {\n appearance: none;\n outline: none;\n }\n\n &::-webkit-slider-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n border: 0;\n background-color: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -webkit-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-webkit-slider-thumb,\n &:focus::-webkit-slider-thumb {\n transform: scale(1);\n }\n\n &::-moz-range-progress {\n background-color: var(--lottie-player-seeker-thumb-color);\n height: 5px;\n border-radius: 3px;\n }\n\n &::-moz-range-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background-color: var(--lottie-player-seeker-thumb-color);\n border: 0;\n cursor: pointer;\n -moz-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-moz-range-thumb,\n &:focus::-moz-range-thumb {\n transform: scale(1);\n }\n\n &::-ms-track {\n width: 100%;\n height: 5px;\n cursor: pointer;\n background: transparent;\n border-color: transparent;\n color: transparent;\n }\n\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n border-radius: 3px;\n }\n\n &::-ms-fill-lower {\n background-color: var(--lottie-player-seeker-thumb-color);\n border-radius: 3px;\n }\n\n &::-ms-thumb {\n border: 0;\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -ms-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-ms-thumb {\n transform: scale(1);\n }\n\n &:focus {\n &::-ms-thumb {\n transform: scale(1);\n }\n\n &::-ms-fill-lower,\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n }\n }\n }\n\n & *::-moz-progress-bar {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n}\n\n@keyframes fade-in {\n 0% {\n opacity: 0;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --lottie-player-toolbar-background-color: #000;\n --lottie-player-toolbar-icon-color: #fff;\n --lottie-player-toolbar-icon-hover-color: #fff;\n --lottie-player-seeker-track-color: rgb(255 255 255 / 60%);\n }\n}\n";
74
+ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-toolbar-height: 35px;\n --lottie-player-toolbar-background-color: #fff;\n --lottie-player-toolbar-icon-color: #000;\n --lottie-player-toolbar-icon-hover-color: #000;\n --lottie-player-toolbar-icon-active-color: #4285f4;\n --lottie-player-seeker-track-color: rgb(0 0 0 / 20%);\n --lottie-player-seeker-thumb-color: #4285f4;\n --lottie-player-seeker-display: block;\n\n width: 100%;\n height: 100%;\n\n &:not([hidden]) {\n display: block;\n }\n\n .main {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n margin: 0;\n padding: 0;\n }\n\n .animation {\n width: 100%;\n height: 100%;\n display: flex;\n margin: 0;\n padding: 0;\n contain: content;\n & svg g {\n will-change: transform;\n }\n }\n\n [data-controls='true'] .animation {\n height: calc(100% - 35px);\n }\n\n .animation-container {\n position: relative;\n }\n\n .popover {\n position: absolute;\n right: 5px;\n bottom: 40px;\n background-color: var(--lottie-player-toolbar-background-color);\n border-radius: 5px;\n padding: 10px 15px;\n border: solid 2px var(--lottie-player-toolbar-icon-color);\n animation: fade-in 0.2s ease-in-out;\n\n &::before {\n content: '';\n right: 10px;\n border: 7px solid transparent;\n margin-right: -7px;\n height: 0;\n width: 0;\n position: absolute;\n pointer-events: none;\n top: 100%;\n border-top-color: var(--lottie-player-toolbar-icon-color);\n }\n }\n\n .error {\n display: flex;\n margin: auto;\n justify-content: center;\n height: 100%;\n align-items: center;\n\n & svg {\n width: 100%;\n height: auto;\n }\n }\n\n .toolbar {\n display: flex;\n place-items: center center;\n background: var(--lottie-player-toolbar-background-color);\n margin: 0;\n height: 35px;\n padding: 5px;\n border-radius: 5px;\n gap: 5px;\n\n &.has-error {\n pointer-events: none;\n opacity: 0.5;\n }\n\n & button {\n cursor: pointer;\n fill: var(--lottie-player-toolbar-icon-color);\n color: var(--lottie-player-toolbar-icon-color);\n background: none;\n border: 0;\n padding: 0;\n outline: 0;\n height: 100%;\n margin: 0;\n align-items: center;\n gap: 5px;\n opacity: 0.9;\n\n &:not([hidden]) {\n display: flex;\n }\n\n &:hover {\n opacity: 1;\n }\n\n &[data-active='true'] {\n opacity: 1;\n fill: var(--lottie-player-toolbar-icon-active-color);\n }\n\n &:disabled {\n opacity: 0.5;\n }\n\n &:focus {\n outline: 0;\n }\n\n & svg {\n pointer-events: none;\n\n & > * {\n fill: inherit;\n }\n }\n\n &.disabled svg {\n display: none;\n }\n }\n }\n\n .progress-container {\n position: relative;\n width: 100%;\n\n &.simple {\n margin-right: 12px;\n }\n }\n\n & progress {\n appearance: none;\n outline: none;\n position: absolute;\n width: 100%;\n height: 5px;\n border-radius: 3px;\n border: 0;\n top: 0;\n left: 0;\n margin: 7.5px 0;\n background-color: var(--lottie-player-seeker-track-color);\n pointer-events: none;\n\n &::-webkit-progress-inner-element {\n border-radius: 3px;\n overflow: hidden;\n }\n\n &::-webkit-slider-runnable-track {\n background-color: transparent;\n }\n\n &::-webkit-progress-value {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n }\n\n .seeker {\n appearance: none;\n outline: none;\n width: 100%;\n height: 20px;\n border-radius: 3px;\n border: 0;\n cursor: pointer;\n background-color: transparent;\n\n display: var(--lottie-player-seeker-display);\n color: var(--lottie-player-seeker-thumb-color);\n margin: 0;\n padding: 7.5px 0;\n position: relative;\n z-index: 1;\n\n &::-webkit-slider-runnable-track,\n &::-webkit-slider-thumb {\n appearance: none;\n outline: none;\n }\n\n &::-webkit-slider-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n border: 0;\n background-color: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -webkit-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-webkit-slider-thumb,\n &:focus::-webkit-slider-thumb {\n transform: scale(1);\n }\n\n &::-moz-range-progress {\n background-color: var(--lottie-player-seeker-thumb-color);\n height: 5px;\n border-radius: 3px;\n }\n\n &::-moz-range-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background-color: var(--lottie-player-seeker-thumb-color);\n border: 0;\n cursor: pointer;\n -moz-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-moz-range-thumb,\n &:focus::-moz-range-thumb {\n transform: scale(1);\n }\n\n &::-ms-track {\n width: 100%;\n height: 5px;\n cursor: pointer;\n background: transparent;\n border-color: transparent;\n color: transparent;\n }\n\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n border-radius: 3px;\n }\n\n &::-ms-fill-lower {\n background-color: var(--lottie-player-seeker-thumb-color);\n border-radius: 3px;\n }\n\n &::-ms-thumb {\n border: 0;\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -ms-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-ms-thumb {\n transform: scale(1);\n }\n\n &:focus {\n &::-ms-thumb {\n transform: scale(1);\n }\n\n &::-ms-fill-lower,\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n }\n }\n }\n\n & *::-moz-progress-bar {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n}\n\n@keyframes fade-in {\n 0% {\n opacity: 0;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --lottie-player-toolbar-background-color: #000;\n --lottie-player-toolbar-icon-color: #fff;\n --lottie-player-toolbar-icon-hover-color: #fff;\n --lottie-player-seeker-track-color: rgb(255 255 255 / 60%);\n }\n}\n";
75
75
 
76
76
  let dotlottieTools = null, loadPromise = null;
77
77
  function loadDotLottieTools() {
@@ -202,21 +202,48 @@ var PlayerState = /*#__PURE__*/ function(PlayerState) {
202
202
  }({});
203
203
  const tagName = 'dotlottie-player';
204
204
 
205
- /**
206
- * Render Controls.
207
- */ function renderControls() {
205
+ function advancedButtons() {
208
206
  if (!this.shadow) {
209
- throw new Error('No Shadow Element');
210
- }
211
- const slot = this.shadow.querySelector('slot[name=controls]');
212
- if (!slot) {
213
207
  return;
214
208
  }
215
- if (!this.controls) {
216
- slot.innerHTML = '';
209
+ const toggleLoop = this.shadow.querySelector('.toggleLoop');
210
+ if (toggleLoop instanceof HTMLButtonElement) {
211
+ toggleLoop.onclick = this.toggleLoop;
212
+ }
213
+ const toggleBoomerang = this.shadow.querySelector('.toggleBoomerang');
214
+ if (toggleBoomerang instanceof HTMLButtonElement) {
215
+ toggleBoomerang.onclick = this.toggleBoomerang;
216
+ }
217
+ const convertButton = this.shadow.querySelector('.convert');
218
+ if (convertButton instanceof HTMLButtonElement) {
219
+ convertButton.onclick = async ()=>{
220
+ convertButton.disabled = true;
221
+ try {
222
+ const { convert } = await loadDotLottieTools();
223
+ await convert({
224
+ isDotLottie: this.isDotLottie,
225
+ manifest: this.getManifest(),
226
+ src: this.src || this.source
227
+ });
228
+ } finally{
229
+ convertButton.disabled = false;
230
+ }
231
+ };
232
+ }
233
+ const snapshot = this.shadow.querySelector('.snapshot');
234
+ if (snapshot instanceof HTMLButtonElement) {
235
+ snapshot.onclick = ()=>this.snapshot(true);
236
+ }
237
+ const toggleSettings = this.shadow.querySelector('.toggleSettings');
238
+ if (toggleSettings instanceof HTMLButtonElement) {
239
+ toggleSettings.onclick = this._handleSettingsClick;
240
+ toggleSettings.onblur = this._handleBlur;
241
+ }
242
+ }
243
+ function handleButtons() {
244
+ if (!this.shadow) {
217
245
  return;
218
246
  }
219
- 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 JSON animation to dotLottie format" aria-label="Convert ${this.isDotLottie ? 'dotLottie animation to JSON format' : 'JSON animation to dotLottie format'}" hidden>${convertIcon} ${this.isDotLottie ? 'Convert to JSON' : 'Convert to dotLottie'}</button> <button class="snapshot" aria-label="Download still image">${downloadIcon} Download still image</button></div>`}</div>`;
220
247
  const togglePlay = this.shadow.querySelector('.togglePlay');
221
248
  if (togglePlay instanceof HTMLButtonElement) {
222
249
  togglePlay.onclick = this.togglePlay;
@@ -244,41 +271,28 @@ const tagName = 'dotlottie-player';
244
271
  seeker.onchange = this._handleSeekChange;
245
272
  seeker.onmousedown = this._freeze;
246
273
  }
247
- if (!this.simple) {
248
- const toggleLoop = this.shadow.querySelector('.toggleLoop');
249
- if (toggleLoop instanceof HTMLButtonElement) {
250
- toggleLoop.onclick = this.toggleLoop;
251
- }
252
- const toggleBoomerang = this.shadow.querySelector('.toggleBoomerang');
253
- if (toggleBoomerang instanceof HTMLButtonElement) {
254
- toggleBoomerang.onclick = this.toggleBoomerang;
255
- }
256
- const convertButton = this.shadow.querySelector('.convert');
257
- if (convertButton instanceof HTMLButtonElement) {
258
- convertButton.onclick = async ()=>{
259
- convertButton.disabled = true;
260
- try {
261
- const { convert } = await loadDotLottieTools();
262
- await convert({
263
- isDotLottie: this.isDotLottie,
264
- manifest: this.getManifest(),
265
- src: this.src || this.source
266
- });
267
- } finally{
268
- convertButton.disabled = false;
269
- }
270
- };
271
- }
272
- const snapshot = this.shadow.querySelector('.snapshot');
273
- if (snapshot instanceof HTMLButtonElement) {
274
- snapshot.onclick = ()=>this.snapshot(true);
275
- }
276
- const toggleSettings = this.shadow.querySelector('.toggleSettings');
277
- if (toggleSettings instanceof HTMLButtonElement) {
278
- toggleSettings.onclick = this._handleSettingsClick;
279
- toggleSettings.onblur = this._handleBlur;
280
- }
274
+ if (this.simple) {
275
+ return;
276
+ }
277
+ advancedButtons.call(this);
278
+ }
279
+ /**
280
+ * Render Controls.
281
+ */ function renderControls() {
282
+ if (!this.shadow) {
283
+ throw new Error('No Shadow Element');
284
+ }
285
+ const slot = this.shadow.querySelector('slot[name=controls]');
286
+ if (!slot) {
287
+ return;
288
+ }
289
+ if (!this.controls) {
290
+ slot.innerHTML = '';
291
+ return;
281
292
  }
293
+ const convertLabel = this.isDotLottie ? 'dotLottie animation to JSON format' : 'JSON animation to dotLottie format', convertButtonText = this.isDotLottie ? 'Convert to JSON' : 'Convert to dotLottie';
294
+ 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>`;
295
+ handleButtons.call(this);
282
296
  }
283
297
  function updatePlayPauseButton(togglePlay, state) {
284
298
  const isActive = state === PlayerState.Playing || state === PlayerState.Paused;
@@ -713,15 +727,14 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
713
727
  * Player state.
714
728
  */ this.playerState = PlayerState.Loading, /**
715
729
  * Animation Container.
716
- */ this._container = null, this._DOMRect = null, this._errorMessage = 'Something went wrong', this._identifier = this.id || createElementID(), /**
730
+ */ this._container = null, this._errorMessage = 'Something went wrong', this._identifier = this.id || createElementID(), /**
717
731
  * Whether settings toolbar is open.
718
732
  */ this._isSettingsOpen = false, this._playerState = {
719
733
  count: 0,
734
+ frame: 0,
720
735
  loaded: false,
721
- prev: PlayerState.Loading,
722
- scrollTimeout: null,
723
- scrollY: 0,
724
- visible: false
736
+ playTimeout: null,
737
+ prev: PlayerState.Loading
725
738
  }, this._render = renderPlayer, /**
726
739
  * Seeker.
727
740
  */ this._seeker = 0, /**
@@ -730,7 +743,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
730
743
  * on load, if controls are visible.
731
744
  */ this._animations = [], this._controlsLoadId = 0, /**
732
745
  * Which animation to show, if several.
733
- */ this._currentAnimation = 0, this._errorLoadId = 0, this._isBounce = false, this._isDotLottie = false, this._lottieInstance = null, /**
746
+ */ this._currentAnimation = 0, this._errorLoadId = 0, this._isBounce = false, this._isDotLottie = false, this._isIntersecting = false, this._lottieInstance = null, /**
734
747
  * Multi-animation settings.
735
748
  */ this._multiAnimationSettings = [], this._renderControls = async ()=>{
736
749
  const slot = this.shadow?.querySelector('slot[name=controls]');
@@ -755,13 +768,13 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
755
768
  this._freeze = this._freeze.bind(this);
756
769
  this._handleBlur = this._handleBlur.bind(this);
757
770
  this._handleClick = this._handleClick.bind(this);
758
- this._handleScroll = this._handleScroll.bind(this);
759
771
  this._handleSeekChange = this._handleSeekChange.bind(this);
760
772
  this._handleWindowBlur = this._handleWindowBlur.bind(this);
761
773
  this._loopComplete = this._loopComplete.bind(this);
762
774
  this._mouseEnter = this._mouseEnter.bind(this);
763
775
  this._mouseLeave = this._mouseLeave.bind(this);
764
776
  this._onVisibilityChange = this._onVisibilityChange.bind(this);
777
+ this._scrollLoop = this._scrollLoop.bind(this);
765
778
  this._switchInstance = this._switchInstance.bind(this);
766
779
  this._handleSettingsClick = this._handleSettingsClick.bind(this);
767
780
  this.togglePlay = this.togglePlay.bind(this);
@@ -782,7 +795,8 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
782
795
  }
783
796
  /**
784
797
  * Runs when the value of an attribute is changed on the component.
785
- */ async attributeChangedCallback(name, _oldValue, value) {
798
+ */ // eslint-disable-next-line sonarjs/cognitive-complexity
799
+ async attributeChangedCallback(name, _oldValue, value) {
786
800
  if (!this._lottieInstance || !this.shadow || !this._container) {
787
801
  return;
788
802
  }
@@ -791,13 +805,12 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
791
805
  {
792
806
  if (value === '' || Boolean(value)) {
793
807
  this._lottieInstance.autoplay = false;
794
- addEventListener('scroll', this._handleScroll, {
795
- capture: true,
796
- passive: true
797
- });
808
+ if (this._isIntersecting) {
809
+ this._startScrollLoop();
810
+ }
798
811
  return;
799
812
  }
800
- removeEventListener('scroll', this._handleScroll, true);
813
+ this._stopScrollLoop();
801
814
  break;
802
815
  }
803
816
  case 'autoplay':
@@ -905,8 +918,8 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
905
918
  * Initialize everything on component first render.
906
919
  */ connectedCallback() {
907
920
  super.connectedCallback();
908
- try {
909
- void (async ()=>{
921
+ void (async ()=>{
922
+ try {
910
923
  await this._render();
911
924
  if (!this.shadow) {
912
925
  throw new Error('Missing Shadow element');
@@ -918,16 +931,13 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
918
931
  if (typeof document.hidden !== 'undefined') {
919
932
  document.addEventListener('visibilitychange', this._onVisibilityChange);
920
933
  }
921
- if (this._container) {
922
- this._DOMRect = this._container.getBoundingClientRect();
923
- }
924
934
  // Add intersection observer for detecting component being out-of-view.
925
935
  this._addIntersectionObserver();
926
936
  this.dispatchEvent(new CustomEvent(PlayerEvent.Rendered));
927
- })();
928
- } catch (error) {
929
- void this._handleError(error);
930
- }
937
+ } catch (error) {
938
+ void this._handleError(error);
939
+ }
940
+ })();
931
941
  }
932
942
  convert(_params) {
933
943
  throw new Error(notImplemented);
@@ -938,9 +948,11 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
938
948
  if (!this._lottieInstance?.destroy) {
939
949
  return;
940
950
  }
951
+ this._cancelScrollProbe();
941
952
  this.playerState = PlayerState.Destroyed;
942
953
  this._lottieInstance.destroy();
943
954
  this._lottieInstance = null;
955
+ this._stopScrollLoop();
944
956
  this.dispatchEvent(new CustomEvent(PlayerEvent.Destroyed));
945
957
  this.remove();
946
958
  document.removeEventListener('visibilitychange', this._onVisibilityChange);
@@ -949,9 +961,14 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
949
961
  * Cleanup on component destroy.
950
962
  */ disconnectedCallback() {
951
963
  // Remove intersection observer for detecting component being out-of-view
952
- if (this._intersectionObserver) {
953
- this._intersectionObserver.disconnect();
954
- this._intersectionObserver = undefined;
964
+ this._intersectionObserver?.disconnect();
965
+ this._intersectionObserver = undefined;
966
+ this._cancelScrollProbe();
967
+ if (this._playerState.scrollLoopId) {
968
+ cancelAnimationFrame(this._playerState.scrollLoopId);
969
+ }
970
+ if (this._playerState.playTimeout) {
971
+ clearTimeout(this._playerState.playTimeout);
955
972
  }
956
973
  // Remove the attached Visibility API's change event listener
957
974
  document.removeEventListener('visibilitychange', this._onVisibilityChange);
@@ -991,19 +1008,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
991
1008
  if (!animations?.every(isLottie)) {
992
1009
  throw new Error('Broken or corrupted file');
993
1010
  }
994
- const ldScript = this.parentElement?.querySelector('script[type="application/ld+json"]');
995
- if (ldScript) {
996
- const settings = JSON.parse(ldScript.innerHTML);
997
- if (settings.selector) {
998
- this.selector = settings.selector;
999
- }
1000
- if (settings.segment) {
1001
- this.setSegment(settings.segment);
1002
- }
1003
- if (settings.multiAnimationSettings) {
1004
- this.setMultiAnimationSettings(settings.multiAnimationSettings);
1005
- }
1006
- }
1011
+ this._handleLdScript();
1007
1012
  this._isBounce = this.mode === PlayMode.Bounce;
1008
1013
  if (this._multiAnimationSettings.length > 0 && this._multiAnimationSettings[this._currentAnimation]?.mode) {
1009
1014
  this._isBounce = this._multiAnimationSettings[this._currentAnimation]?.mode === PlayMode.Bounce;
@@ -1046,6 +1051,10 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1046
1051
  this._lottieInstance.setSpeed(speed);
1047
1052
  this._lottieInstance.setDirection(direction);
1048
1053
  this._lottieInstance.setSubframe(Boolean(this.subframe));
1054
+ // Set progress on load if AnimateOnScroll
1055
+ if (this.animateOnScroll) {
1056
+ this._applyScrollProgress();
1057
+ }
1049
1058
  // Start playing if autoplay is enabled
1050
1059
  if ((this.autoplay || this.animateOnScroll || this.playOnVisible) && this.direction === -1) {
1051
1060
  this.seek('99%');
@@ -1156,7 +1165,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1156
1165
  return;
1157
1166
  }
1158
1167
  // Extract frame number from either number or percentage value
1159
- const matches = value.toString().match(/^(\d+)(%?)$/);
1168
+ const matches = RegExp(/^(\d+)(%?)$/).exec(value.toString());
1160
1169
  if (!matches) {
1161
1170
  return;
1162
1171
  }
@@ -1345,9 +1354,12 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1345
1354
  /**
1346
1355
  * Handle blur.
1347
1356
  */ _handleBlur() {
1348
- setTimeout(()=>{
1357
+ const blurTimeout = setTimeout(()=>{
1349
1358
  this._toggleSettings(false);
1350
1359
  }, 200);
1360
+ if (!this._isSettingsOpen) {
1361
+ clearTimeout(blurTimeout);
1362
+ }
1351
1363
  }
1352
1364
  /**
1353
1365
  * Handle click.
@@ -1407,51 +1419,61 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1407
1419
  if (!this._container || this._intersectionObserver) {
1408
1420
  return;
1409
1421
  }
1410
- if (!('IntersectionObserver' in window)) {
1411
- this._intersectionObserverFallback();
1412
- removeEventListener('scroll', this._intersectionObserverFallback, true);
1413
- addEventListener('scroll', this._intersectionObserverFallback, {
1414
- capture: true,
1415
- passive: true
1416
- });
1417
- return;
1418
- }
1419
- this._intersectionObserver = new IntersectionObserver((entries)=>{
1420
- const { length } = entries;
1421
- for(let i = 0; i < length; i++){
1422
- if (!entries[i]?.isIntersecting || document.hidden) {
1423
- if (this.playerState === PlayerState.Playing) {
1424
- this._freeze();
1425
- }
1426
- this._playerState.visible = false;
1427
- continue;
1428
- }
1429
- if (!this.animateOnScroll && !this.playOnVisible && this.playerState === PlayerState.Frozen) {
1430
- this.play();
1431
- }
1432
- if (this.playOnVisible) {
1433
- if (this.playerState === PlayerState.Completed && !this.once) {
1434
- this.playerState = PlayerState.Playing;
1435
- this._lottieInstance?.goToAndPlay(this.direction === 1 ? 0 : this._lottieInstance.totalFrames);
1436
- } else {
1437
- setTimeout(()=>{
1438
- this.play();
1439
- }, this.delay);
1440
- }
1422
+ this._intersectionObserver = new IntersectionObserver(([{ isIntersecting }])=>{
1423
+ this._isIntersecting = isIntersecting;
1424
+ if (this.animateOnScroll) {
1425
+ if (isIntersecting) {
1426
+ this._startScrollLoop();
1427
+ } else {
1428
+ this._stopScrollLoop();
1441
1429
  }
1442
- /**
1443
- * If the player is a ways down the page, we need to account for this by
1444
- * setting _playerState.scrollY to the current scroll position. However, we
1445
- * also need to check that the player hasn't been scrolled past, so we check
1446
- * boundingClientRect as well.
1447
- */ if (!this._playerState.scrollY && (entries[i]?.boundingClientRect.y || 0) > 0) {
1448
- this._playerState.scrollY = scrollY;
1430
+ return;
1431
+ }
1432
+ if (!isIntersecting || document.hidden) {
1433
+ if (this.playerState === PlayerState.Playing) {
1434
+ this._freeze();
1449
1435
  }
1450
- this._playerState.visible = true;
1436
+ return;
1437
+ }
1438
+ if (!this.playOnVisible && this.playerState === PlayerState.Frozen) {
1439
+ this.play();
1440
+ }
1441
+ if (!this.playOnVisible) {
1442
+ return;
1443
+ }
1444
+ if (this.playerState === PlayerState.Completed && !this.once) {
1445
+ this.playerState = PlayerState.Playing;
1446
+ this._lottieInstance?.goToAndPlay(this.direction === 1 ? 0 : this._lottieInstance.totalFrames);
1447
+ return;
1448
+ }
1449
+ this._playerState.playTimeout = setTimeout(()=>{
1450
+ this.play();
1451
+ }, this.delay);
1452
+ if (this.playerState === PlayerState.Playing) {
1453
+ clearTimeout(this._playerState.playTimeout);
1451
1454
  }
1452
1455
  });
1453
1456
  this._intersectionObserver.observe(this._container);
1454
1457
  }
1458
+ _applyScrollProgress() {
1459
+ if (!this.animateOnScroll || !this._lottieInstance) {
1460
+ return;
1461
+ }
1462
+ const progress = this._getScrollProgress();
1463
+ if (progress === null) {
1464
+ return;
1465
+ }
1466
+ const { totalFrames } = this._lottieInstance, frame = progress * (totalFrames - 1), epsilon = this.subframe ? 0.1 : 0.5;
1467
+ if (Math.abs(frame - this._playerState.frame) < epsilon) {
1468
+ return;
1469
+ }
1470
+ this._playerState.frame = frame;
1471
+ this._lottieInstance.goToAndStop(frame, true);
1472
+ }
1473
+ _cancelScrollProbe() {
1474
+ this._scrollProbe?.cancel();
1475
+ this._scrollProbe = undefined;
1476
+ }
1455
1477
  _clearPrevious() {
1456
1478
  this._lottieInstance?.destroy();
1457
1479
  const figure = this.shadow?.querySelector('.animation');
@@ -1553,6 +1575,30 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1553
1575
  rendererType: this.renderer
1554
1576
  });
1555
1577
  }
1578
+ _getScrollProgress() {
1579
+ if (!this._container) {
1580
+ return null;
1581
+ }
1582
+ if ('ViewTimeline' in window) {
1583
+ this._scrollProbe ??= this._container.animate({
1584
+ '--dotlottie-scroll': [
1585
+ 0,
1586
+ 1
1587
+ ]
1588
+ }, {
1589
+ fill: 'both',
1590
+ rangeEnd: 'cover 100%',
1591
+ rangeStart: 'cover 0%',
1592
+ timeline: new ViewTimeline({
1593
+ axis: 'block',
1594
+ subject: this._container
1595
+ })
1596
+ });
1597
+ return this._scrollProbe.effect?.getComputedTiming().progress ?? null;
1598
+ }
1599
+ const { height, top } = this._container.getBoundingClientRect(), viewport = visualViewport?.height ?? innerHeight;
1600
+ return clamp((viewport - top) / (viewport + height), 0, 1);
1601
+ }
1556
1602
  async _handleError(error) {
1557
1603
  this.playerState = PlayerState.Error;
1558
1604
  if (!this.quiet) {
@@ -1562,44 +1608,37 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1562
1608
  this.dispatchEvent(new CustomEvent(PlayerEvent.Error));
1563
1609
  console.error(error);
1564
1610
  }
1565
- /**
1566
- * Handle scroll.
1567
- */ _handleScroll() {
1568
- if (!this.animateOnScroll || !this._DOMRect || !this._lottieInstance) {
1611
+ _handleLdScript() {
1612
+ const ldScript = this.parentElement?.querySelector('script[type="application/ld+json"]');
1613
+ if (!ldScript) {
1569
1614
  return;
1570
1615
  }
1571
- if (isServer) {
1572
- console.warn('DotLottie: Scroll animations might not work properly in a Server Side Rendering context. Try to wrap this in a client component.');
1573
- return;
1616
+ const settings = JSON.parse(ldScript.innerHTML);
1617
+ if (settings.selector) {
1618
+ this.selector = settings.selector;
1574
1619
  }
1575
- if (!this._playerState.visible) {
1620
+ if (settings.segment) {
1621
+ this.setSegment(settings.segment);
1622
+ }
1623
+ if (settings.multiAnimationSettings) {
1624
+ this.setMultiAnimationSettings(settings.multiAnimationSettings);
1625
+ }
1626
+ }
1627
+ _handleSelector(method) {
1628
+ if (!this.selector) {
1576
1629
  return;
1577
1630
  }
1578
- if (this._playerState.scrollTimeout) {
1579
- clearTimeout(this._playerState.scrollTimeout);
1580
- }
1581
- this._playerState.scrollTimeout = setTimeout(()=>{
1582
- this.playerState = PlayerState.Paused;
1583
- }, 400);
1584
- const { totalFrames } = this._lottieInstance;
1585
- let scrollPosition = scrollY - this._playerState.scrollY;
1586
- if (scrollY <= this._playerState.scrollY) {
1587
- scrollPosition = this._playerState.scrollY - scrollY;
1588
- }
1589
- const { bottom, height, top } = this._DOMRect;
1590
- let offset = height - bottom;
1591
- if (top >= innerHeight) {
1592
- offset = height;
1593
- }
1594
- const scrollProgress = scrollPosition / (innerHeight + offset), currentFrame = clamp(scrollProgress * (totalFrames - 1), 0, totalFrames);
1595
- requestAnimationFrame(()=>{
1596
- if (currentFrame >= totalFrames) {
1597
- this.playerState = PlayerState.Paused;
1598
- return;
1631
+ const selector = document.getElementById(this.selector);
1632
+ if (selector) {
1633
+ if (this.hover) {
1634
+ selector[method]('mouseenter', this._mouseEnter);
1635
+ selector[method]('mouseleave', this._mouseLeave);
1636
+ } else {
1637
+ selector[method]('click', this._handleClick);
1599
1638
  }
1600
- this.playerState = PlayerState.Playing;
1601
- this._lottieInstance?.goToAndStop(currentFrame, true);
1602
- });
1639
+ } else {
1640
+ this.selector = null;
1641
+ }
1603
1642
  }
1604
1643
  _handleWindowBlur({ type }) {
1605
1644
  if (this.dontFreezeOnBlur) {
@@ -1612,20 +1651,6 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1612
1651
  this.play();
1613
1652
  }
1614
1653
  }
1615
- _intersectionObserverFallback() {
1616
- if (!this._container) {
1617
- return;
1618
- }
1619
- const { bottom, left, right, top } = this._container.getBoundingClientRect();
1620
- this._playerState.visible = top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
1621
- if (this.autoplay || this.playOnVisible || this.playerState === PlayerState.Playing || this.playerState === PlayerState.Frozen) {
1622
- if (this._playerState.visible) {
1623
- this.play();
1624
- } else {
1625
- this._freeze();
1626
- }
1627
- }
1628
- }
1629
1654
  _loopComplete() {
1630
1655
  if (!this._lottieInstance) {
1631
1656
  return;
@@ -1698,10 +1723,6 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1698
1723
  }
1699
1724
  case MouseOut.Reverse:
1700
1725
  {
1701
- // const { direction = 1 } =
1702
- // this._multiAnimationSettings.length > 0 ?
1703
- // this._multiAnimationSettings[this._currentAnimation + 1] ?? { direction: 1 } : this,
1704
- // newDirection = direction * -1 as AnimationDirection
1705
1726
  this._lottieInstance.setDirection(-1);
1706
1727
  this.play();
1707
1728
  break;
@@ -1728,6 +1749,25 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1728
1749
  */ _removeEventListeners() {
1729
1750
  this._toggleEventListeners('remove');
1730
1751
  }
1752
+ _scrollLoop() {
1753
+ this._playerState.scrollLoopId = requestAnimationFrame(this._scrollLoop);
1754
+ this._applyScrollProgress();
1755
+ }
1756
+ _startScrollLoop() {
1757
+ if (isServer) {
1758
+ console.warn('DotLottie: Scroll animations will not work in a Server Side Rendering context. Try to wrap this in a client component.');
1759
+ return;
1760
+ }
1761
+ this._playerState.scrollLoopId ??= requestAnimationFrame(this._scrollLoop);
1762
+ }
1763
+ _stopScrollLoop() {
1764
+ if (this._playerState.scrollLoopId === undefined) {
1765
+ return;
1766
+ }
1767
+ cancelAnimationFrame(this._playerState.scrollLoopId);
1768
+ this._playerState.scrollLoopId = undefined;
1769
+ this._applyScrollProgress();
1770
+ }
1731
1771
  _switchInstance(isPrevious = false) {
1732
1772
  // Bail early if there is not animation to play
1733
1773
  if (!this._animations[this._currentAnimation]) {
@@ -1735,9 +1775,8 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1735
1775
  }
1736
1776
  try {
1737
1777
  // Clear previous animation
1738
- if (this._lottieInstance) {
1739
- this._lottieInstance.destroy();
1740
- }
1778
+ this._lottieInstance?.destroy();
1779
+ this._cancelScrollProbe();
1741
1780
  // Re-initialize lottie player
1742
1781
  this._lottieInstance = this.loadAnimation({
1743
1782
  ...this._getOptions(),
@@ -1753,7 +1792,8 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1753
1792
  this.dispatchEvent(new CustomEvent(isPrevious ? PlayerEvent.Previous : PlayerEvent.Next));
1754
1793
  if (this._multiAnimationSettings[this._currentAnimation]?.autoplay ?? this.autoplay) {
1755
1794
  if (this.animateOnScroll) {
1756
- this._lottieInstance.goToAndStop(0, true);
1795
+ // Get instant scroll position
1796
+ this._applyScrollProgress();
1757
1797
  this.playerState = PlayerState.Paused;
1758
1798
  return;
1759
1799
  }
@@ -1781,19 +1821,7 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1781
1821
  this._lottieInstance[method](PlayerEvent.DataReady, this._dataReady);
1782
1822
  this._lottieInstance[method](PlayerEvent.DataFailed, this._dataFailed);
1783
1823
  }
1784
- if (this.selector) {
1785
- const selector = document.getElementById(this.selector);
1786
- if (selector) {
1787
- if (this.hover) {
1788
- selector[method]('mouseenter', this._mouseEnter);
1789
- selector[method]('mouseleave', this._mouseLeave);
1790
- } else {
1791
- selector[method]('click', this._handleClick);
1792
- }
1793
- } else {
1794
- this.selector = null;
1795
- }
1796
- }
1824
+ this._handleSelector(method);
1797
1825
  if (this._container && !this.selector) {
1798
1826
  if (this.hover) {
1799
1827
  this._container[method]('mouseenter', this._mouseEnter);
@@ -1811,12 +1839,6 @@ const notImplemented = 'Method is not implemented', getStyles = async ()=>{
1811
1839
  capture: false,
1812
1840
  passive: true
1813
1841
  });
1814
- if (this.animateOnScroll) {
1815
- window[method]('scroll', this._handleScroll, {
1816
- capture: true,
1817
- passive: true
1818
- });
1819
- }
1820
1842
  }
1821
1843
  /**
1822
1844
  * Toggle show Settings.