react-lottie 2.4.2 → 2.5.1
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 +22 -0
- package/README.md +52 -5
- package/dist/index.js +39 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.5.1](https://github.com/chenqingspring/react-lottie/compare/v2.5.0...v2.5.1) (2026-11-13)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* regenerate the animation when `options.renderer` changes, so a mounted component can switch between the `svg`, `canvas` and `html` engines and keeps its current frame and play/pause state; dropping the key falls back to `svg` (a change used to be silently ignored)
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* document switching `options.renderer` on a mounted component, and that a light `lottie-web` build registers one renderer only
|
|
14
|
+
* Storybook demo `lottie-renderer` with a live `svg` / `canvas` toggle next to both engines on their own component
|
|
15
|
+
|
|
16
|
+
## [2.5.0](https://github.com/chenqingspring/react-lottie/compare/v2.4.2...v2.5.0) (2026-11-10)
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* regenerate the animation when `options.rendererSettings` changes, so `preserveAspectRatio` and the other renderer settings can be switched on a mounted component; the current frame and play/pause state are kept, and recreating an equal object does not reload anything ([#98](https://github.com/chenqingspring/react-lottie/issues/98))
|
|
21
|
+
* Storybook demo of the live `preserveAspectRatio` toggle next to the three static modes
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
|
|
25
|
+
* document switching `rendererSettings` on a mounted component instead of remounting the animation ([#98](https://github.com/chenqingspring/react-lottie/issues/98))
|
|
26
|
+
|
|
5
27
|
## [2.4.2](https://github.com/chenqingspring/react-lottie/compare/v2.4.1...v2.4.2) (2026-11-09)
|
|
6
28
|
|
|
7
29
|
### Documentation
|
package/README.md
CHANGED
|
@@ -472,7 +472,8 @@ In rough order of impact:
|
|
|
472
472
|
3. **Compare the renderers.** `svg` (the default) is usually fastest for a few
|
|
473
473
|
large shapes and stays crisp at any size. `canvas` avoids updating hundreds
|
|
474
474
|
of DOM nodes and often wins for animations with many small shapes. Measure
|
|
475
|
-
both on the target device
|
|
475
|
+
both on the target device; a mounted component can switch between them
|
|
476
|
+
(see [Switch renderers](#switch-renderers-svg-canvas-html)).
|
|
476
477
|
4. **Stop off-screen animations.** lottie-web drives every playing animation
|
|
477
478
|
from the same `requestAnimationFrame` loop, so an animation scrolled out of
|
|
478
479
|
view still costs a frame. Pause it with `isPaused`, or with `pause()` on a
|
|
@@ -538,9 +539,11 @@ Two things make a working setting look broken:
|
|
|
538
539
|
- `meet` and `slice` render identically when the container has the same aspect
|
|
539
540
|
ratio as the animation. Sizing a square animation with `height={400}
|
|
540
541
|
width={400}` leaves nothing to crop or letterbox.
|
|
541
|
-
- The value is applied when the
|
|
542
|
-
`options.rendererSettings` on a mounted component
|
|
543
|
-
|
|
542
|
+
- The value is applied when the renderer is generated. Changing
|
|
543
|
+
`options.rendererSettings` on a mounted component regenerates the animation
|
|
544
|
+
and keeps the current frame and playback state, so a live toggle works; a
|
|
545
|
+
render that recreates an object with the same values does not reload
|
|
546
|
+
anything. See [Fit or crop, live](#fit-or-crop-live).
|
|
544
547
|
|
|
545
548
|
If the `<svg>` in the inspector still lacks `preserveAspectRatio`, check what
|
|
546
549
|
you pass as `options.animationData`. An ES module namespace
|
|
@@ -550,6 +553,50 @@ scale. See [Usage](#usage).
|
|
|
550
553
|
|
|
551
554
|
The canvas renderer reads `preserveAspectRatio` too.
|
|
552
555
|
|
|
556
|
+
#### Fit or crop, live
|
|
557
|
+
|
|
558
|
+
`rendererSettings` can be switched after mount. The animation is regenerated
|
|
559
|
+
with the new settings and the current frame plus play/pause state are restored,
|
|
560
|
+
so a `meet`/`slice` toggle does not restart a half-played animation:
|
|
561
|
+
|
|
562
|
+
```jsx
|
|
563
|
+
const [mode, setMode] = useState('xMidYMid meet');
|
|
564
|
+
|
|
565
|
+
<Lottie
|
|
566
|
+
options={{ animationData, loop: true, autoplay: true, rendererSettings: { preserveAspectRatio: mode } }}
|
|
567
|
+
height={200}
|
|
568
|
+
width={400}
|
|
569
|
+
/>
|
|
570
|
+
<button onClick={() => setMode(mode === 'xMidYMid meet' ? 'xMidYMid slice' : 'xMidYMid meet')}>
|
|
571
|
+
toggle fit / crop
|
|
572
|
+
</button>
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
The Storybook story `lottie-renderer-settings` demonstrates the same toggle.
|
|
576
|
+
|
|
577
|
+
### Switch renderers (svg, canvas, html)
|
|
578
|
+
|
|
579
|
+
`renderer` selects the engine lottie-web uses to draw the animation and belongs
|
|
580
|
+
inside `options`. It defaults to `svg`; `canvas` and `html` are the other
|
|
581
|
+
choices.
|
|
582
|
+
|
|
583
|
+
```jsx
|
|
584
|
+
const [renderer, setRenderer] = useState('svg');
|
|
585
|
+
|
|
586
|
+
<Lottie options={{ animationData, loop: true, autoplay: true, renderer }} height={200} width={200} />
|
|
587
|
+
<button onClick={() => setRenderer(renderer === 'svg' ? 'canvas' : 'svg')}>
|
|
588
|
+
switch renderer
|
|
589
|
+
</button>
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Changing `options.renderer` on a mounted component regenerates the animation
|
|
593
|
+
with the new engine and keeps the current frame and play/pause state, so the
|
|
594
|
+
toggle does not restart it. Dropping the key falls back to `svg` again. This
|
|
595
|
+
needs the full `lottie-web` player: a [light build](#shrinking-the-lottie-web-bundle)
|
|
596
|
+
registers a single renderer, so switching to another one fails to start.
|
|
597
|
+
|
|
598
|
+
The Storybook story `lottie-renderer` demonstrates the toggle.
|
|
599
|
+
|
|
553
600
|
### Masks are missing in Safari
|
|
554
601
|
|
|
555
602
|
Safari resolves the `url(#…)` references that lottie-web generates for masks,
|
|
@@ -875,7 +922,7 @@ the object representing the animation settings that will be instantiated by body
|
|
|
875
922
|
>
|
|
876
923
|
>**renderer** *optional* [default: `svg`] — `svg`, `canvas` or `html`. See [Performance on iOS and low-end devices](#performance-on-ios-and-low-end-devices).
|
|
877
924
|
>
|
|
878
|
-
>**rendererSettings** *optional* — settings forwarded to lottie-web's renderer, such as `preserveAspectRatio`, `progressiveLoad`, `className` and `id`. Must be inside `options`, not a prop on `<Lottie />`.
|
|
925
|
+
>**rendererSettings** *optional* — settings forwarded to lottie-web's renderer, such as `preserveAspectRatio`, `progressiveLoad`, `className` and `id`. Must be inside `options`, not a prop on `<Lottie />`. Changing it regenerates the animation and keeps the current frame and playback state. See [Fit or crop an animation](#fit-or-crop-an-animation-preserveaspectratio).
|
|
879
926
|
>
|
|
880
927
|
>**locationHref** *optional* — the base URL lottie-web writes into its SVG mask, matte, gradient and filter references, applied with `setLocationHref()` before the animation is generated. Use it when Safari drops masks because of a `<base>` tag. See [Masks are missing in Safari](#masks-are-missing-in-safari).
|
|
881
928
|
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,6 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
|
9
9
|
var _lottieWeb = _interopRequireDefault(require("lottie-web"));
|
|
10
10
|
var _colorFilters = require("./colorFilters");
|
|
11
11
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
13
12
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14
13
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15
14
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
@@ -25,6 +24,7 @@ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf
|
|
|
25
24
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
26
25
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
27
26
|
function _toPrimitive(t, e) { if ("object" != _typeof(t) || !t) return t; var r; if ("undefined" != typeof Symbol && void 0 !== (r = t[Symbol.toPrimitive])) { var i = r.call(t, e || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === e ? String : Number)(t); }
|
|
27
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
28
28
|
var segmentsAreEqual = function segmentsAreEqual(a, b) {
|
|
29
29
|
if (a === b) return true;
|
|
30
30
|
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
@@ -33,6 +33,27 @@ var segmentsAreEqual = function segmentsAreEqual(a, b) {
|
|
|
33
33
|
});
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
/* `rendererSettings` is usually written as an object literal inside `options`,
|
|
37
|
+
so a re-render creates a new object even when nothing changed. Compare the
|
|
38
|
+
values, not the identity, to avoid reloading the animation on every render
|
|
39
|
+
while still reacting to a real change (issue #98). */
|
|
40
|
+
var rendererSettingsAreEqual = function rendererSettingsAreEqual(a, b) {
|
|
41
|
+
if (a === b) return true;
|
|
42
|
+
if (a == null || b == null) return a == null && b == null;
|
|
43
|
+
if (_typeof(a) !== 'object' || _typeof(b) !== 'object') return false;
|
|
44
|
+
var keys = Object.keys(a);
|
|
45
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
46
|
+
return keys.every(function (key) {
|
|
47
|
+
return Object.prototype.hasOwnProperty.call(b, key) && a[key] === b[key];
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/* lottie-web falls back to the SVG renderer when `options.renderer` is absent,
|
|
52
|
+
so compare the value it actually ends up with. */
|
|
53
|
+
var rendererOf = function rendererOf(options) {
|
|
54
|
+
return options && options.renderer ? options.renderer : 'svg';
|
|
55
|
+
};
|
|
56
|
+
|
|
36
57
|
/* lottie-web crashes deep inside its renderer with "Cannot read property
|
|
37
58
|
'length' of undefined" when the data is not a plain animation object. Accept
|
|
38
59
|
the shapes users actually pass (a namespace import, a frozen ESM object) and
|
|
@@ -101,6 +122,8 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
101
122
|
this.animationData = animationData;
|
|
102
123
|
this.colorFilters = colorFilters;
|
|
103
124
|
this.locationHref = options.locationHref;
|
|
125
|
+
this.rendererSettings = rendererSettings;
|
|
126
|
+
this.renderer = rendererOf(options);
|
|
104
127
|
var filteredAnimationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(animationData, options.path), colorFilters);
|
|
105
128
|
this.options = {
|
|
106
129
|
container: this.el,
|
|
@@ -159,10 +182,18 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
159
182
|
/* lottie-web bakes the base URL into each generated `url(#id)` reference,
|
|
160
183
|
so a new href only takes effect when the animation is generated again. */
|
|
161
184
|
var locationHrefChanged = this.locationHref !== this.props.options.locationHref;
|
|
185
|
+
/* `preserveAspectRatio`, `progressiveLoad` and the other renderer settings
|
|
186
|
+
are read by lottie-web when it generates the renderer, so a change has to
|
|
187
|
+
regenerate the animation too (issue #98). */
|
|
188
|
+
var rendererSettingsChanged = !rendererSettingsAreEqual(this.rendererSettings, this.props.options.rendererSettings);
|
|
189
|
+
/* `renderer` selects the engine that generates the animation (`svg`,
|
|
190
|
+
`canvas` or `html`), so it can only change by loading again. A change
|
|
191
|
+
used to be ignored, which left the animation on the old engine. */
|
|
192
|
+
var rendererChanged = this.renderer !== rendererOf(this.props.options);
|
|
162
193
|
/* The wrapper element is part of lottie-web's configuration, so a new
|
|
163
194
|
element means the animation has to be bound to the new node. */
|
|
164
195
|
var containerChanged = prevProps.as !== this.props.as;
|
|
165
|
-
if (dataChanged || pathChanged || colorFiltersChanged || locationHrefChanged || containerChanged) {
|
|
196
|
+
if (dataChanged || pathChanged || colorFiltersChanged || locationHrefChanged || rendererSettingsChanged || rendererChanged || containerChanged) {
|
|
166
197
|
var currentFrame = this.anim ? this.anim.currentFrame : 0;
|
|
167
198
|
var wasPaused = this.anim ? this.anim.isPaused : true;
|
|
168
199
|
/* lottie-web cannot recolor a loaded animation and cannot move one to
|
|
@@ -174,10 +205,16 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
174
205
|
this.animationData = this.props.options.animationData;
|
|
175
206
|
this.colorFilters = this.props.colorFilters;
|
|
176
207
|
this.locationHref = this.props.options.locationHref;
|
|
208
|
+
this.rendererSettings = this.props.options.rendererSettings;
|
|
209
|
+
this.renderer = rendererOf(this.props.options);
|
|
177
210
|
this.options = _objectSpread(_objectSpread({}, this.options), this.props.options);
|
|
178
211
|
/* `options` is caller-owned and knows nothing about the wrapper, so the
|
|
179
212
|
ref (already pointing at the newly rendered node) wins. */
|
|
180
213
|
this.options.container = this.el;
|
|
214
|
+
/* Spreading does not delete a key the caller dropped, so a removed
|
|
215
|
+
rendererSettings or renderer would otherwise keep its old value. */
|
|
216
|
+
this.options.rendererSettings = this.props.options.rendererSettings;
|
|
217
|
+
this.options.renderer = this.renderer;
|
|
181
218
|
this.options.animationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(this.props.options.animationData, this.props.options.path), this.props.colorFilters);
|
|
182
219
|
this.setLocationHref(this.options);
|
|
183
220
|
this.anim = _lottieWeb["default"].loadAnimation(this.options);
|