@remotion/transitions 4.0.496 → 4.0.497

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.
@@ -9,8 +9,8 @@ type SeriesSequenceProps = PropsWithChildren<{
9
9
  readonly durationInFrames: number;
10
10
  readonly offset?: number;
11
11
  readonly className?: string;
12
- } & LayoutBasedProps & Pick<SequencePropsWithoutDuration, 'name' | 'showInTimeline' | 'freeze' | 'hidden'>>;
13
- export type DrawFunction = (prevImage: ElementImage | null, nextImage: ElementImage | null, progress: number) => void;
12
+ } & LayoutBasedProps & Pick<SequencePropsWithoutDuration, 'name' | 'showInTimeline' | 'freeze' | 'hidden' | 'trimBefore'>>;
13
+ export type DrawFunction = (prevImage: OffscreenCanvas | null, nextImage: OffscreenCanvas | null, progress: number) => void;
14
14
  declare const TransitionSeries: FC<SequencePropsWithoutDuration> & {
15
15
  Sequence: FC<SeriesSequenceProps>;
16
16
  Transition: TransitionSeriesTransitionComponent;
@@ -77,6 +77,7 @@ const transitionSeriesSequenceSchema = {
77
77
  hidden: remotion_1.Internals.sequenceSchema.hidden,
78
78
  showInTimeline: remotion_1.Internals.sequenceSchema.showInTimeline,
79
79
  freeze: remotion_1.Internals.freezeField,
80
+ trimBefore: remotion_1.Internals.sequenceSchema.trimBefore,
80
81
  layout: remotion_1.Internals.sequenceSchema.layout,
81
82
  };
82
83
  const SeriesSequenceInner = ({ offset = 0, className = '', stack = null, _remotionInternalRender = null, ...props }) => {
@@ -7,7 +7,7 @@ import {
7
7
  Internals,
8
8
  useDelayRender
9
9
  } from "remotion";
10
- import { jsx } from "react/jsx-runtime";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
11
  var HtmlInCanvasPresentation = ({
12
12
  children,
13
13
  onElementImage,
@@ -23,6 +23,7 @@ var HtmlInCanvasPresentation = ({
23
23
  throw new Error(HTML_IN_CANVAS_UNSUPPORTED_MESSAGE);
24
24
  }
25
25
  const canvasRef = useRef(null);
26
+ const outputCanvasRef = useRef(null);
26
27
  const canvasSubtreeStyle = useMemo(() => {
27
28
  return {
28
29
  width: "100%",
@@ -34,7 +35,21 @@ var HtmlInCanvasPresentation = ({
34
35
  bottom: 0
35
36
  };
36
37
  }, []);
37
- const [offscreenCanvas] = useState(() => new OffscreenCanvas(1, 1));
38
+ const outputCanvasStyle = useMemo(() => {
39
+ return {
40
+ width: "100%",
41
+ height: "100%",
42
+ position: "absolute",
43
+ top: 0,
44
+ left: 0,
45
+ right: 0,
46
+ bottom: 0,
47
+ pointerEvents: "none"
48
+ };
49
+ }, []);
50
+ const captureCanvasRef = useRef(null);
51
+ const captureContextRef = useRef(null);
52
+ const [shaderCanvas] = useState(() => new OffscreenCanvas(1, 1));
38
53
  const passedPropsRef = useRef(passedProps);
39
54
  passedPropsRef.current = passedProps;
40
55
  const memoizedEffects = Internals.useMemoizedEffects({
@@ -43,33 +58,58 @@ var HtmlInCanvasPresentation = ({
43
58
  });
44
59
  const effectsRef = useRef(memoizedEffects);
45
60
  effectsRef.current = memoizedEffects;
46
- const [instance] = useState(() => shader(offscreenCanvas));
61
+ const [instance] = useState(() => shader(shaderCanvas));
47
62
  useLayoutEffect(() => {
63
+ const canvas = canvasRef.current;
64
+ if (!canvas) {
65
+ return () => {
66
+ instance.cleanup();
67
+ };
68
+ }
69
+ const captureCanvas = canvas.transferControlToOffscreen();
70
+ const captureContext = captureCanvas.getContext("2d");
71
+ if (!captureContext) {
72
+ throw new Error("Failed to create capture canvas context");
73
+ }
74
+ captureCanvasRef.current = captureCanvas;
75
+ captureContextRef.current = captureContext;
48
76
  return () => {
49
77
  instance.cleanup();
78
+ captureCanvasRef.current = null;
79
+ captureContextRef.current = null;
50
80
  };
51
- }, [offscreenCanvas, instance]);
81
+ }, [instance]);
52
82
  const chainState = Internals.useEffectChainState();
53
83
  const { delayRender, continueRender } = useDelayRender();
54
84
  const draw = useCallback(async (prevImage, nextImage, progress) => {
55
- if (!canvasRef.current) {
85
+ const outputCanvas = outputCanvasRef.current;
86
+ if (!outputCanvas) {
56
87
  throw new Error("Canvas not found");
57
88
  }
58
89
  const handle = delayRender("onPaint");
90
+ const clearOutput = () => {
91
+ const context = outputCanvas.getContext("2d");
92
+ if (!context) {
93
+ throw new Error("Failed to create output canvas context");
94
+ }
95
+ context.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
96
+ };
59
97
  if (!prevImage && !nextImage) {
60
- continueRender(handle);
61
98
  instance.clear();
99
+ clearOutput();
100
+ continueRender(handle);
62
101
  return;
63
102
  }
64
103
  const width = prevImage?.width ?? nextImage?.width ?? 0;
65
104
  const height = prevImage?.height ?? nextImage?.height ?? 0;
66
105
  if (width === 0 || height === 0) {
67
- continueRender(handle);
68
106
  instance.clear();
107
+ clearOutput();
108
+ continueRender(handle);
69
109
  return;
70
110
  }
71
- offscreenCanvas.width = width;
72
- offscreenCanvas.height = height;
111
+ shaderCanvas.width = width;
112
+ shaderCanvas.height = height;
73
113
  instance.draw({
74
114
  prevImage,
75
115
  nextImage,
@@ -80,14 +120,14 @@ var HtmlInCanvasPresentation = ({
80
120
  });
81
121
  await Internals.runEffectChain({
82
122
  state: chainState.get(width, height),
83
- source: offscreenCanvas,
123
+ source: shaderCanvas,
84
124
  effects: effectsRef.current ?? [],
85
125
  width,
86
126
  height,
87
- output: canvasRef.current
127
+ output: outputCanvas
88
128
  });
89
129
  continueRender(handle);
90
- }, [chainState, instance, offscreenCanvas, continueRender, delayRender]);
130
+ }, [chainState, continueRender, delayRender, instance, shaderCanvas]);
91
131
  const passThrough = bothEnteringAndExiting && presentationDirection === "exiting";
92
132
  useLayoutEffect(() => {
93
133
  if (passThrough) {
@@ -100,11 +140,19 @@ var HtmlInCanvasPresentation = ({
100
140
  canvas.layoutSubtree = true;
101
141
  const onPaint = () => {
102
142
  const firstChild = canvas.firstChild;
103
- if (!firstChild) {
143
+ const captureCanvas = captureCanvasRef.current;
144
+ const captureContext = captureContextRef.current;
145
+ if (!firstChild || !captureCanvas || !captureContext) {
104
146
  return;
105
147
  }
106
148
  const elementImage = canvas.captureElementImage(firstChild);
107
- onElementImage(elementImage, draw);
149
+ try {
150
+ captureContext.reset();
151
+ captureContext.drawElementImage(elementImage, 0, 0);
152
+ } finally {
153
+ elementImage.close();
154
+ }
155
+ onElementImage(captureCanvas, draw);
108
156
  };
109
157
  canvas.addEventListener("paint", onPaint);
110
158
  return () => {
@@ -138,20 +186,39 @@ var HtmlInCanvasPresentation = ({
138
186
  return;
139
187
  }
140
188
  const observer = new ResizeObserver(([entry]) => {
141
- canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
142
- canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
189
+ const outputCanvas = outputCanvasRef.current;
190
+ const captureCanvas = captureCanvasRef.current;
191
+ if (!outputCanvas || !captureCanvas) {
192
+ return;
193
+ }
194
+ const width = entry.devicePixelContentBoxSize[0].inlineSize;
195
+ const height = entry.devicePixelContentBoxSize[0].blockSize;
196
+ captureCanvas.width = width;
197
+ captureCanvas.height = height;
198
+ outputCanvas.width = width;
199
+ outputCanvas.height = height;
200
+ canvas.requestPaint?.();
143
201
  });
144
202
  observer.observe(canvas, { box: "device-pixel-content-box" });
203
+ return () => {
204
+ observer.disconnect();
205
+ };
145
206
  }, [passThrough]);
146
207
  if (passThrough) {
147
208
  return children;
148
209
  }
149
- return /* @__PURE__ */ jsx(AbsoluteFill, {
150
- children: /* @__PURE__ */ jsx("canvas", {
151
- ref: canvasRef,
152
- style: canvasSubtreeStyle,
153
- children
154
- })
210
+ return /* @__PURE__ */ jsxs(AbsoluteFill, {
211
+ children: [
212
+ /* @__PURE__ */ jsx("canvas", {
213
+ ref: canvasRef,
214
+ style: canvasSubtreeStyle,
215
+ children
216
+ }),
217
+ /* @__PURE__ */ jsx("canvas", {
218
+ ref: outputCanvasRef,
219
+ style: outputCanvasStyle
220
+ })
221
+ ]
155
222
  });
156
223
  };
157
224
  var makeHtmlInCanvasPresentation = (shader) => {
@@ -173,6 +240,11 @@ var makeHtmlInCanvasPresentation = (shader) => {
173
240
  };
174
241
  };
175
242
 
243
+ // src/presentations/upload-element-image.ts
244
+ var uploadElementImage = (gl, elementImage) => {
245
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, elementImage);
246
+ };
247
+
176
248
  // src/presentations/book-flip.tsx
177
249
  var DIRECTION_FROM_LEFT = 0;
178
250
  var DIRECTION_FROM_RIGHT = 1;
@@ -408,13 +480,13 @@ var bookFlipShader = (canvas) => {
408
480
  gl.activeTexture(gl.TEXTURE0);
409
481
  gl.bindTexture(gl.TEXTURE_2D, prevTex);
410
482
  if (prevImage) {
411
- gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, prevImage);
483
+ uploadElementImage(gl, prevImage);
412
484
  }
413
485
  gl.uniform1i(uPrev, 0);
414
486
  gl.activeTexture(gl.TEXTURE1);
415
487
  gl.bindTexture(gl.TEXTURE_2D, nextTex);
416
488
  if (nextImage) {
417
- gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nextImage);
489
+ uploadElementImage(gl, nextImage);
418
490
  }
419
491
  gl.uniform1i(uNext, 1);
420
492
  gl.uniform1f(uTime, effectiveTime);
@@ -7,7 +7,7 @@ import {
7
7
  Internals,
8
8
  useDelayRender
9
9
  } from "remotion";
10
- import { jsx } from "react/jsx-runtime";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
11
  var HtmlInCanvasPresentation = ({
12
12
  children,
13
13
  onElementImage,
@@ -23,6 +23,7 @@ var HtmlInCanvasPresentation = ({
23
23
  throw new Error(HTML_IN_CANVAS_UNSUPPORTED_MESSAGE);
24
24
  }
25
25
  const canvasRef = useRef(null);
26
+ const outputCanvasRef = useRef(null);
26
27
  const canvasSubtreeStyle = useMemo(() => {
27
28
  return {
28
29
  width: "100%",
@@ -34,7 +35,21 @@ var HtmlInCanvasPresentation = ({
34
35
  bottom: 0
35
36
  };
36
37
  }, []);
37
- const [offscreenCanvas] = useState(() => new OffscreenCanvas(1, 1));
38
+ const outputCanvasStyle = useMemo(() => {
39
+ return {
40
+ width: "100%",
41
+ height: "100%",
42
+ position: "absolute",
43
+ top: 0,
44
+ left: 0,
45
+ right: 0,
46
+ bottom: 0,
47
+ pointerEvents: "none"
48
+ };
49
+ }, []);
50
+ const captureCanvasRef = useRef(null);
51
+ const captureContextRef = useRef(null);
52
+ const [shaderCanvas] = useState(() => new OffscreenCanvas(1, 1));
38
53
  const passedPropsRef = useRef(passedProps);
39
54
  passedPropsRef.current = passedProps;
40
55
  const memoizedEffects = Internals.useMemoizedEffects({
@@ -43,33 +58,58 @@ var HtmlInCanvasPresentation = ({
43
58
  });
44
59
  const effectsRef = useRef(memoizedEffects);
45
60
  effectsRef.current = memoizedEffects;
46
- const [instance] = useState(() => shader(offscreenCanvas));
61
+ const [instance] = useState(() => shader(shaderCanvas));
47
62
  useLayoutEffect(() => {
63
+ const canvas = canvasRef.current;
64
+ if (!canvas) {
65
+ return () => {
66
+ instance.cleanup();
67
+ };
68
+ }
69
+ const captureCanvas = canvas.transferControlToOffscreen();
70
+ const captureContext = captureCanvas.getContext("2d");
71
+ if (!captureContext) {
72
+ throw new Error("Failed to create capture canvas context");
73
+ }
74
+ captureCanvasRef.current = captureCanvas;
75
+ captureContextRef.current = captureContext;
48
76
  return () => {
49
77
  instance.cleanup();
78
+ captureCanvasRef.current = null;
79
+ captureContextRef.current = null;
50
80
  };
51
- }, [offscreenCanvas, instance]);
81
+ }, [instance]);
52
82
  const chainState = Internals.useEffectChainState();
53
83
  const { delayRender, continueRender } = useDelayRender();
54
84
  const draw = useCallback(async (prevImage, nextImage, progress) => {
55
- if (!canvasRef.current) {
85
+ const outputCanvas = outputCanvasRef.current;
86
+ if (!outputCanvas) {
56
87
  throw new Error("Canvas not found");
57
88
  }
58
89
  const handle = delayRender("onPaint");
90
+ const clearOutput = () => {
91
+ const context = outputCanvas.getContext("2d");
92
+ if (!context) {
93
+ throw new Error("Failed to create output canvas context");
94
+ }
95
+ context.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
96
+ };
59
97
  if (!prevImage && !nextImage) {
60
- continueRender(handle);
61
98
  instance.clear();
99
+ clearOutput();
100
+ continueRender(handle);
62
101
  return;
63
102
  }
64
103
  const width = prevImage?.width ?? nextImage?.width ?? 0;
65
104
  const height = prevImage?.height ?? nextImage?.height ?? 0;
66
105
  if (width === 0 || height === 0) {
67
- continueRender(handle);
68
106
  instance.clear();
107
+ clearOutput();
108
+ continueRender(handle);
69
109
  return;
70
110
  }
71
- offscreenCanvas.width = width;
72
- offscreenCanvas.height = height;
111
+ shaderCanvas.width = width;
112
+ shaderCanvas.height = height;
73
113
  instance.draw({
74
114
  prevImage,
75
115
  nextImage,
@@ -80,14 +120,14 @@ var HtmlInCanvasPresentation = ({
80
120
  });
81
121
  await Internals.runEffectChain({
82
122
  state: chainState.get(width, height),
83
- source: offscreenCanvas,
123
+ source: shaderCanvas,
84
124
  effects: effectsRef.current ?? [],
85
125
  width,
86
126
  height,
87
- output: canvasRef.current
127
+ output: outputCanvas
88
128
  });
89
129
  continueRender(handle);
90
- }, [chainState, instance, offscreenCanvas, continueRender, delayRender]);
130
+ }, [chainState, continueRender, delayRender, instance, shaderCanvas]);
91
131
  const passThrough = bothEnteringAndExiting && presentationDirection === "exiting";
92
132
  useLayoutEffect(() => {
93
133
  if (passThrough) {
@@ -100,11 +140,19 @@ var HtmlInCanvasPresentation = ({
100
140
  canvas.layoutSubtree = true;
101
141
  const onPaint = () => {
102
142
  const firstChild = canvas.firstChild;
103
- if (!firstChild) {
143
+ const captureCanvas = captureCanvasRef.current;
144
+ const captureContext = captureContextRef.current;
145
+ if (!firstChild || !captureCanvas || !captureContext) {
104
146
  return;
105
147
  }
106
148
  const elementImage = canvas.captureElementImage(firstChild);
107
- onElementImage(elementImage, draw);
149
+ try {
150
+ captureContext.reset();
151
+ captureContext.drawElementImage(elementImage, 0, 0);
152
+ } finally {
153
+ elementImage.close();
154
+ }
155
+ onElementImage(captureCanvas, draw);
108
156
  };
109
157
  canvas.addEventListener("paint", onPaint);
110
158
  return () => {
@@ -138,20 +186,39 @@ var HtmlInCanvasPresentation = ({
138
186
  return;
139
187
  }
140
188
  const observer = new ResizeObserver(([entry]) => {
141
- canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
142
- canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
189
+ const outputCanvas = outputCanvasRef.current;
190
+ const captureCanvas = captureCanvasRef.current;
191
+ if (!outputCanvas || !captureCanvas) {
192
+ return;
193
+ }
194
+ const width = entry.devicePixelContentBoxSize[0].inlineSize;
195
+ const height = entry.devicePixelContentBoxSize[0].blockSize;
196
+ captureCanvas.width = width;
197
+ captureCanvas.height = height;
198
+ outputCanvas.width = width;
199
+ outputCanvas.height = height;
200
+ canvas.requestPaint?.();
143
201
  });
144
202
  observer.observe(canvas, { box: "device-pixel-content-box" });
203
+ return () => {
204
+ observer.disconnect();
205
+ };
145
206
  }, [passThrough]);
146
207
  if (passThrough) {
147
208
  return children;
148
209
  }
149
- return /* @__PURE__ */ jsx(AbsoluteFill, {
150
- children: /* @__PURE__ */ jsx("canvas", {
151
- ref: canvasRef,
152
- style: canvasSubtreeStyle,
153
- children
154
- })
210
+ return /* @__PURE__ */ jsxs(AbsoluteFill, {
211
+ children: [
212
+ /* @__PURE__ */ jsx("canvas", {
213
+ ref: canvasRef,
214
+ style: canvasSubtreeStyle,
215
+ children
216
+ }),
217
+ /* @__PURE__ */ jsx("canvas", {
218
+ ref: outputCanvasRef,
219
+ style: outputCanvasStyle
220
+ })
221
+ ]
155
222
  });
156
223
  };
157
224
  var makeHtmlInCanvasPresentation = (shader) => {
@@ -173,6 +240,11 @@ var makeHtmlInCanvasPresentation = (shader) => {
173
240
  };
174
241
  };
175
242
 
243
+ // src/presentations/upload-element-image.ts
244
+ var uploadElementImage = (gl, elementImage) => {
245
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, elementImage);
246
+ };
247
+
176
248
  // src/presentations/cross-zoom.tsx
177
249
  var DEFAULT_STRENGTH = 0.4;
178
250
  var VERTEX_SHADER = `#version 300 es
@@ -351,13 +423,13 @@ var crossZoomShader = (canvas) => {
351
423
  gl.activeTexture(gl.TEXTURE0);
352
424
  gl.bindTexture(gl.TEXTURE_2D, prevTex);
353
425
  if (prevImage) {
354
- gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, prevImage);
426
+ uploadElementImage(gl, prevImage);
355
427
  }
356
428
  gl.uniform1i(uPrev, 0);
357
429
  gl.activeTexture(gl.TEXTURE1);
358
430
  gl.bindTexture(gl.TEXTURE_2D, nextTex);
359
431
  if (nextImage) {
360
- gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nextImage);
432
+ uploadElementImage(gl, nextImage);
361
433
  }
362
434
  gl.uniform1i(uNext, 1);
363
435
  gl.uniform1f(uTime, effectiveTime);
@@ -7,7 +7,7 @@ import {
7
7
  Internals,
8
8
  useDelayRender
9
9
  } from "remotion";
10
- import { jsx } from "react/jsx-runtime";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
11
  var HtmlInCanvasPresentation = ({
12
12
  children,
13
13
  onElementImage,
@@ -23,6 +23,7 @@ var HtmlInCanvasPresentation = ({
23
23
  throw new Error(HTML_IN_CANVAS_UNSUPPORTED_MESSAGE);
24
24
  }
25
25
  const canvasRef = useRef(null);
26
+ const outputCanvasRef = useRef(null);
26
27
  const canvasSubtreeStyle = useMemo(() => {
27
28
  return {
28
29
  width: "100%",
@@ -34,7 +35,21 @@ var HtmlInCanvasPresentation = ({
34
35
  bottom: 0
35
36
  };
36
37
  }, []);
37
- const [offscreenCanvas] = useState(() => new OffscreenCanvas(1, 1));
38
+ const outputCanvasStyle = useMemo(() => {
39
+ return {
40
+ width: "100%",
41
+ height: "100%",
42
+ position: "absolute",
43
+ top: 0,
44
+ left: 0,
45
+ right: 0,
46
+ bottom: 0,
47
+ pointerEvents: "none"
48
+ };
49
+ }, []);
50
+ const captureCanvasRef = useRef(null);
51
+ const captureContextRef = useRef(null);
52
+ const [shaderCanvas] = useState(() => new OffscreenCanvas(1, 1));
38
53
  const passedPropsRef = useRef(passedProps);
39
54
  passedPropsRef.current = passedProps;
40
55
  const memoizedEffects = Internals.useMemoizedEffects({
@@ -43,33 +58,58 @@ var HtmlInCanvasPresentation = ({
43
58
  });
44
59
  const effectsRef = useRef(memoizedEffects);
45
60
  effectsRef.current = memoizedEffects;
46
- const [instance] = useState(() => shader(offscreenCanvas));
61
+ const [instance] = useState(() => shader(shaderCanvas));
47
62
  useLayoutEffect(() => {
63
+ const canvas = canvasRef.current;
64
+ if (!canvas) {
65
+ return () => {
66
+ instance.cleanup();
67
+ };
68
+ }
69
+ const captureCanvas = canvas.transferControlToOffscreen();
70
+ const captureContext = captureCanvas.getContext("2d");
71
+ if (!captureContext) {
72
+ throw new Error("Failed to create capture canvas context");
73
+ }
74
+ captureCanvasRef.current = captureCanvas;
75
+ captureContextRef.current = captureContext;
48
76
  return () => {
49
77
  instance.cleanup();
78
+ captureCanvasRef.current = null;
79
+ captureContextRef.current = null;
50
80
  };
51
- }, [offscreenCanvas, instance]);
81
+ }, [instance]);
52
82
  const chainState = Internals.useEffectChainState();
53
83
  const { delayRender, continueRender } = useDelayRender();
54
84
  const draw = useCallback(async (prevImage, nextImage, progress) => {
55
- if (!canvasRef.current) {
85
+ const outputCanvas = outputCanvasRef.current;
86
+ if (!outputCanvas) {
56
87
  throw new Error("Canvas not found");
57
88
  }
58
89
  const handle = delayRender("onPaint");
90
+ const clearOutput = () => {
91
+ const context = outputCanvas.getContext("2d");
92
+ if (!context) {
93
+ throw new Error("Failed to create output canvas context");
94
+ }
95
+ context.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
96
+ };
59
97
  if (!prevImage && !nextImage) {
60
- continueRender(handle);
61
98
  instance.clear();
99
+ clearOutput();
100
+ continueRender(handle);
62
101
  return;
63
102
  }
64
103
  const width = prevImage?.width ?? nextImage?.width ?? 0;
65
104
  const height = prevImage?.height ?? nextImage?.height ?? 0;
66
105
  if (width === 0 || height === 0) {
67
- continueRender(handle);
68
106
  instance.clear();
107
+ clearOutput();
108
+ continueRender(handle);
69
109
  return;
70
110
  }
71
- offscreenCanvas.width = width;
72
- offscreenCanvas.height = height;
111
+ shaderCanvas.width = width;
112
+ shaderCanvas.height = height;
73
113
  instance.draw({
74
114
  prevImage,
75
115
  nextImage,
@@ -80,14 +120,14 @@ var HtmlInCanvasPresentation = ({
80
120
  });
81
121
  await Internals.runEffectChain({
82
122
  state: chainState.get(width, height),
83
- source: offscreenCanvas,
123
+ source: shaderCanvas,
84
124
  effects: effectsRef.current ?? [],
85
125
  width,
86
126
  height,
87
- output: canvasRef.current
127
+ output: outputCanvas
88
128
  });
89
129
  continueRender(handle);
90
- }, [chainState, instance, offscreenCanvas, continueRender, delayRender]);
130
+ }, [chainState, continueRender, delayRender, instance, shaderCanvas]);
91
131
  const passThrough = bothEnteringAndExiting && presentationDirection === "exiting";
92
132
  useLayoutEffect(() => {
93
133
  if (passThrough) {
@@ -100,11 +140,19 @@ var HtmlInCanvasPresentation = ({
100
140
  canvas.layoutSubtree = true;
101
141
  const onPaint = () => {
102
142
  const firstChild = canvas.firstChild;
103
- if (!firstChild) {
143
+ const captureCanvas = captureCanvasRef.current;
144
+ const captureContext = captureContextRef.current;
145
+ if (!firstChild || !captureCanvas || !captureContext) {
104
146
  return;
105
147
  }
106
148
  const elementImage = canvas.captureElementImage(firstChild);
107
- onElementImage(elementImage, draw);
149
+ try {
150
+ captureContext.reset();
151
+ captureContext.drawElementImage(elementImage, 0, 0);
152
+ } finally {
153
+ elementImage.close();
154
+ }
155
+ onElementImage(captureCanvas, draw);
108
156
  };
109
157
  canvas.addEventListener("paint", onPaint);
110
158
  return () => {
@@ -138,20 +186,39 @@ var HtmlInCanvasPresentation = ({
138
186
  return;
139
187
  }
140
188
  const observer = new ResizeObserver(([entry]) => {
141
- canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
142
- canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
189
+ const outputCanvas = outputCanvasRef.current;
190
+ const captureCanvas = captureCanvasRef.current;
191
+ if (!outputCanvas || !captureCanvas) {
192
+ return;
193
+ }
194
+ const width = entry.devicePixelContentBoxSize[0].inlineSize;
195
+ const height = entry.devicePixelContentBoxSize[0].blockSize;
196
+ captureCanvas.width = width;
197
+ captureCanvas.height = height;
198
+ outputCanvas.width = width;
199
+ outputCanvas.height = height;
200
+ canvas.requestPaint?.();
143
201
  });
144
202
  observer.observe(canvas, { box: "device-pixel-content-box" });
203
+ return () => {
204
+ observer.disconnect();
205
+ };
145
206
  }, [passThrough]);
146
207
  if (passThrough) {
147
208
  return children;
148
209
  }
149
- return /* @__PURE__ */ jsx(AbsoluteFill, {
150
- children: /* @__PURE__ */ jsx("canvas", {
151
- ref: canvasRef,
152
- style: canvasSubtreeStyle,
153
- children
154
- })
210
+ return /* @__PURE__ */ jsxs(AbsoluteFill, {
211
+ children: [
212
+ /* @__PURE__ */ jsx("canvas", {
213
+ ref: canvasRef,
214
+ style: canvasSubtreeStyle,
215
+ children
216
+ }),
217
+ /* @__PURE__ */ jsx("canvas", {
218
+ ref: outputCanvasRef,
219
+ style: outputCanvasStyle
220
+ })
221
+ ]
155
222
  });
156
223
  };
157
224
  var makeHtmlInCanvasPresentation = (shader) => {
@@ -173,6 +240,11 @@ var makeHtmlInCanvasPresentation = (shader) => {
173
240
  };
174
241
  };
175
242
 
243
+ // src/presentations/upload-element-image.ts
244
+ var uploadElementImage = (gl, elementImage) => {
245
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, elementImage);
246
+ };
247
+
176
248
  // src/presentations/crosswarp.tsx
177
249
  var VERTEX_SHADER = `#version 300 es
178
250
  in vec2 a_pos;
@@ -306,13 +378,13 @@ var crosswarpShader = (canvas) => {
306
378
  gl.activeTexture(gl.TEXTURE0);
307
379
  gl.bindTexture(gl.TEXTURE_2D, prevTex);
308
380
  if (prevImage) {
309
- gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, prevImage);
381
+ uploadElementImage(gl, prevImage);
310
382
  }
311
383
  gl.uniform1i(uPrev, 0);
312
384
  gl.activeTexture(gl.TEXTURE1);
313
385
  gl.bindTexture(gl.TEXTURE_2D, nextTex);
314
386
  if (nextImage) {
315
- gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nextImage);
387
+ uploadElementImage(gl, nextImage);
316
388
  }
317
389
  gl.uniform1i(uNext, 1);
318
390
  gl.uniform1f(uTime, effectiveTime);