@remotion/motion-blur 4.0.528 → 4.0.530

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/README.md CHANGED
@@ -15,4 +15,4 @@ Remove the `^` character from the version number to use the exact version.
15
15
 
16
16
  ## Usage
17
17
 
18
- See the [documentation](https://www.remotion.dev/docs/motion-blur) for more information.
18
+ See the [documentation](https://www.remotion.dev/docs/motion-blur/api) for more information.
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { type InteractiveBaseProps, type InteractivePremountProps } from 'remotion';
3
+ export type HtmlInCanvasMotionBlurProps = InteractiveBaseProps & InteractivePremountProps & {
4
+ readonly children: React.ReactNode;
5
+ readonly width: number;
6
+ readonly height: number;
7
+ readonly shutterAngle?: number;
8
+ readonly samples?: number;
9
+ readonly disabled?: boolean;
10
+ };
11
+ /**
12
+ * Experimental motion blur that averages separately captured HTML-in-canvas
13
+ * snapshots of the children at fractional frames. Preview requires Chrome with
14
+ * the experimental HTML-in-canvas flag enabled. Nested HtmlInCanvas components
15
+ * are currently unsupported.
16
+ */
17
+ export declare const HtmlInCanvasMotionBlur: React.ComponentType<HtmlInCanvasMotionBlurProps>;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HtmlInCanvasMotionBlur = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const remotion_1 = require("remotion");
7
+ const htmlInCanvasMotionBlurSchema = {
8
+ ...remotion_1.Interactive.baseSchema,
9
+ ...remotion_1.Interactive.premountSchema,
10
+ shutterAngle: {
11
+ type: 'number',
12
+ min: 0,
13
+ max: 360,
14
+ step: 1,
15
+ default: 180,
16
+ description: 'Shutter angle',
17
+ hiddenFromList: false,
18
+ },
19
+ samples: {
20
+ type: 'number',
21
+ min: 1,
22
+ max: 64,
23
+ step: 1,
24
+ integer: true,
25
+ default: 8,
26
+ description: 'Samples',
27
+ hiddenFromList: false,
28
+ keyframable: false,
29
+ },
30
+ disabled: {
31
+ type: 'boolean',
32
+ default: false,
33
+ description: 'Disabled',
34
+ },
35
+ };
36
+ const MotionBlurSample = ({ children, width, height, index, count, shutterInFrames, firstFrame, lastFrame, }) => {
37
+ const frame = (0, remotion_1.useCurrentFrame)();
38
+ const sampleFrame = Math.max(firstFrame, Math.min(lastFrame, frame + ((index + 0.5) / count - 0.5) * shutterInFrames));
39
+ const content = jsx_runtime_1.jsx(remotion_1.Freeze, { frame: sampleFrame, children: children });
40
+ const isRepresentativeSample = index === Math.floor(count / 2);
41
+ return (jsx_runtime_1.jsx("div", { "aria-hidden": !isRepresentativeSample, drawable: '', style: {
42
+ position: 'absolute',
43
+ inset: 0,
44
+ width,
45
+ height,
46
+ isolation: 'isolate',
47
+ pointerEvents: isRepresentativeSample ? 'auto' : 'none',
48
+ }, children: isRepresentativeSample ? (content) : (jsx_runtime_1.jsx(remotion_1.Internals.DisableSequenceRegistrationProvider, { children: content })) }));
49
+ };
50
+ const HtmlInCanvasMotionBlurInner = ({ children, width, height, shutterAngle = 180, samples = 8, disabled = false, from, durationInFrames, trimBefore, playbackRate, loop, freeze, hidden, name, premountFor, postmountFor, showInTimeline, styleWhilePremounted, styleWhilePostmounted, controls, }) => {
51
+ const { durationInFrames: compositionDurationInFrames } = (0, remotion_1.useVideoConfig)();
52
+ if (!Number.isInteger(samples) || samples < 1 || samples > 64) {
53
+ throw new TypeError(`"samples" must be an integer between 1 and 64, but got ${String(samples)}`);
54
+ }
55
+ if (typeof shutterAngle !== 'number' ||
56
+ !Number.isFinite(shutterAngle) ||
57
+ shutterAngle < 0 ||
58
+ shutterAngle > 360) {
59
+ throw new TypeError(`"shutterAngle" must be between 0 and 360, but got ${String(shutterAngle)}`);
60
+ }
61
+ // Keep the first sample mounted when blur is disabled so the canvas stays visible.
62
+ const actualSamples = disabled || shutterAngle === 0 ? 1 : samples;
63
+ const shutterInFrames = (shutterAngle / 360) * (playbackRate !== null && playbackRate !== void 0 ? playbackRate : 1);
64
+ const firstFrame = trimBefore !== null && trimBefore !== void 0 ? trimBefore : 0;
65
+ const visibleDuration = Math.max(0, Math.min(remotion_1.Internals.resolveSequenceDuration({
66
+ durationInFrames,
67
+ playbackRate,
68
+ loop,
69
+ }), compositionDurationInFrames - (from !== null && from !== void 0 ? from : 0)));
70
+ const lastFrame = firstFrame + Math.max(0, visibleDuration - 1) * (playbackRate !== null && playbackRate !== void 0 ? playbackRate : 1);
71
+ const onPaint = (0, react_1.useCallback)(({ canvas, element, elementImage }) => {
72
+ const context = canvas.getContext('2d');
73
+ if (!context) {
74
+ throw new Error('Failed to acquire 2D context for motion blur');
75
+ }
76
+ const layoutCanvas = element.parentElement;
77
+ if (!(layoutCanvas instanceof HTMLCanvasElement)) {
78
+ throw new Error('Failed to find the HTML-in-canvas layout canvas');
79
+ }
80
+ const captureElements = Array.from(layoutCanvas.children);
81
+ if (captureElements.length !== actualSamples ||
82
+ captureElements[0] !== element) {
83
+ throw new Error('Motion blur samples are not ready to paint');
84
+ }
85
+ context.reset();
86
+ context.globalCompositeOperation = 'lighter';
87
+ context.globalAlpha = 1 / actualSamples;
88
+ for (const [index, sampleElement] of captureElements.entries()) {
89
+ const image = index === 0
90
+ ? elementImage
91
+ : layoutCanvas.captureElementImage(sampleElement);
92
+ try {
93
+ const sampleTransform = context.drawElementImage(image, 0, 0);
94
+ if (index === Math.floor(actualSamples / 2)) {
95
+ sampleElement.style.transform =
96
+ sampleTransform.toString();
97
+ }
98
+ }
99
+ finally {
100
+ if (index !== 0) {
101
+ image.close();
102
+ }
103
+ }
104
+ }
105
+ }, [actualSamples]);
106
+ const sampleElements = Array.from({ length: actualSamples }, (_, index) => {
107
+ return (jsx_runtime_1.jsx(MotionBlurSample, { width: width, height: height, index: index, count: actualSamples, shutterInFrames: shutterInFrames, firstFrame: firstFrame, lastFrame: lastFrame, children: children }, index));
108
+ });
109
+ // The root wrapper holds the first sample. The rest must be canvas children.
110
+ // Reuse HtmlInCanvas's Sequence for the motion blur controls.
111
+ return (jsx_runtime_1.jsx(remotion_1.HtmlInCanvas, { width: width, height: height, from: from, durationInFrames: durationInFrames, trimBefore: trimBefore, playbackRate: playbackRate, loop: loop, freeze: freeze, hidden: hidden, name: name !== null && name !== void 0 ? name : '<HtmlInCanvasMotionBlur>', premountFor: premountFor, postmountFor: postmountFor, showInTimeline: showInTimeline, styleWhilePremounted: styleWhilePremounted, styleWhilePostmounted: styleWhilePostmounted, controls, onPaint: onPaint, _remotionInternalCanvasSiblings: sampleElements.slice(1), children: sampleElements[0] }));
112
+ };
113
+ /**
114
+ * Experimental motion blur that averages separately captured HTML-in-canvas
115
+ * snapshots of the children at fractional frames. Preview requires Chrome with
116
+ * the experimental HTML-in-canvas flag enabled. Nested HtmlInCanvas components
117
+ * are currently unsupported.
118
+ */
119
+ exports.HtmlInCanvasMotionBlur = remotion_1.Interactive.withSchema({
120
+ Component: HtmlInCanvasMotionBlurInner,
121
+ componentName: '<HtmlInCanvasMotionBlur>',
122
+ componentIdentity: 'dev.remotion.motionBlur.HtmlInCanvasMotionBlur',
123
+ schema: htmlInCanvasMotionBlurSchema,
124
+ supportsEffects: false,
125
+ });
126
+ exports.HtmlInCanvasMotionBlur.displayName = 'HtmlInCanvasMotionBlur';
@@ -1,2 +1,3 @@
1
1
  export { CameraMotionBlur, CameraMotionBlurProps } from './CameraMotionBlur';
2
+ export { HtmlInCanvasMotionBlur, HtmlInCanvasMotionBlurProps, } from './HtmlInCanvasMotionBlur';
2
3
  export { Trail, TrailProps } from './Trail';
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Trail = exports.CameraMotionBlur = void 0;
3
+ exports.Trail = exports.HtmlInCanvasMotionBlur = exports.CameraMotionBlur = void 0;
4
4
  const CameraMotionBlur_1 = require("./CameraMotionBlur");
5
5
  Object.defineProperty(exports, "CameraMotionBlur", { enumerable: true, get: function () { return CameraMotionBlur_1.CameraMotionBlur; } });
6
+ const HtmlInCanvasMotionBlur_1 = require("./HtmlInCanvasMotionBlur");
7
+ Object.defineProperty(exports, "HtmlInCanvasMotionBlur", { enumerable: true, get: function () { return HtmlInCanvasMotionBlur_1.HtmlInCanvasMotionBlur; } });
6
8
  const Trail_1 = require("./Trail");
7
9
  Object.defineProperty(exports, "Trail", { enumerable: true, get: function () { return Trail_1.Trail; } });
@@ -56,16 +56,199 @@ var CameraMotionBlur = ({
56
56
  })
57
57
  });
