react-lottie 2.4.2 → 2.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
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.0](https://github.com/chenqingspring/react-lottie/compare/v2.4.2...v2.5.0) (2026-11-10)
6
+
7
+ ### Features
8
+
9
+ * 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))
10
+ * Storybook demo of the live `preserveAspectRatio` toggle next to the three static modes
11
+
12
+ ### Documentation
13
+
14
+ * document switching `rendererSettings` on a mounted component instead of remounting the animation ([#98](https://github.com/chenqingspring/react-lottie/issues/98))
15
+
5
16
  ## [2.4.2](https://github.com/chenqingspring/react-lottie/compare/v2.4.1...v2.4.2) (2026-11-09)
6
17
 
7
18
  ### Documentation
package/README.md CHANGED
@@ -538,9 +538,11 @@ Two things make a working setting look broken:
538
538
  - `meet` and `slice` render identically when the container has the same aspect
539
539
  ratio as the animation. Sizing a square animation with `height={400}
540
540
  width={400}` leaves nothing to crop or letterbox.
541
- - The value is applied when the animation loads. Changing
542
- `options.rendererSettings` on a mounted component does not re-apply it;
543
- remount the animation (or change its `key`) to pick up a new value.
541
+ - The value is applied when the renderer is generated. Changing
542
+ `options.rendererSettings` on a mounted component regenerates the animation
543
+ and keeps the current frame and playback state, so a live toggle works; a
544
+ render that recreates an object with the same values does not reload
545
+ anything. See [Fit or crop, live](#fit-or-crop-live).
544
546
 
545
547
  If the `<svg>` in the inspector still lacks `preserveAspectRatio`, check what
546
548
  you pass as `options.animationData`. An ES module namespace
@@ -550,6 +552,27 @@ scale. See [Usage](#usage).
550
552
 
551
553
  The canvas renderer reads `preserveAspectRatio` too.
552
554
 
555
+ #### Fit or crop, live
556
+
557
+ `rendererSettings` can be switched after mount. The animation is regenerated
558
+ with the new settings and the current frame plus play/pause state are restored,
559
+ so a `meet`/`slice` toggle does not restart a half-played animation:
560
+
561
+ ```jsx
562
+ const [mode, setMode] = useState('xMidYMid meet');
563
+
564
+ <Lottie
565
+ options={{ animationData, loop: true, autoplay: true, rendererSettings: { preserveAspectRatio: mode } }}
566
+ height={200}
567
+ width={400}
568
+ />
569
+ <button onClick={() => setMode(mode === 'xMidYMid meet' ? 'xMidYMid slice' : 'xMidYMid meet')}>
570
+ toggle fit / crop
571
+ </button>
572
+ ```
573
+
574
+ The Storybook story `lottie-renderer-settings` demonstrates the same toggle.
575
+
553
576
  ### Masks are missing in Safari
554
577
 
555
578
  Safari resolves the `url(#…)` references that lottie-web generates for masks,
@@ -875,7 +898,7 @@ the object representing the animation settings that will be instantiated by body
875
898
  >
876
899
  >**renderer** *optional* [default: `svg`] — `svg`, `canvas` or `html`. See [Performance on iOS and low-end devices](#performance-on-ios-and-low-end-devices).
877
900
  >
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 />`. Read once when the animation loads. See [Fit or crop an animation](#fit-or-crop-an-animation-preserveaspectratio).
901
+ >**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
902
  >
880
903
  >**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
904
 
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,21 @@ 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
+
36
51
  /* lottie-web crashes deep inside its renderer with "Cannot read property
37
52
  'length' of undefined" when the data is not a plain animation object. Accept
38
53
  the shapes users actually pass (a namespace import, a frozen ESM object) and
@@ -101,6 +116,7 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
101
116
  this.animationData = animationData;
102
117
  this.colorFilters = colorFilters;
103
118
  this.locationHref = options.locationHref;
119
+ this.rendererSettings = rendererSettings;
104
120
  var filteredAnimationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(animationData, options.path), colorFilters);
105
121
  this.options = {
106
122
  container: this.el,
@@ -159,10 +175,14 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
159
175
  /* lottie-web bakes the base URL into each generated `url(#id)` reference,
160
176
  so a new href only takes effect when the animation is generated again. */
161
177
  var locationHrefChanged = this.locationHref !== this.props.options.locationHref;
178
+ /* `preserveAspectRatio`, `progressiveLoad` and the other renderer settings
179
+ are read by lottie-web when it generates the renderer, so a change has to
180
+ regenerate the animation too (issue #98). */
181
+ var rendererSettingsChanged = !rendererSettingsAreEqual(this.rendererSettings, this.props.options.rendererSettings);
162
182
  /* The wrapper element is part of lottie-web's configuration, so a new
163
183
  element means the animation has to be bound to the new node. */
164
184
  var containerChanged = prevProps.as !== this.props.as;
165
- if (dataChanged || pathChanged || colorFiltersChanged || locationHrefChanged || containerChanged) {
185
+ if (dataChanged || pathChanged || colorFiltersChanged || locationHrefChanged || rendererSettingsChanged || containerChanged) {
166
186
  var currentFrame = this.anim ? this.anim.currentFrame : 0;
167
187
  var wasPaused = this.anim ? this.anim.isPaused : true;
168
188
  /* lottie-web cannot recolor a loaded animation and cannot move one to
@@ -174,10 +194,14 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
174
194
  this.animationData = this.props.options.animationData;
175
195
  this.colorFilters = this.props.colorFilters;
176
196
  this.locationHref = this.props.options.locationHref;
197
+ this.rendererSettings = this.props.options.rendererSettings;
177
198
  this.options = _objectSpread(_objectSpread({}, this.options), this.props.options);
178
199
  /* `options` is caller-owned and knows nothing about the wrapper, so the
179
200
  ref (already pointing at the newly rendered node) wins. */
180
201
  this.options.container = this.el;
202
+ /* Spreading does not delete a key the caller dropped, so a removed
203
+ rendererSettings would otherwise keep its old value. */
204
+ this.options.rendererSettings = this.props.options.rendererSettings;
181
205
  this.options.animationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(this.props.options.animationData, this.props.options.path), this.props.colorFilters);
182
206
  this.setLocationHref(this.options);
183
207
  this.anim = _lottieWeb["default"].loadAnimation(this.options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-lottie",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "funding": "https://opencollective.com/react-lottie",
5
5
  "description": "lottie animation view for React",
6
6
  "repository": {