@remotion/transitions 4.0.464 → 4.0.466

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.
Files changed (39) hide show
  1. package/book-flip.js +2 -0
  2. package/crosswarp.js +2 -0
  3. package/dissolve.js +2 -0
  4. package/dist/TransitionSeries.js +16 -6
  5. package/dist/esm/book-flip.mjs +433 -0
  6. package/dist/esm/crosswarp.mjs +330 -0
  7. package/dist/esm/dissolve.mjs +376 -0
  8. package/dist/esm/dreamy-zoom.mjs +347 -0
  9. package/dist/esm/index.mjs +553 -209
  10. package/dist/esm/linear-blur.mjs +342 -0
  11. package/dist/esm/ripple.mjs +341 -0
  12. package/dist/esm/swap.mjs +393 -0
  13. package/dist/esm/zoom-blur.mjs +4 -4
  14. package/dist/esm/zoom-in-out.mjs +4 -4
  15. package/dist/html-in-canvas-presentation.d.ts +4 -4
  16. package/dist/html-in-canvas-presentation.js +4 -4
  17. package/dist/index.d.ts +9 -3
  18. package/dist/index.js +5 -1
  19. package/dist/presentations/book-flip.d.ts +14 -0
  20. package/dist/presentations/book-flip.js +255 -0
  21. package/dist/presentations/crosswarp.d.ts +11 -0
  22. package/dist/presentations/crosswarp.js +154 -0
  23. package/dist/presentations/dissolve.d.ts +17 -0
  24. package/dist/presentations/dissolve.js +193 -0
  25. package/dist/presentations/dreamy-zoom.d.ts +14 -0
  26. package/dist/presentations/dreamy-zoom.js +169 -0
  27. package/dist/presentations/linear-blur.d.ts +13 -0
  28. package/dist/presentations/linear-blur.js +164 -0
  29. package/dist/presentations/ripple.d.ts +14 -0
  30. package/dist/presentations/ripple.js +164 -0
  31. package/dist/presentations/swap.d.ts +15 -0
  32. package/dist/presentations/swap.js +212 -0
  33. package/dist/presentations/zoom-blur.d.ts +2 -2
  34. package/dist/presentations/zoom-in-out.d.ts +2 -2
  35. package/dreamy-zoom.js +2 -0
  36. package/linear-blur.js +2 -0
  37. package/package.json +71 -8
  38. package/ripple.js +2 -0
  39. package/swap.js +2 -0