58
58
  };
59
+ // src/HtmlInCanvasMotionBlur.tsx
60
+ import { useCallback } from "react";
61
+ import {
62
+ Freeze as Freeze2,
63
+ HtmlInCanvas,
64
+ Interactive,
65
+ Internals,
66
+ useCurrentFrame as useCurrentFrame2,
67
+ useVideoConfig
68
+ } from "remotion";
69
+ import { jsx as jsx2 } from "react/jsx-runtime";
70
+ var htmlInCanvasMotionBlurSchema = {
71
+ ...Interactive.baseSchema,
72
+ ...Interactive.premountSchema,
73
+ shutterAngle: {
74
+ type: "number",
75
+ min: 0,
76
+ max: 360,
77
+ step: 1,
78
+ default: 180,
79
+ description: "Shutter angle",
80
+ hiddenFromList: false
81
+ },
82
+ samples: {
83
+ type: "number",
84
+ min: 1,
85
+ max: 64,
86
+ step: 1,
87
+ integer: true,
88
+ default: 8,
89
+ description: "Samples",
90
+ hiddenFromList: false,
91
+ keyframable: false
92
+ },
93
+ disabled: {
94
+ type: "boolean",
95
+ default: false,
96
+ description: "Disabled"
97
+ }
98
+ };
99
+ var MotionBlurSample = ({
100
+ children,
101
+ width,
102
+ height,
103
+ index,
104
+ count,
105
+ shutterInFrames,
106
+ firstFrame,
107
+ lastFrame
108
+ }) => {
109
+ const frame = useCurrentFrame2();
110
+ const sampleFrame = Math.max(firstFrame, Math.min(lastFrame, frame + ((index + 0.5) / count - 0.5) * shutterInFrames));
111
+ const content = /* @__PURE__ */ jsx2(Freeze2, {
112
+ frame: sampleFrame,
113
+ children
114
+ });
115
+ const isRepresentativeSample = index === Math.floor(count / 2);
116
+ return /* @__PURE__ */ jsx2("div", {
117
+ "aria-hidden": !isRepresentativeSample,
118
+ ...{ drawable: "" },
119
+ style: {
120
+ position: "absolute",
121
+ inset: 0,
122
+ width,
123
+ height,
124
+ isolation: "isolate",
125
+ pointerEvents: isRepresentativeSample ? "auto" : "none"
126
+ },
127
+ children: isRepresentativeSample ? content : /* @__PURE__ */ jsx2(Internals.DisableSequenceRegistrationProvider, {
128
+ children: content
129
+ })
130
+ });
131
+ };
132
+ var HtmlInCanvasMotionBlurInner = ({
133
+ children,
134
+ width,
135
+ height,
136
+ shutterAngle = 180,
137
+ samples = 8,
138
+ disabled = false,
139
+ from,
140
+ durationInFrames,
141
+ trimBefore,
142
+ playbackRate,
143
+ loop,
144
+ freeze,
145
+ hidden,
146
+ name,
147
+ premountFor,
148
+ postmountFor,
149
+ showInTimeline,
150
+ styleWhilePremounted,
151
+ styleWhilePostmounted,
152
+ controls
153
+ }) => {
154
+ const { durationInFrames: compositionDurationInFrames } = useVideoConfig();
155
+ if (!Number.isInteger(samples) || samples < 1 || samples > 64) {
156
+ throw new TypeError(`"samples" must be an integer between 1 and 64, but got ${String(samples)}`);
157
+ }
158
+ if (typeof shutterAngle !== "number" || !Number.isFinite(shutterAngle) || shutterAngle < 0 || shutterAngle > 360) {
159
+ throw new TypeError(`"shutterAngle" must be between 0 and 360, but got ${String(shutterAngle)}`);
160
+ }
161
+ const actualSamples = disabled || shutterAngle === 0 ? 1 : samples;
162
+ const shutterInFrames = shutterAngle / 360 * (playbackRate ?? 1);
163
+ const firstFrame = trimBefore ?? 0;
164
+ const visibleDuration = Math.max(0, Math.min(Internals.resolveSequenceDuration({
165
+ durationInFrames,
166
+ playbackRate,
167
+ loop
168
+ }), compositionDurationInFrames - (from ?? 0)));
169
+ const lastFrame = firstFrame + Math.max(0, visibleDuration - 1) * (playbackRate ?? 1);
170
+ const onPaint = useCallback(({ canvas, element, elementImage }) => {
171
+ const context = canvas.getContext("2d");
172
+ if (!context) {
173
+ throw new Error("Failed to acquire 2D context for motion blur");
174
+ }
175
+ const layoutCanvas = element.parentElement;
176
+ if (!(layoutCanvas instanceof HTMLCanvasElement)) {
177
+ throw new Error("Failed to find the HTML-in-canvas layout canvas");
178
+ }
179
+ const captureElements = Array.from(layoutCanvas.children);
180
+ if (captureElements.length !== actualSamples || captureElements[0] !== element) {
181
+ throw new Error("Motion blur samples are not ready to paint");
182
+ }
183
+ context.reset();
184
+ context.globalCompositeOperation = "lighter";
185
+ context.globalAlpha = 1 / actualSamples;
186
+ for (const [index, sampleElement] of captureElements.entries()) {
187
+ const image = index === 0 ? elementImage : layoutCanvas.captureElementImage(sampleElement);
188
+ try {
189
+ const sampleTransform = context.drawElementImage(image, 0, 0);
190
+ if (index === Math.floor(actualSamples / 2)) {
191
+ sampleElement.style.transform = sampleTransform.toString();
192
+ }
193
+ } finally {
194
+ if (index !== 0) {
195
+ image.close();
196
+ }
197
+ }
198
+ }
199
+ }, [actualSamples]);
200
+ const sampleElements = Array.from({ length: actualSamples }, (_, index) => {
201
+ return /* @__PURE__ */ jsx2(MotionBlurSample, {
202
+ width,
203
+ height,
204
+ index,
205
+ count: actualSamples,
206
+ shutterInFrames,
207
+ firstFrame,
208
+ lastFrame,
209
+ children
210
+ }, index);
211
+ });
212
+ return /* @__PURE__ */ jsx2(HtmlInCanvas, {
213
+ width,
214
+ height,
215
+ from,
216
+ durationInFrames,
217
+ trimBefore,
218
+ playbackRate,
219
+ loop,
220
+ freeze,
221
+ hidden,
222
+ name: name ?? "<HtmlInCanvasMotionBlur>",
223
+ premountFor,
224
+ postmountFor,
225
+ showInTimeline,
226
+ styleWhilePremounted,
227
+ styleWhilePostmounted,
228
+ ...{ controls },
229
+ onPaint,
230
+ _remotionInternalCanvasSiblings: sampleElements.slice(1),
231
+ children: sampleElements[0]
232
+ });
233
+ };
234
+ var HtmlInCanvasMotionBlur = Interactive.withSchema({
235
+ Component: HtmlInCanvasMotionBlurInner,
236
+ componentName: "<HtmlInCanvasMotionBlur>",
237
+ componentIdentity: "dev.remotion.motionBlur.HtmlInCanvasMotionBlur",
238
+ schema: htmlInCanvasMotionBlurSchema,
239
+ supportsEffects: false
240
+ });
241
+ HtmlInCanvasMotionBlur.displayName = "HtmlInCanvasMotionBlur";
59
242
  // src/Trail.tsx
