@aarsteinmedia/dotlottie-player 6.0.1 → 6.0.3

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  Changelog was only added since [3.2.3], so it's not exhaustive. [Please report any missing noteable changes to us](https://github.com/aarsteinmedia/dotlottie-player/issues), and we'll add them promptly.
9
9
 
10
+ ## [6.0.3] - 13-08-2025
11
+
12
+ ### Changed
13
+
14
+ - Minor optimizations.
15
+
16
+ ## [6.0.2] - 04-07-2025
17
+
18
+ ### Changed
19
+
20
+ - Added new attribute: `dontFreezeOnBlur`. This disables default behavior where animation is frozen on window blur.
21
+
10
22
  ## [6.0.1] - 22-06-2025
11
23
 
12
24
  ### Changed
package/README.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  ![Awesome Vector Animations](/.github/readmeBanner.svg)
4
4
 
5
- We proudly claim this to be the most versatile, lightweight and efficient Lottie Player Web Component available. It's compatible with server side rendering, and completely framework agnostic.
5
+ We proudly claim this to be the most versatile, lightweight, and efficient Lottie Player Web Component available. It's compatible with server-side rendering and completely framework-agnostic.
6
6
 
7
- If you only need to render animations as SVGs, don't use any SVG effects like blur or drop shadow, don't use [Expressions](https://helpx.adobe.com/after-effects/using/expression-basics.html), and don't need to be able convert or combine animations on the fly you can access a light version of this package by importing `@aarsteinmedia/dotlottie-player/light`.
7
+ If you only need to render animations as SVGs, dont use any SVG effects like blur or drop shadow, dont use [Expressions](https://helpx.adobe.com/after-effects/using/expression-basics.html), and dont need to convert or combine animations on the fly you can use a lighter version of this package by importing `@aarsteinmedia/dotlottie-player/light`.
8
8
 
9
9
  ## Demo
10
10
 
11
- Here is [a demo](https://www.aarstein.media/en/dotlottie-player), running on Next.js 15 using TypeScript.
11
+ Here is [a demo](https://www.aarstein.media/en/dotlottie-player), running on Next.js 15 with TypeScript.
12
12
 
13
13
  ## Installation
14
14
 
@@ -16,53 +16,66 @@ Here is [a demo](https://www.aarstein.media/en/dotlottie-player), running on Nex
16
16
 
17
17
  - Import from CDN:
18
18
  - Full version:
19
- ```html
20
- <script src="https://unpkg.com/@aarsteinmedia/dotlottie-player@latest/dist/unpkg-full.js"></script>
21
- ```
19
+ ```html
20
+ <script src="https://unpkg.com/@aarsteinmedia/dotlottie-player@latest/dist/unpkg-full.js"></script>
21
+ ```
22
22
  - Light version:
23
- ```html
24
- <script src="https://unpkg.com/@aarsteinmedia/dotlottie-player@latest/dist/unpkg-light.js"></script>
25
- ```
23
+ ```html
24
+ <script src="https://unpkg.com/@aarsteinmedia/dotlottie-player@latest/dist/unpkg-light.js"></script>
25
+ ```
26
26
 
27
- - Import from node_modules directory:
27
+ - Import from `node_modules`:
28
28
  - Full version:
29
- ```html
30
- <script src="/node_modules/@aarsteinmedia/dotlottie-player/dist/unpkg-full.js"></script>
31
- ```
32
- - Light Version:
33
- <script src="/node_modules/@aarsteinmedia/dotlottie-player/dist/unpkg-light.js"></script>
29
+ ```html
30
+ <script src="/node_modules/@aarsteinmedia/dotlottie-player/dist/unpkg-full.js"></script>
31
+ ```
32
+ - Light version:
33
+ ```html
34
+ <script src="/node_modules/@aarsteinmedia/dotlottie-player/dist/unpkg-light.js"></script>
35
+ ```
34
36
 
35
37
  ### In JavaScript or TypeScript
36
38
 
37
- 1. Install using npm or yarn:
39
+ 1. Install using npm, pnpm, or yarn:
38
40
 
39
- ```shell
40
- npm install --save @aarsteinmedia/dotlottie-player
41
- ```
41
+ ```bash
42
+ pnpm add @aarsteinmedia/dotlottie-player
43
+ ```
42
44
 
43
45
  2. Import in your app:
44
46
 
45
- ```javascript
46
- import '@aarsteinmedia/dotlottie-player'
47
- ```
47
+ ```js
48
+ import '@aarsteinmedia/dotlottie-player'
49
+ ```
48
50
 
49
- or:
51
+ Or for the light version:
50
52
 
51
- ```javascript
52
- import '@aarsteinmedia/dotlottie-player/light'
53
+ ```js
54
+ import '@aarsteinmedia/dotlottie-player/light'
55
+ ```
56
+
57
+ Because this is a Web Component, you're adding it to the global scope of your web app. Unlike modular components, it should only be imported once – preferably early in your app lifecycle.
58
+
59
+ If you're using TypeScript and want to import the component type, do it modularly in addition to the global import:
60
+
61
+ ```ts
62
+ import '@aarsteinmedia/dotlottie-player' // Do this once globally.
63
+ import type DotLottiePlayer from '@aarsteinmedia/dotlottie-player' // Do this per component that needs it.
53
64
  ```
54
65
 
66
+ ⚠️ Note that this pattern may provoke linter errors, such as `import/no-duplicates`.
67
+
55
68
  ## Usage
56
69
 
57
- Add the element `dotlottie-player` to your markup and point `src` to a Lottie animation of your choice.
70
+ Add the `dotlottie-player` element to your markup and point the `src` to a Lottie animation of your choice:
58
71
 
59
72
  ```html
60
73
  <dotlottie-player
61
- id="find-me"
62
74
  autoplay
63
75
  controls
64
76
  subframe
65
77
  loop
78
+ id="find-me"
66
79
  src="https://storage.googleapis.com/aarsteinmedia/am.lottie"
67
80
  style="width: 320px; margin: auto;"
68
81
  >
@@ -77,13 +90,15 @@ player?.load('https://storage.googleapis.com/aarsteinmedia/am.lottie')
77
90
  ```
78
91
 
79
92
  ### Convert to dotLottie
80
- If you have a Lottie JSON animation and want to convert it to a dotLottie – to leverage compression, combine multiple animations in one file and keep your file library tidy with a discrete file extension you can do so with the `convert()` method. This will trigger a download in the browser. If you have `controls` set to visible there's a convert button in the context menu on the right hand side.
93
+ If you have a Lottie JSON animation and want to convert it to a dotLottie file – to leverage compression, combine multiple animations, and maintain a tidy file library you can use the `convert()` method. This will trigger a browser download.
94
+
95
+ If `controls` are visible, there’s also a convert button in the context menu on the right-hand side.
81
96
 
82
97
  ### Convert to JSON
83
- If you're debugging a dotLottie animation for instance if expressions aren't working as expected, you can convert it to JSON, either by usin the `convert()` method, or if `controls` are set to visible – a button in the context menu on the right hand side.
98
+ If you're debugging a dotLottie animation (e.g., expressions arent working as expected), you can convert it to JSON either using the `convert()` method or via the convert button if `controls` are enabled.
84
99
 
85
100
  ### Combine animations
86
- If you want to combine multiple animations in one single dotLottie file you can use the `addAnimation` method. This will trigger a download in the browser. The source files can be either dotLottie or JSON, and the output file will will always be a dotLottie.
101
+ To combine multiple animations into a single dotLottie file, use the `addAnimation()` method. This also triggers a browser download. Source files can be either dotLottie or JSON, and the output will always be dotLottie:
87
102
 
88
103
  ```javascript
89
104
  const lottiePlayer = document.querySelector('#find-me')
@@ -95,9 +110,9 @@ const lottiePlayer = document.querySelector('#find-me')
95
110
  }())
96
111
  ```
97
112
 
98
- You can also use this method independent of any Lottie player on the page, as long as the script is loaded, of course.
113
+ You can also use this method without any `<dotlottie-player>` on the page. As long as the script is loaded, `dotLottiePlayer()` is available as a global method.
99
114
 
100
- ```javascript
115
+ ```js
101
116
  (async () => {
102
117
  await dotLottiePlayer().addAnimation([
103
118
  { id: 'animation_1', url: '/path/to/animation_1.lottie' },
@@ -106,20 +121,20 @@ You can also use this method independent of any Lottie player on the page, as lo
106
121
  }())
107
122
  ```
108
123
 
109
- The new file wil automatically load the first animation when initialized. You can toggle between animations with the `next()` and `prev()` methods, or you can use the navigation buttons in the controls.
124
+ The new file will automatically load the first animation when initialized. You can toggle between animations using the `next()` and `prev()` methods, or the navigation buttons in the controls.
110
125
 
111
- Control the playback of multiple animations in a single file. In the example below the first animation will play once, and then the next animation will loop:
126
+ Here’s how to control playback settings for multiple animations:
112
127
 
113
128
  ```html
114
129
  <dotlottie-player
115
- id="find-me"
116
130
  subframe
131
+ id="find-me"
117
132
  src="/path/to/combined-animations.lottie"
118
133
  >
119
134
  </dotlottie-player>
120
135
  ```
121
136
 
122
- ```javascript
137
+ ```js
123
138
  const player = document.querySelector('#find-me')
124
139
  player?.setMultiAnimationSettings(
125
140
  [
@@ -138,7 +153,7 @@ Control the playback of multiple animations in a single file. In the example bel
138
153
 
139
154
  1. Import the component in `app.component.ts`.
140
155
 
141
- ```typescript
156
+ ```ts
142
157
  import { Component } from '@angular/core'
143
158
  import '@aarsteinmedia/dotlottie-player'
144
159
 
@@ -156,7 +171,9 @@ export class AppComponent {
156
171
 
157
172
  ### React.js / Next.js
158
173
 
159
- If you've already imported the library in a parent component, you don't need to import it again in children of that component. If you want to assign the element a CSS class note that you need to use the `class` namespace, and not `className`.
174
+ Because this is a Web Component and not a React component, note that you must use the `class` attribute (not `className`) when assigning a CSS class.
175
+
176
+ If you prefer pure React logic, you may want to check out [@aarsteinmedia/dotlottie-react](https://www.npmjs.com/package/@aarsteinmedia/dotlottie-react).
160
177
 
161
178
  ```jsx
162
179
  import '@aarsteinmedia/dotlottie-player'
@@ -164,11 +181,11 @@ import '@aarsteinmedia/dotlottie-player'
164
181
  function App() {
165
182
  return (
166
183
  <dotlottie-player
167
- class="your-class-name"
168
- src="https://storage.googleapis.com/aarsteinmedia/am.lottie"
169
184
  autoplay
170
185
  controls
171
186
  loop
187
+ class="your-class-name"
188
+ src="https://storage.googleapis.com/aarsteinmedia/am.lottie"
172
189
  style={{
173
190
  width: '320px',
174
191
  margin: 'auto'
@@ -180,7 +197,7 @@ function App() {
180
197
  export default App
181
198
  ```
182
199
 
183
- If you're using TypeScript and want to assign the component a `ref`, you can do it like this:
200
+ If you're using TypeScript and want to assign a `ref`, do it like this:
184
201
 
185
202
  ```tsx
186
203
  import { useRef } from 'react'
@@ -191,8 +208,8 @@ function App() {
191
208
  const animation = useRef<DotLottiePlayer | null>(null)
192
209
  return (
193
210
  <dotlottie-player
194
- ref={animation}
195
211
  subframe
212
+ ref={animation}
196
213
  src="https://storage.googleapis.com/aarsteinmedia/am.lottie"
197
214
  />
198
215
  )
@@ -210,7 +227,7 @@ Compared to React and Angular there's a couple of extra steps, but surely nothin
210
227
  #### In Vue.js
211
228
  `vite.config.ts`:
212
229
 
213
- ```typescript
230
+ ```ts
214
231
  import { defineConfig } from 'vite'
215
232
  import vue from '@vitejs/plugin-vue'
216
233
 
@@ -230,7 +247,7 @@ export default defineConfig({
230
247
  #### In Nuxt.js
231
248
  `nuxt.config.ts`:
232
249
 
233
- ```typescript
250
+ ```ts
234
251
  export default defineNuxtConfig({
235
252
  vue: {
236
253
  compilerOptions: {
@@ -245,7 +262,7 @@ export default defineNuxtConfig({
245
262
  #### In Vue.js
246
263
  `main.ts`:
247
264
 
248
- ```typescript
265
+ ```ts
249
266
  import { createApp } from 'vue'
250
267
  import DotLottiePlayer from '@aarsteinmedia/dotlottie-player'
251
268
  import App from './App.vue'
@@ -290,6 +307,7 @@ export default defineNuxtPlugin(({ vueApp }) => {
290
307
  | `controls` | Show controls | `boolean` | `false` |
291
308
  | `count` | Number of times to loop animation | `number` | `undefined` |
292
309
  | `direction` | Direction of animation | `1` \| `-1` | `1` |
310
+ | `dontFreezeOnBlur` | Whether to freeze playback on window blur. This is default behavior, but can be disabled | `boolean` | `1` |
293
311
  | `hover` | Whether to play on mouse hover | `boolean` | `false` |
294
312
  | `loop` | Whether to loop animation | `boolean` | `false` |
295
313
  | `mode` | Play mode | `normal` \| `bounce` | `normal` |
package/dist/full.d.ts CHANGED
@@ -105,6 +105,8 @@ declare abstract class DotLottiePlayerBase extends PropertyCallbackElement {
105
105
  get description(): string | null;
106
106
  set direction(value: AnimationDirection);
107
107
  get direction(): AnimationDirection;
108
+ set dontFreezeOnBlur(value: boolean);
109
+ get dontFreezeOnBlur(): boolean;
108
110
  set hover(value: boolean);
109
111
  get hover(): boolean;
110
112
  set intermission(value: number);
package/dist/full.js CHANGED
@@ -25,7 +25,7 @@ if (isServer) {
25
25
  for(let i = 0; i < length; i++){
26
26
  const initialValue = this[observedProperties[i]], cachedValue = Symbol(observedProperties[i]);
27
27
  this[cachedValue] = initialValue;
28
- Object.defineProperty(this, observedProperties[i], {
28
+ Object.defineProperty(this, observedProperties[i] ?? '', {
29
29
  get () {
30
30
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
31
31
  return this[cachedValue];
@@ -51,8 +51,8 @@ if (isServer) {
51
51
  if (!('propertyChangedCallback' in this) || typeof this.propertyChangedCallback !== 'function') {
52
52
  continue;
53
53
  }
54
- if (arr[i] in this) {
55
- this.propertyChangedCallback(arr[i], undefined, this[arr[i]]);
54
+ if (arr[i] ?? '' in this) {
55
+ this.propertyChangedCallback(arr[i] ?? '', undefined, this[arr[i]]);
56
56
  }
57
57
  }
58
58
  }
@@ -172,7 +172,7 @@ const tagName = 'dotlottie-player';
172
172
  slot.innerHTML = '';
173
173
  return;
174
174
  }
175
- 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="${Boolean(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>`;
175
+ 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>`;
176
176
  const togglePlay = this.shadow.querySelector('.togglePlay');
177
177
  if (togglePlay instanceof HTMLButtonElement) {
178
178
  togglePlay.onclick = this.togglePlay;
@@ -350,7 +350,7 @@ const notImplemented = 'Method is not implemented';
350
350
  }
351
351
  get animateOnScroll() {
352
352
  const val = this.getAttribute('animateOnScroll');
353
- return Boolean(val === 'true' || val === '' || val === '1');
353
+ return val === 'true' || val === '' || val === '1';
354
354
  }
355
355
  get animations() {
356
356
  return this._animations;
@@ -362,7 +362,7 @@ const notImplemented = 'Method is not implemented';
362
362
  }
363
363
  get autoplay() {
364
364
  const val = this.getAttribute('autoplay');
365
- return Boolean(val === 'true' || val === '' || val === '1');
365
+ return val === 'true' || val === '' || val === '1';
366
366
  }
367
367
  /**
368
368
  * Background color.
@@ -379,7 +379,7 @@ const notImplemented = 'Method is not implemented';
379
379
  }
380
380
  get controls() {
381
381
  const val = this.getAttribute('controls');
382
- return Boolean(val === 'true' || val === '' || val === '1');
382
+ return val === 'true' || val === '' || val === '1';
383
383
  }
384
384
  /**
385
385
  * Number of times to loop.
@@ -419,13 +419,22 @@ const notImplemented = 'Method is not implemented';
419
419
  return 1;
420
420
  }
421
421
  /**
422
+ * Whether to freeze animation when window loses focus.
423
+ */ set dontFreezeOnBlur(value) {
424
+ this.setAttribute('dontFreezeOnBlur', value.toString());
425
+ }
426
+ get dontFreezeOnBlur() {
427
+ const val = this.getAttribute('dontFreezeOnBlur');
428
+ return val === 'true' || val === '' || val === '1';
429
+ }
430
+ /**
422
431
  * Whether to play on mouseover.
423
432
  */ set hover(value) {
424
433
  this.setAttribute('hover', value.toString());
425
434
  }
426
435
  get hover() {
427
436
  const val = this.getAttribute('hover');
428
- return Boolean(val === 'true' || val === '' || val === '1');
437
+ return val === 'true' || val === '' || val === '1';
429
438
  }
430
439
  /**
431
440
  * Pause between loop intrations, in miliseconds.
@@ -449,12 +458,12 @@ const notImplemented = 'Method is not implemented';
449
458
  }
450
459
  get loop() {
451
460
  const val = this.getAttribute('loop');
452
- return Boolean(val === 'true' || val === '' || val === '1');
461
+ return val === 'true' || val === '' || val === '1';
453
462
  }
454
463
  /**
455
464
  * Play mode.
456
465
  */ set mode(value) {
457
- this.setAttribute('mode', value.toString());
466
+ this.setAttribute('mode', value);
458
467
  }
459
468
  get mode() {
460
469
  const val = this.getAttribute('mode');
@@ -506,7 +515,7 @@ const notImplemented = 'Method is not implemented';
506
515
  }
507
516
  get simple() {
508
517
  const val = this.getAttribute('simple');
509
- return Boolean(val === 'true' || val === '' || val === '1');
518
+ return val === 'true' || val === '' || val === '1';
510
519
  }
511
520
  /**
512
521
  * Speed.
@@ -535,7 +544,7 @@ const notImplemented = 'Method is not implemented';
535
544
  }
536
545
  get subframe() {
537
546
  const val = this.getAttribute('subframe');
538
- return Boolean(val === 'true' || val === '' || val === '1');
547
+ return val === 'true' || val === '' || val === '1';
539
548
  }
540
549
  constructor(){
541
550
  super(), this.isLight = false, /**
@@ -758,11 +767,12 @@ const notImplemented = 'Method is not implemented';
758
767
  }
759
768
  this._isBounce = this.mode === PlayMode.Bounce;
760
769
  if (this._multiAnimationSettings.length > 0 && this._multiAnimationSettings[this._currentAnimation]?.mode) {
761
- this._isBounce = this._multiAnimationSettings[this._currentAnimation].mode === PlayMode.Bounce;
770
+ this._isBounce = this._multiAnimationSettings[this._currentAnimation]?.mode === PlayMode.Bounce;
762
771
  }
763
- if (manifest?.animations.length === 1) {
764
- manifest.animations[0].autoplay = this.autoplay;
765
- manifest.animations[0].loop = this.loop;
772
+ const firstAnimation = manifest?.animations[0];
773
+ if (firstAnimation) {
774
+ firstAnimation.autoplay = this.autoplay;
775
+ firstAnimation.loop = this.loop;
766
776
  }
767
777
  this._isDotLottie = isDotLottie;
768
778
  this._animations = animations;
@@ -1147,7 +1157,7 @@ const notImplemented = 'Method is not implemented';
1147
1157
  this._intersectionObserver = new IntersectionObserver((entries)=>{
1148
1158
  const { length } = entries;
1149
1159
  for(let i = 0; i < length; i++){
1150
- if (!entries[i].isIntersecting || document.hidden) {
1160
+ if (!entries[i]?.isIntersecting || document.hidden) {
1151
1161
  if (this.playerState === PlayerState.Playing) {
1152
1162
  this._freeze();
1153
1163
  }
@@ -1289,6 +1299,9 @@ const notImplemented = 'Method is not implemented';
1289
1299
  }
1290
1300
  }
1291
1301
  _handleWindowBlur({ type }) {
1302
+ if (this.dontFreezeOnBlur) {
1303
+ return;
1304
+ }
1292
1305
  if (this.playerState === PlayerState.Playing && type === 'blur') {
1293
1306
  this._freeze();
1294
1307
  }
@@ -1379,7 +1392,7 @@ const notImplemented = 'Method is not implemented';
1379
1392
  });
1380
1393
  // Check play mode for current animation
1381
1394
  if (this._multiAnimationSettings[this._currentAnimation]?.mode) {
1382
- this._isBounce = this._multiAnimationSettings[this._currentAnimation].mode === PlayMode.Bounce;
1395
+ this._isBounce = this._multiAnimationSettings[this._currentAnimation]?.mode === PlayMode.Bounce;
1383
1396
  }
1384
1397
  // Remove event listeners to new Lottie instance, and add new
1385
1398
  this._removeEventListeners();
package/dist/light.d.ts CHANGED
@@ -105,6 +105,8 @@ declare abstract class DotLottiePlayerBase extends PropertyCallbackElement {
105
105
  get description(): string | null;
106
106
  set direction(value: AnimationDirection);
107
107
  get direction(): AnimationDirection;
108
+ set dontFreezeOnBlur(value: boolean);
109
+ get dontFreezeOnBlur(): boolean;
108
110
  set hover(value: boolean);
109
111
  get hover(): boolean;
110
112
  set intermission(value: number);
package/dist/light.js CHANGED
@@ -25,7 +25,7 @@ if (isServer) {
25
25
  for(let i = 0; i < length; i++){
26
26
  const initialValue = this[observedProperties[i]], cachedValue = Symbol(observedProperties[i]);
27
27
  this[cachedValue] = initialValue;
28
- Object.defineProperty(this, observedProperties[i], {
28
+ Object.defineProperty(this, observedProperties[i] ?? '', {
29
29
  get () {
30
30
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
31
31
  return this[cachedValue];
@@ -51,8 +51,8 @@ if (isServer) {
51
51
  if (!('propertyChangedCallback' in this) || typeof this.propertyChangedCallback !== 'function') {
52
52
  continue;
53
53
  }
54
- if (arr[i] in this) {
55
- this.propertyChangedCallback(arr[i], undefined, this[arr[i]]);
54
+ if (arr[i] ?? '' in this) {
55
+ this.propertyChangedCallback(arr[i] ?? '', undefined, this[arr[i]]);
56
56
  }
57
57
  }
58
58
  }
@@ -172,7 +172,7 @@ const tagName = 'dotlottie-player';
172
172
  slot.innerHTML = '';
173
173
  return;
174
174
  }
175
- 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="${Boolean(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>`;
175
+ 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>`;
176
176
  const togglePlay = this.shadow.querySelector('.togglePlay');
177
177
  if (togglePlay instanceof HTMLButtonElement) {
178
178
  togglePlay.onclick = this.togglePlay;
@@ -350,7 +350,7 @@ const notImplemented = 'Method is not implemented';
350
350
  }
351
351
  get animateOnScroll() {
352
352
  const val = this.getAttribute('animateOnScroll');
353
- return Boolean(val === 'true' || val === '' || val === '1');
353
+ return val === 'true' || val === '' || val === '1';
354
354
  }
355
355
  get animations() {
356
356
  return this._animations;
@@ -362,7 +362,7 @@ const notImplemented = 'Method is not implemented';
362
362
  }
363
363
  get autoplay() {
364
364
  const val = this.getAttribute('autoplay');
365
- return Boolean(val === 'true' || val === '' || val === '1');
365
+ return val === 'true' || val === '' || val === '1';
366
366
  }
367
367
  /**
368
368
  * Background color.
@@ -379,7 +379,7 @@ const notImplemented = 'Method is not implemented';
379
379
  }
380
380
  get controls() {
381
381
  const val = this.getAttribute('controls');
382
- return Boolean(val === 'true' || val === '' || val === '1');
382
+ return val === 'true' || val === '' || val === '1';
383
383
  }
384
384
  /**
385
385
  * Number of times to loop.
@@ -419,13 +419,22 @@ const notImplemented = 'Method is not implemented';
419
419
  return 1;
420
420
  }
421
421
  /**
422
+ * Whether to freeze animation when window loses focus.
423
+ */ set dontFreezeOnBlur(value) {
424
+ this.setAttribute('dontFreezeOnBlur', value.toString());
425
+ }
426
+ get dontFreezeOnBlur() {
427
+ const val = this.getAttribute('dontFreezeOnBlur');
428
+ return val === 'true' || val === '' || val === '1';
429
+ }
430
+ /**
422
431
  * Whether to play on mouseover.
423
432
  */ set hover(value) {
424
433
  this.setAttribute('hover', value.toString());
425
434
  }
426
435
  get hover() {
427
436
  const val = this.getAttribute('hover');
428
- return Boolean(val === 'true' || val === '' || val === '1');
437
+ return val === 'true' || val === '' || val === '1';
429
438
  }
430
439
  /**
431
440
  * Pause between loop intrations, in miliseconds.
@@ -449,12 +458,12 @@ const notImplemented = 'Method is not implemented';
449
458
  }
450
459
  get loop() {
451
460
  const val = this.getAttribute('loop');
452
- return Boolean(val === 'true' || val === '' || val === '1');
461
+ return val === 'true' || val === '' || val === '1';
453
462
  }
454
463
  /**
455
464
  * Play mode.
456
465
  */ set mode(value) {
457
- this.setAttribute('mode', value.toString());
466
+ this.setAttribute('mode', value);
458
467
  }
459
468
  get mode() {
460
469
  const val = this.getAttribute('mode');
@@ -506,7 +515,7 @@ const notImplemented = 'Method is not implemented';
506
515
  }
507
516
  get simple() {
508
517
  const val = this.getAttribute('simple');
509
- return Boolean(val === 'true' || val === '' || val === '1');
518
+ return val === 'true' || val === '' || val === '1';
510
519
  }
511
520
  /**
512
521
  * Speed.
@@ -535,7 +544,7 @@ const notImplemented = 'Method is not implemented';
535
544
  }
536
545
  get subframe() {
537
546
  const val = this.getAttribute('subframe');
538
- return Boolean(val === 'true' || val === '' || val === '1');
547
+ return val === 'true' || val === '' || val === '1';
539
548
  }
540
549
  constructor(){
541
550
  super(), this.isLight = false, /**
@@ -758,11 +767,12 @@ const notImplemented = 'Method is not implemented';
758
767
  }
759
768
  this._isBounce = this.mode === PlayMode.Bounce;
760
769
  if (this._multiAnimationSettings.length > 0 && this._multiAnimationSettings[this._currentAnimation]?.mode) {
761
- this._isBounce = this._multiAnimationSettings[this._currentAnimation].mode === PlayMode.Bounce;
770
+ this._isBounce = this._multiAnimationSettings[this._currentAnimation]?.mode === PlayMode.Bounce;
762
771
  }
763
- if (manifest?.animations.length === 1) {
764
- manifest.animations[0].autoplay = this.autoplay;
765
- manifest.animations[0].loop = this.loop;
772
+ const firstAnimation = manifest?.animations[0];
773
+ if (firstAnimation) {
774
+ firstAnimation.autoplay = this.autoplay;
775
+ firstAnimation.loop = this.loop;
766
776
  }
767
777
  this._isDotLottie = isDotLottie;
768
778
  this._animations = animations;
@@ -1147,7 +1157,7 @@ const notImplemented = 'Method is not implemented';
1147
1157
  this._intersectionObserver = new IntersectionObserver((entries)=>{
1148
1158
  const { length } = entries;
1149
1159
  for(let i = 0; i < length; i++){
1150
- if (!entries[i].isIntersecting || document.hidden) {
1160
+ if (!entries[i]?.isIntersecting || document.hidden) {
1151
1161
  if (this.playerState === PlayerState.Playing) {
1152
1162
  this._freeze();
1153
1163
  }
@@ -1289,6 +1299,9 @@ const notImplemented = 'Method is not implemented';
1289
1299
  }
1290
1300
  }
1291
1301
  _handleWindowBlur({ type }) {
1302
+ if (this.dontFreezeOnBlur) {
1303
+ return;
1304
+ }
1292
1305
  if (this.playerState === PlayerState.Playing && type === 'blur') {
1293
1306
  this._freeze();
1294
1307
  }
@@ -1379,7 +1392,7 @@ const notImplemented = 'Method is not implemented';
1379
1392
  });
1380
1393
  // Check play mode for current animation
1381
1394
  if (this._multiAnimationSettings[this._currentAnimation]?.mode) {
1382
- this._isBounce = this._multiAnimationSettings[this._currentAnimation].mode === PlayMode.Bounce;
1395
+ this._isBounce = this._multiAnimationSettings[this._currentAnimation]?.mode === PlayMode.Bounce;
1383
1396
  }
1384
1397
  // Remove event listeners to new Lottie instance, and add new
1385
1398
  this._removeEventListeners();