@@ -0,0 +1,330 @@
1
+ // src/html-in-canvas-presentation.tsx
2
+ import { useLayoutEffect, useMemo, useRef, useState, useCallback } from "react";
3
+ import {
4
+ HtmlInCanvas,
5
+ HTML_IN_CANVAS_UNSUPPORTED_MESSAGE,
6
+ useDelayRender
7
+ } from "remotion";
8
+ import { AbsoluteFill, Internals } from "remotion";
9
+ import { jsx } from "react/jsx-runtime";
10
+ var HtmlInCanvasPresentation = ({
11
+ children,
12
+ onElementImage,
13
+ onUnmount,
14
+ presentationProgress,
15
+ presentationDirection,
16
+ shader,
17
+ effects,
18
+ passedProps,
19
+ bothEnteringAndExiting
20
+ }) => {
21
+ if (!HtmlInCanvas.isSupported()) {
22
+ throw new Error(HTML_IN_CANVAS_UNSUPPORTED_MESSAGE);
23
+ }
24
+ const canvasRef = useRef(null);
25
+ const canvasSubtreeStyle = useMemo(() => {
26
+ return {
27
+ width: "100%",
28
+ height: "100%",
29
+ position: "absolute",
30
+ top: 0,
31
+ left: 0,
32
+ right: 0,
33
+ bottom: 0
34
+ };
35
+ }, []);
36
+ const [offscreenCanvas] = useState(() => new OffscreenCanvas(1, 1));
37
+ const passedPropsRef = useRef(passedProps);
38
+ passedPropsRef.current = passedProps;
39
+ const memoizedEffects = Internals.useMemoizedEffects({
40
+ effects: effects ?? [],
41
+ overrideId: null
42
+ });
43
+ const effectsRef = useRef(memoizedEffects);
44
+ effectsRef.current = memoizedEffects;
45
+ const [instance] = useState(() => shader(offscreenCanvas));
46
+ useLayoutEffect(() => {
47
+ return () => {
48
+ instance.cleanup();
49
+ };
50
+ }, [offscreenCanvas, instance]);
51
+ const chainState = Internals.useEffectChainState();
52
+ const { delayRender, continueRender } = useDelayRender();
53
+ const draw = useCallback(async (prevImage, nextImage, progress) => {
54
+ if (!canvasRef.current) {
55
+ throw new Error("Canvas not found");
56
+ }
57
+ const handle = delayRender("onPaint");
58
+ if (!prevImage && !nextImage) {
59
+ continueRender(handle);
60
+ instance.clear();
61
+ return;
62
+ }
63
+ const width = prevImage?.width ?? nextImage?.width ?? 0;
64
+ const height = prevImage?.height ?? nextImage?.height ?? 0;
65
+ if (width === 0 || height === 0) {
66
+ continueRender(handle);
67
+ instance.clear();
68
+ return;
69
+ }
70
+ offscreenCanvas.width = width;
71
+ offscreenCanvas.height = height;
72
+ instance.draw({
73
+ prevImage,
74
+ nextImage,
75
+ width,
76
+ height,
77
+ time: progress,
78
+ passedProps: passedPropsRef.current
79
+ });
80
+ await Internals.runEffectChain({
81
+ state: chainState.get(width, height),
82
+ source: offscreenCanvas,
83
+ effects: effectsRef.current ?? [],
84
+ width,
85
+ height,
86
+ output: canvasRef.current
87
+ });
88
+ continueRender(handle);
89
+ }, [chainState, instance, offscreenCanvas, continueRender, delayRender]);
90
+ const passThrough = bothEnteringAndExiting && presentationDirection === "exiting";
91
+ useLayoutEffect(() => {
92
+ if (passThrough) {
93
+ return;
94
+ }
95
+ const canvas = canvasRef.current;
96
+ if (!canvas) {
97
+ throw new Error("Canvas not found");
98
+ }
99
+ canvas.layoutSubtree = true;
100
+ const onPaint = () => {
101
+ const firstChild = canvas.firstChild;
102
+ if (!firstChild) {
103
+ return;
104
+ }
105
+ const elementImage = canvas.captureElementImage(firstChild);
106
+ onElementImage(elementImage, draw);
107
+ };
108
+ canvas.addEventListener("paint", onPaint);
109
+ return () => {
110
+ canvas.removeEventListener("paint", onPaint);
111
+ };
112
+ }, [onElementImage, presentationDirection, draw, passThrough]);
113
+ useLayoutEffect(() => {
114
+ if (passThrough) {
115
+ return;
116
+ }
117
+ const canvas = canvasRef.current;
118
+ if (!canvas) {
119
+ throw new Error("Canvas not found");
120
+ }
121
+ canvas.requestPaint?.();
122
+ }, [presentationProgress, passThrough, memoizedEffects]);
123
+ useLayoutEffect(() => {
124
+ if (passThrough) {
125
+ return;
126
+ }
127
+ return () => {
128
+ onUnmount();
129
+ };
130
+ }, [onUnmount, passThrough]);
131
+ useLayoutEffect(() => {
132
+ if (passThrough) {
133
+ return;
134
+ }
135
+ const canvas = canvasRef.current;
136
+ if (!canvas) {
137
+ return;
138
+ }
139
+ const observer = new ResizeObserver(([entry]) => {
140
+ canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
141
+ canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
142
+ });
143
+ observer.observe(canvas, { box: "device-pixel-content-box" });
144
+ }, [passThrough]);
145
+ if (passThrough) {
146
+ return children;
147
+ }
148
+ return /* @__PURE__ */ jsx(AbsoluteFill, {
149
+ children: /* @__PURE__ */ jsx("canvas", {
150
+ ref: canvasRef,
151
+ style: canvasSubtreeStyle,
152
+ children
153
+ })
154
+ });
155
+ };
156
+ var makeHtmlInCanvasPresentation = (shader) => {
157
+ const CompWithShader = (props) => {
158
+ const { passedProps, ...otherProps } = props;
159
+ const { effects, ...restPassedProps } = props.passedProps;
160
+ return /* @__PURE__ */ jsx(HtmlInCanvasPresentation, {
161
+ shader,
162
+ passedProps: restPassedProps,
163
+ effects,
164
+ ...otherProps
165
+ });
166
+ };
167
+ return (props) => {
168
+ return {
169
+ component: CompWithShader,
170
+ props
171
+ };
172
+ };
173
+ };
174
+
175
+ // src/presentations/crosswarp.tsx
176
+ var VERTEX_SHADER = `#version 300 es
177
+ in vec2 a_pos;
178
+ out vec2 v_uv;
179
+ void main() {
180
+ v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5);
181
+ gl_Position = vec4(a_pos, 0.0, 1.0);
182
+ }`;
183
+ var FRAGMENT_SHADER = `#version 300 es
184
+ precision highp float;
185
+
186
+ uniform sampler2D u_prev;
187
+ uniform sampler2D u_next;
188
+ uniform float u_time;
189
+
190
+ in vec2 v_uv;
191
+ out vec4 outColor;
192
+
193
+ vec4 transition(vec2 uv, float progress) {
194
+ float x = progress;
195
+ x = smoothstep(0.0, 1.0, x * 2.0 + uv.x - 1.0);
196
+ return mix(
197
+ texture(u_next, (uv - 0.5) * (1.0 - x) + 0.5),
198
+ texture(u_prev, (uv - 0.5) * x + 0.5),
199
+ x
200
+ );
201
+ }
202
+
203
+ void main() {
204
+ // In Remotion's HTML-in-canvas convention, u_prev is bound to the incoming
205
+ // scene and u_next is bound to the outgoing scene, so the gl-transitions
206
+ // "from" → u_next and "to" → u_prev. With this binding, progress = u_time
207
+ // (no inversion) maps to the gl-transitions convention where progress = 0
208
+ // shows the outgoing scene and progress = 1 shows the incoming one.
209
+ float progress = u_time;
210
+ outColor = transition(v_uv, progress);
211
+ }`;
212
+ var compileShader = (gl, source, type) => {
213
+ const shader = gl.createShader(type);
214
+ if (!shader) {
215
+ throw new Error("Failed to create shader");
216
+ }
217
+ gl.shaderSource(shader, source);
218
+ gl.compileShader(shader);
219
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
220
+ const log = gl.getShaderInfoLog(shader);
221
+ gl.deleteShader(shader);
222
+ throw new Error(`Failed to compile shader: ${log}`);
223
+ }
224
+ return shader;
225
+ };
226
+ var createProgram = (gl) => {
227
+ const program = gl.createProgram();
228
+ if (!program) {
229
+ throw new Error("Failed to create WebGL program");
230
+ }
231
+ const vs = compileShader(gl, VERTEX_SHADER, gl.VERTEX_SHADER);
232
+ const fs = compileShader(gl, FRAGMENT_SHADER, gl.FRAGMENT_SHADER);
233
+ gl.attachShader(program, vs);
234
+ gl.attachShader(program, fs);
235
+ gl.linkProgram(program);
236
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
237
+ const log = gl.getProgramInfoLog(program);
238
+ gl.deleteProgram(program);
239
+ throw new Error(`Failed to link program: ${log}`);
240
+ }
241
+ return program;
242
+ };
243
+ var createTexture = (gl) => {
244
+ const tex = gl.createTexture();
245
+ if (!tex) {
246
+ throw new Error("Failed to create texture");
247
+ }
248
+ gl.bindTexture(gl.TEXTURE_2D, tex);
249
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
250
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
251
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
252
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
253
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
254
+ return tex;
255
+ };
256
+ var crosswarpShader = (canvas) => {
257
+ const gl = canvas.getContext("webgl2", { premultipliedAlpha: true });
258
+ if (!gl) {
259
+ throw new Error("Failed to create WebGL2 context");
260
+ }
261
+ const program = createProgram(gl);
262
+ const prevTex = createTexture(gl);
263
+ const nextTex = createTexture(gl);
264
+ const vao = gl.createVertexArray();
265
+ gl.bindVertexArray(vao);
266
+ const buffer = gl.createBuffer();
267
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
268
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
269
+ const aPos = gl.getAttribLocation(program, "a_pos");
270
+ gl.enableVertexAttribArray(aPos);
271
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
272
+ const uTime = gl.getUniformLocation(program, "u_time");
273
+ const uPrev = gl.getUniformLocation(program, "u_prev");
274
+ const uNext = gl.getUniformLocation(program, "u_next");
275
+ const cleanup = () => {
276
+ gl.deleteProgram(program);
277
+ gl.deleteTexture(prevTex);
278
+ gl.deleteTexture(nextTex);
279
+ };
280
+ const clear = () => {
281
+ gl.clearColor(0, 0, 0, 0);
282
+ gl.clear(gl.COLOR_BUFFER_BIT);
283
+ };
284
+ const draw = ({
285
+ prevImage,
286
+ nextImage,
287
+ width,
288
+ height,
289
+ time
290
+ }) => {
291
+ if (!prevImage && !nextImage) {
292
+ return;
293
+ }
294
+ if (prevImage && (prevImage.width === 0 || prevImage.height === 0)) {
295
+ return;
296
+ }
297
+ if (nextImage && (nextImage.width === 0 || nextImage.height === 0)) {
298
+ return;
299
+ }
300
+ const effectiveTime = !prevImage ? 0 : !nextImage ? 1 : time;
301
+ gl.viewport(0, 0, width, height);
302
+ gl.clearColor(0, 0, 0, 0);
303
+ gl.clear(gl.COLOR_BUFFER_BIT);
304
+ gl.useProgram(program);
305
+ gl.activeTexture(gl.TEXTURE0);
306
+ gl.bindTexture(gl.TEXTURE_2D, prevTex);
307
+ if (prevImage) {
308
+ gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, prevImage);
309
+ }
310
+ gl.uniform1i(uPrev, 0);
311
+ gl.activeTexture(gl.TEXTURE1);
312
+ gl.bindTexture(gl.TEXTURE_2D, nextTex);
313
+ if (nextImage) {
314
+ gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nextImage);
315
+ }
316
+ gl.uniform1i(uNext, 1);
317
+ gl.uniform1f(uTime, effectiveTime);
318
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
319
+ };
320
+ return {
321
+ clear,
322
+ cleanup,
323
+ draw
324
+ };
325
+ };
326
+ var crosswarp = makeHtmlInCanvasPresentation(crosswarpShader);
327
+ export {
328
+ crosswarpShader,
329
+ crosswarp
330
+ };
@@ -0,0 +1,376 @@
1
+ // src/html-in-canvas-presentation.tsx
2
+ import { useLayoutEffect, useMemo, useRef, useState, useCallback } from "react";
3
+ import {
4
+ HtmlInCanvas,
5
+ HTML_IN_CANVAS_UNSUPPORTED_MESSAGE,
6
+ useDelayRender
7
+ } from "remotion";
8
+ import { AbsoluteFill, Internals } from "remotion";
9
+ import { jsx } from "react/jsx-runtime";
10
+ var HtmlInCanvasPresentation = ({
11
+ children,
12
+ onElementImage,
13
+ onUnmount,
14
+ presentationProgress,
15
+ presentationDirection,
16
+ shader,
17
+ effects,
18
+ passedProps,
19
+ bothEnteringAndExiting
20
+ }) => {
21
+ if (!HtmlInCanvas.isSupported()) {
22
+ throw new Error(HTML_IN_CANVAS_UNSUPPORTED_MESSAGE);
23
+ }
24
+ const canvasRef = useRef(null);
25
+ const canvasSubtreeStyle = useMemo(() => {
26
+ return {
27
+ width: "100%",
28
+ height: "100%",
29
+ position: "absolute",
30
+ top: 0,
31
+ left: 0,
32
+ right: 0,
33
+ bottom: 0
34
+ };
35
+ }, []);
36
+ const [offscreenCanvas] = useState(() => new OffscreenCanvas(1, 1));
37
+ const passedPropsRef = useRef(passedProps);
38
+ passedPropsRef.current = passedProps;
39
+ const memoizedEffects = Internals.useMemoizedEffects({
40
+ effects: effects ?? [],
41
+ overrideId: null
42
+ });
43
+ const effectsRef = useRef(memoizedEffects);
44
+ effectsRef.current = memoizedEffects;
45
+ const [instance] = useState(() => shader(offscreenCanvas));
46
+ useLayoutEffect(() => {
47
+ return () => {
48
+ instance.cleanup();
49
+ };
50
+ }, [offscreenCanvas, instance]);
51
+ const chainState = Internals.useEffectChainState();
52
+ const { delayRender, continueRender } = useDelayRender();
53
+ const draw = useCallback(async (prevImage, nextImage, progress) => {
54
+ if (!canvasRef.current) {
55
+ throw new Error("Canvas not found");
56
+ }
57
+ const handle = delayRender("onPaint");
58
+ if (!prevImage && !nextImage) {
59
+ continueRender(handle);
60
+ instance.clear();
61
+ return;
62
+ }
63
+ const width = prevImage?.width ?? nextImage?.width ?? 0;
64
+ const height = prevImage?.height ?? nextImage?.height ?? 0;
65
+ if (width === 0 || height === 0) {
66
+ continueRender(handle);
67
+ instance.clear();
68
+ return;
69
+ }
70
+ offscreenCanvas.width = width;
71
+ offscreenCanvas.height = height;
72
+ instance.draw({
73
+ prevImage,
74
+ nextImage,
75
+ width,
76
+ height,
77
+ time: progress,
78
+ passedProps: passedPropsRef.current
79
+ });
80
+ await Internals.runEffectChain({
81
+ state: chainState.get(width, height),
82
+ source: offscreenCanvas,
83
+ effects: effectsRef.current ?? [],
84
+ width,
85
+ height,
86
+ output: canvasRef.current
87
+ });
88
+ continueRender(handle);
89
+ }, [chainState, instance, offscreenCanvas, continueRender, delayRender]);
90
+ const passThrough = bothEnteringAndExiting && presentationDirection === "exiting";
91
+ useLayoutEffect(() => {
92
+ if (passThrough) {
93
+ return;
94
+ }
95
+ const canvas = canvasRef.current;
96
+ if (!canvas) {
97
+ throw new Error("Canvas not found");
98
+ }
99
+ canvas.layoutSubtree = true;
100
+ const onPaint = () => {
101
+ const firstChild = canvas.firstChild;
102
+ if (!firstChild) {
103
+ return;
104
+ }
105
+ const elementImage = canvas.captureElementImage(firstChild);
106
+ onElementImage(elementImage, draw);
107
+ };
108
+ canvas.addEventListener("paint", onPaint);
109
+ return () => {
110
+ canvas.removeEventListener("paint", onPaint);
111
+ };
112
+ }, [onElementImage, presentationDirection, draw, passThrough]);
113
+ useLayoutEffect(() => {
114
+ if (passThrough) {
115
+ return;
116
+ }
117
+ const canvas = canvasRef.current;
118
+ if (!canvas) {
119
+ throw new Error("Canvas not found");
120
+ }
121
+ canvas.requestPaint?.();
122
+ }, [presentationProgress, passThrough, memoizedEffects]);
123
+ useLayoutEffect(() => {
124
+ if (passThrough) {
125
+ return;
126
+ }
127
+ return () => {
128
+ onUnmount();
129
+ };
130
+ }, [onUnmount, passThrough]);
131
+ useLayoutEffect(() => {
132
+ if (passThrough) {
133
+ return;
134
+ }
135
+ const canvas = canvasRef.current;
136
+ if (!canvas) {
137
+ return;
138
+ }
139
+ const observer = new ResizeObserver(([entry]) => {
140
+ canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
141
+ canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
142
+ });
143
+ observer.observe(canvas, { box: "device-pixel-content-box" });
144
+ }, [passThrough]);
145
+ if (passThrough) {
146
+ return children;
147
+ }
148
+ return /* @__PURE__ */ jsx(AbsoluteFill, {
149
+ children: /* @__PURE__ */ jsx("canvas", {
150
+ ref: canvasRef,
151
+ style: canvasSubtreeStyle,
152
+ children
153
+ })
154
+ });
155
+ };
156
+ var makeHtmlInCanvasPresentation = (shader) => {
157
+ const CompWithShader = (props) => {
158
+ const { passedProps, ...otherProps } = props;
159
+ const { effects, ...restPassedProps } = props.passedProps;
160
+ return /* @__PURE__ */ jsx(HtmlInCanvasPresentation, {
161
+ shader,
162
+ passedProps: restPassedProps,
163
+ effects,
164
+ ...otherProps
165
+ });
166
+ };
167
+ return (props) => {
168
+ return {
169
+ component: CompWithShader,
170
+ props
171
+ };
172
+ };
173
+ };
174
+
175
+ // src/presentations/dissolve.tsx
176
+ var DEFAULT_LINE_WIDTH = 0.1;
177
+ var DEFAULT_SPREAD_COLOR = "#ff0000";
178
+ var DEFAULT_HOT_COLOR = "#e6e633";
179
+ var DEFAULT_POW = 5;
180
+ var DEFAULT_INTENSITY = 1;
181
+ var parseHexColor = (hex) => {
182
+ const cleaned = hex.startsWith("#") ? hex.slice(1) : hex;
183
+ if (!/^[0-9a-fA-F]{6}$/.test(cleaned)) {
184
+ throw new Error(`Invalid color "${hex}" passed to dissolve(). Expected a "#rrggbb" hex string.`);
185
+ }
186
+ return [
187
+ parseInt(cleaned.slice(0, 2), 16) / 255,
188
+ parseInt(cleaned.slice(2, 4), 16) / 255,
189
+ parseInt(cleaned.slice(4, 6), 16) / 255
190
+ ];
191
+ };
192
+ var VERTEX_SHADER = `#version 300 es
193
+ in vec2 a_pos;
194
+ out vec2 v_uv;
195
+ void main() {
196
+ v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5);
197
+ gl_Position = vec4(a_pos, 0.0, 1.0);
198
+ }`;
199
+ var FRAGMENT_SHADER = `#version 300 es
200
+ precision highp float;
201
+
202
+ uniform sampler2D u_prev;
203
+ uniform sampler2D u_next;
204
+ uniform float u_time;
205
+ uniform float u_line_width;
206
+ uniform vec3 u_spread_color;
207
+ uniform vec3 u_hot_color;
208
+ uniform float u_pow;
209
+ uniform float u_intensity;
210
+
211
+ in vec2 v_uv;
212
+ out vec4 outColor;
213
+
214
+ vec4 transition(vec2 uv, float progress) {
215
+ vec4 from = texture(u_next, uv);
216
+ vec4 to = texture(u_prev, uv);
217
+ float burn = 0.5 + 0.5 * (0.299 * from.r + 0.587 * from.g + 0.114 * from.b);
218
+ float show = burn - progress;
219
+ if (show < 0.001) {
220
+ return to;
221
+ }
222
+ float factor = 1.0 - smoothstep(0.0, u_line_width, show);
223
+ vec3 burnColor = mix(u_spread_color, u_hot_color, factor);
224
+ burnColor = pow(burnColor, vec3(u_pow)) * u_intensity;
225
+ vec3 finalRGB = mix(from.rgb, burnColor, factor * step(0.0001, progress));
226
+ return vec4(finalRGB, from.a);
227
+ }
228
+
229
+ void main() {
230
+ // In Remotion's HTML-in-canvas convention, u_prev is bound to the incoming
231
+ // scene and u_next is bound to the outgoing scene, so the gl-transitions
232
+ // "from" → u_next and "to" → u_prev. With this binding, progress = u_time
233
+ // (no inversion) maps to the gl-transitions convention where progress = 0
234
+ // shows the outgoing scene and progress = 1 shows the incoming one.
235
+ float progress = u_time;
236
+ outColor = transition(v_uv, progress);
237
+ }`;
238
+ var compileShader = (gl, source, type) => {
239
+ const shader = gl.createShader(type);
240
+ if (!shader) {
241
+ throw new Error("Failed to create shader");
242
+ }
243
+ gl.shaderSource(shader, source);
244
+ gl.compileShader(shader);
245
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
246
+ const log = gl.getShaderInfoLog(shader);
247
+ gl.deleteShader(shader);
248
+ throw new Error(`Failed to compile shader: ${log}`);
249
+ }
250
+ return shader;
251
+ };
252
+ var createProgram = (gl) => {
253
+ const program = gl.createProgram();
254
+ if (!program) {
255
+ throw new Error("Failed to create WebGL program");
256
+ }
257
+ const vs = compileShader(gl, VERTEX_SHADER, gl.VERTEX_SHADER);
258
+ const fs = compileShader(gl, FRAGMENT_SHADER, gl.FRAGMENT_SHADER);
259
+ gl.attachShader(program, vs);
260
+ gl.attachShader(program, fs);
261
+ gl.linkProgram(program);
262
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
263
+ const log = gl.getProgramInfoLog(program);
264
+ gl.deleteProgram(program);
265
+ throw new Error(`Failed to link program: ${log}`);
266
+ }
267
+ return program;
268
+ };
269
+ var createTexture = (gl) => {
270
+ const tex = gl.createTexture();
271
+ if (!tex) {
272
+ throw new Error("Failed to create texture");
273
+ }
274
+ gl.bindTexture(gl.TEXTURE_2D, tex);
275
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
276
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
277
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
278
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
279
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
280
+ return tex;
281
+ };
282
+ var dissolveShader = (canvas) => {
283
+ const gl = canvas.getContext("webgl2", { premultipliedAlpha: true });
284
+ if (!gl) {
285
+ throw new Error("Failed to create WebGL2 context");
286
+ }
287
+ const program = createProgram(gl);
288
+ const prevTex = createTexture(gl);
289
+ const nextTex = createTexture(gl);
290
+ const vao = gl.createVertexArray();
291
+ gl.bindVertexArray(vao);
292
+ const buffer = gl.createBuffer();
293
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
294
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
295
+ const aPos = gl.getAttribLocation(program, "a_pos");
296
+ gl.enableVertexAttribArray(aPos);
297
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
298
+ const uTime = gl.getUniformLocation(program, "u_time");
299
+ const uPrev = gl.getUniformLocation(program, "u_prev");
300
+ const uNext = gl.getUniformLocation(program, "u_next");
301
+ const uLineWidth = gl.getUniformLocation(program, "u_line_width");
302
+ const uSpreadColor = gl.getUniformLocation(program, "u_spread_color");
303
+ const uHotColor = gl.getUniformLocation(program, "u_hot_color");
304
+ const uPow = gl.getUniformLocation(program, "u_pow");
305
+ const uIntensity = gl.getUniformLocation(program, "u_intensity");
306
+ const cleanup = () => {
307
+ gl.deleteProgram(program);
308
+ gl.deleteTexture(prevTex);
309
+ gl.deleteTexture(nextTex);
310
+ };
311
+ const clear = () => {
312
+ gl.clearColor(0, 0, 0, 0);
313
+ gl.clear(gl.COLOR_BUFFER_BIT);
314
+ };
315
+ const draw = ({
316
+ prevImage,
317
+ nextImage,
318
+ width,
319
+ height,
320
+ time,
321
+ passedProps
322
+ }) => {
323
+ const {
324
+ lineWidth = DEFAULT_LINE_WIDTH,
325
+ spreadColor = DEFAULT_SPREAD_COLOR,
326
+ hotColor = DEFAULT_HOT_COLOR,
327
+ pow = DEFAULT_POW,
328
+ intensity = DEFAULT_INTENSITY
329
+ } = passedProps;
330
+ if (!prevImage && !nextImage) {
331
+ return;
332
+ }
333
+ if (prevImage && (prevImage.width === 0 || prevImage.height === 0)) {
334
+ return;
335
+ }
336
+ if (nextImage && (nextImage.width === 0 || nextImage.height === 0)) {
337
+ return;
338
+ }
339
+ const effectiveTime = !prevImage ? 0 : !nextImage ? 1 : time;
340
+ gl.viewport(0, 0, width, height);
341
+ gl.clearColor(0, 0, 0, 0);
342
+ gl.clear(gl.COLOR_BUFFER_BIT);
343
+ gl.useProgram(program);
344
+ gl.activeTexture(gl.TEXTURE0);
345
+ gl.bindTexture(gl.TEXTURE_2D, prevTex);
346
+ if (prevImage) {
347
+ gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, prevImage);
348
+ }
349
+ gl.uniform1i(uPrev, 0);
350
+ gl.activeTexture(gl.TEXTURE1);
351
+ gl.bindTexture(gl.TEXTURE_2D, nextTex);
352
+ if (nextImage) {
353
+ gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nextImage);
354
+ }
355
+ gl.uniform1i(uNext, 1);
356
+ const spread = parseHexColor(spreadColor);
357
+ const hot = parseHexColor(hotColor);
358
+ gl.uniform1f(uTime, effectiveTime);
359
+ gl.uniform1f(uLineWidth, lineWidth);
360
+ gl.uniform3f(uSpreadColor, spread[0], spread[1], spread[2]);
361
+ gl.uniform3f(uHotColor, hot[0], hot[1], hot[2]);
362
+ gl.uniform1f(uPow, pow);
363
+ gl.uniform1f(uIntensity, intensity);
364
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
365
+ };
366
+ return {
367
+ clear,
368
+ cleanup,
369
+ draw
370
+ };
371
+ };
372
+ var dissolve = makeHtmlInCanvasPresentation(dissolveShader);
373
+ export {
374
+ dissolveShader,
375
+ dissolve
376
+ };