60
- import { AbsoluteFill as AbsoluteFill2, Freeze as Freeze2, useCurrentFrame as useCurrentFrame2 } from "remotion";
61
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
243
+ import { AbsoluteFill as AbsoluteFill2, Freeze as Freeze3, useCurrentFrame as useCurrentFrame3 } from "remotion";
244
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
62
245
  var Trail = ({
63
246
  children,
64
247
  layers,
65
248
  lagInFrames,
66
249
  trailOpacity
67
250
  }) => {
68
- const frame = useCurrentFrame2();
251
+ const frame = useCurrentFrame3();
69
252
  if (typeof layers !== "number" || Number.isNaN(layers) || !Number.isFinite(layers)) {
70
253
  throw new TypeError(`"layers" must be a number, but got ${JSON.stringify(layers)}`);
71
254
  }
@@ -84,11 +267,11 @@ var Trail = ({
84
267
  return /* @__PURE__ */ jsxs(AbsoluteFill2, {
85
268
  children: [
86
269
  new Array(layers).fill(true).map((_, i) => {
87
- return /* @__PURE__ */ jsx2(AbsoluteFill2, {
270
+ return /* @__PURE__ */ jsx3(AbsoluteFill2, {
88
271
  style: {
89
272
  opacity: trailOpacity - (layers - i) / layers * trailOpacity
90
273
  },
91
- children: /* @__PURE__ */ jsx2(Freeze2, {
274
+ children: /* @__PURE__ */ jsx3(Freeze3, {
92
275
  frame: frame - lagInFrames * (layers - i),
93
276
  children
94
277
  })
@@ -100,5 +283,6 @@ var Trail = ({
100
283
  };
101
284
  export {
102
285
  Trail,
286
+ HtmlInCanvasMotionBlur,
103
287
  CameraMotionBlur
104
288
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/motion-blur"
4
4
  },
5
5
  "name": "@remotion/motion-blur",
6
- "version": "4.0.528",
6
+ "version": "4.0.530",
7
7
  "description": "Motion blur effect for Remotion",
8
8
  "main": "dist/cjs/index.js",
9
9
  "types": "dist/cjs/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  "url": "https://github.com/remotion-dev/remotion/issues"
22
22
  },
23
23
  "dependencies": {
24
- "remotion": "4.0.528"
24
+ "remotion": "4.0.530"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": ">=16.8.0",
@@ -39,8 +39,8 @@
39
39
  "devDependencies": {
40
40
  "react": "19.2.3",
41
41
  "react-dom": "19.2.3",
42
- "remotion": "4.0.528",
43
- "@remotion/eslint-config-internal": "4.0.528",
42
+ "remotion": "4.0.530",
43
+ "@remotion/eslint-config-internal": "4.0.530",
44
44
  "eslint": "9.19.0",
45
45
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
46
46
  },
@@ -52,5 +52,5 @@
52
52
  "publishConfig": {
53
53
  "access": "public"
54
54
  },
55
- "homepage": "https://www.remotion.dev/docs/motion-blur"
55
+ "homepage": "https://www.remotion.dev/docs/motion-blur/api"
56
56
  }