@remotion/transitions 4.0.465 → 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.
- package/book-flip.js +2 -0
- package/crosswarp.js +2 -0
- package/dist/TransitionSeries.js +16 -6
- package/dist/esm/book-flip.mjs +433 -0
- package/dist/esm/crosswarp.mjs +330 -0
- package/dist/esm/dreamy-zoom.mjs +347 -0
- package/dist/esm/index.mjs +553 -209
- package/dist/esm/linear-blur.mjs +342 -0
- package/dist/esm/ripple.mjs +341 -0
- package/dist/esm/swap.mjs +393 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.js +5 -1
- package/dist/presentations/book-flip.d.ts +14 -0
- package/dist/presentations/book-flip.js +255 -0
- package/dist/presentations/crosswarp.d.ts +11 -0
- package/dist/presentations/crosswarp.js +154 -0
- package/dist/presentations/dreamy-zoom.d.ts +14 -0
- package/dist/presentations/dreamy-zoom.js +169 -0
- package/dist/presentations/linear-blur.d.ts +13 -0
- package/dist/presentations/linear-blur.js +164 -0
- package/dist/presentations/ripple.d.ts +14 -0
- package/dist/presentations/ripple.js +164 -0
- package/dist/presentations/swap.d.ts +15 -0
- package/dist/presentations/swap.js +212 -0
- package/dreamy-zoom.js +2 -0
- package/linear-blur.js +2 -0
- package/package.json +62 -8
- package/ripple.js +2 -0
- package/swap.js +2 -0
|
@@ -0,0 +1,342 @@
|
|
|
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/linear-blur.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
|
+
uniform float u_intensity;
|
|
190
|
+
|
|
191
|
+
in vec2 v_uv;
|
|
192
|
+
out vec4 outColor;
|
|
193
|
+
|
|
194
|
+
const int PASSES = 6;
|
|
195
|
+
|
|
196
|
+
vec4 transition(vec2 uv, float progress) {
|
|
197
|
+
vec4 c1 = vec4(0.0);
|
|
198
|
+
vec4 c2 = vec4(0.0);
|
|
199
|
+
|
|
200
|
+
float disp = u_intensity * (0.5 - distance(0.5, progress));
|
|
201
|
+
for (int xi = 0; xi < PASSES; xi++) {
|
|
202
|
+
float x = float(xi) / float(PASSES) - 0.5;
|
|
203
|
+
for (int yi = 0; yi < PASSES; yi++) {
|
|
204
|
+
float y = float(yi) / float(PASSES) - 0.5;
|
|
205
|
+
vec2 v = vec2(x, y);
|
|
206
|
+
c1 += texture(u_prev, uv + disp * v);
|
|
207
|
+
c2 += texture(u_next, uv + disp * v);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
c1 /= float(PASSES * PASSES);
|
|
212
|
+
c2 /= float(PASSES * PASSES);
|
|
213
|
+
return mix(c1, c2, progress);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void main() {
|
|
217
|
+
float progress = 1.0 - u_time;
|
|
218
|
+
outColor = transition(v_uv, progress);
|
|
219
|
+
}`;
|
|
220
|
+
var compileShader = (gl, source, type) => {
|
|
221
|
+
const shader = gl.createShader(type);
|
|
222
|
+
if (!shader) {
|
|
223
|
+
throw new Error("Failed to create shader");
|
|
224
|
+
}
|
|
225
|
+
gl.shaderSource(shader, source);
|
|
226
|
+
gl.compileShader(shader);
|
|
227
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
228
|
+
const log = gl.getShaderInfoLog(shader);
|
|
229
|
+
gl.deleteShader(shader);
|
|
230
|
+
throw new Error(`Failed to compile shader: ${log}`);
|
|
231
|
+
}
|
|
232
|
+
return shader;
|
|
233
|
+
};
|
|
234
|
+
var createProgram = (gl) => {
|
|
235
|
+
const program = gl.createProgram();
|
|
236
|
+
if (!program) {
|
|
237
|
+
throw new Error("Failed to create WebGL program");
|
|
238
|
+
}
|
|
239
|
+
const vs = compileShader(gl, VERTEX_SHADER, gl.VERTEX_SHADER);
|
|
240
|
+
const fs = compileShader(gl, FRAGMENT_SHADER, gl.FRAGMENT_SHADER);
|
|
241
|
+
gl.attachShader(program, vs);
|
|
242
|
+
gl.attachShader(program, fs);
|
|
243
|
+
gl.linkProgram(program);
|
|
244
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
245
|
+
const log = gl.getProgramInfoLog(program);
|
|
246
|
+
gl.deleteProgram(program);
|
|
247
|
+
throw new Error(`Failed to link program: ${log}`);
|
|
248
|
+
}
|
|
249
|
+
return program;
|
|
250
|
+
};
|
|
251
|
+
var createTexture = (gl) => {
|
|
252
|
+
const tex = gl.createTexture();
|
|
253
|
+
if (!tex) {
|
|
254
|
+
throw new Error("Failed to create texture");
|
|
255
|
+
}
|
|
256
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
257
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
258
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
259
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
260
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
261
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
|
|
262
|
+
return tex;
|
|
263
|
+
};
|
|
264
|
+
var linearBlurShader = (canvas) => {
|
|
265
|
+
const gl = canvas.getContext("webgl2", { premultipliedAlpha: true });
|
|
266
|
+
if (!gl) {
|
|
267
|
+
throw new Error("Failed to create WebGL2 context");
|
|
268
|
+
}
|
|
269
|
+
const program = createProgram(gl);
|
|
270
|
+
const prevTex = createTexture(gl);
|
|
271
|
+
const nextTex = createTexture(gl);
|
|
272
|
+
const vao = gl.createVertexArray();
|
|
273
|
+
gl.bindVertexArray(vao);
|
|
274
|
+
const buffer = gl.createBuffer();
|
|
275
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
276
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
|
|
277
|
+
const aPos = gl.getAttribLocation(program, "a_pos");
|
|
278
|
+
gl.enableVertexAttribArray(aPos);
|
|
279
|
+
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
|
|
280
|
+
const uTime = gl.getUniformLocation(program, "u_time");
|
|
281
|
+
const uPrev = gl.getUniformLocation(program, "u_prev");
|
|
282
|
+
const uNext = gl.getUniformLocation(program, "u_next");
|
|
283
|
+
const uIntensity = gl.getUniformLocation(program, "u_intensity");
|
|
284
|
+
const cleanup = () => {
|
|
285
|
+
gl.deleteProgram(program);
|
|
286
|
+
gl.deleteTexture(prevTex);
|
|
287
|
+
gl.deleteTexture(nextTex);
|
|
288
|
+
};
|
|
289
|
+
const clear = () => {
|
|
290
|
+
gl.clearColor(0, 0, 0, 0);
|
|
291
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
292
|
+
};
|
|
293
|
+
const draw = ({
|
|
294
|
+
prevImage,
|
|
295
|
+
nextImage,
|
|
296
|
+
width,
|
|
297
|
+
height,
|
|
298
|
+
time,
|
|
299
|
+
passedProps
|
|
300
|
+
}) => {
|
|
301
|
+
const { intensity = 0.1 } = passedProps;
|
|
302
|
+
if (!prevImage && !nextImage) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (prevImage && (prevImage.width === 0 || prevImage.height === 0)) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (nextImage && (nextImage.width === 0 || nextImage.height === 0)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const effectiveTime = !prevImage ? 0 : !nextImage ? 1 : time;
|
|
312
|
+
gl.viewport(0, 0, width, height);
|
|
313
|
+
gl.clearColor(0, 0, 0, 0);
|
|
314
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
315
|
+
gl.useProgram(program);
|
|
316
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
317
|
+
gl.bindTexture(gl.TEXTURE_2D, prevTex);
|
|
318
|
+
if (prevImage) {
|
|
319
|
+
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, prevImage);
|
|
320
|
+
}
|
|
321
|
+
gl.uniform1i(uPrev, 0);
|
|
322
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
323
|
+
gl.bindTexture(gl.TEXTURE_2D, nextTex);
|
|
324
|
+
if (nextImage) {
|
|
325
|
+
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nextImage);
|
|
326
|
+
}
|
|
327
|
+
gl.uniform1i(uNext, 1);
|
|
328
|
+
gl.uniform1f(uTime, effectiveTime);
|
|
329
|
+
gl.uniform1f(uIntensity, intensity);
|
|
330
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
331
|
+
};
|
|
332
|
+
return {
|
|
333
|
+
clear,
|
|
334
|
+
cleanup,
|
|
335
|
+
draw
|
|
336
|
+
};
|
|
337
|
+
};
|
|
338
|
+
var linearBlur = makeHtmlInCanvasPresentation(linearBlurShader);
|
|
339
|
+
export {
|
|
340
|
+
linearBlurShader,
|
|
341
|
+
linearBlur
|
|
342
|
+
};
|
|
@@ -0,0 +1,341 @@
|
|
|
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/ripple.tsx
|
|
176
|
+
var DEFAULT_AMPLITUDE = 100;
|
|
177
|
+
var DEFAULT_SPEED = 50;
|
|
178
|
+
var VERTEX_SHADER = `#version 300 es
|
|
179
|
+
in vec2 a_pos;
|
|
180
|
+
out vec2 v_uv;
|
|
181
|
+
void main() {
|
|
182
|
+
v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5);
|
|
183
|
+
gl_Position = vec4(a_pos, 0.0, 1.0);
|
|
184
|
+
}`;
|
|
185
|
+
var FRAGMENT_SHADER = `#version 300 es
|
|
186
|
+
precision highp float;
|
|
187
|
+
|
|
188
|
+
uniform sampler2D u_prev;
|
|
189
|
+
uniform sampler2D u_next;
|
|
190
|
+
uniform float u_time;
|
|
191
|
+
uniform float u_amplitude;
|
|
192
|
+
uniform float u_speed;
|
|
193
|
+
|
|
194
|
+
in vec2 v_uv;
|
|
195
|
+
out vec4 outColor;
|
|
196
|
+
|
|
197
|
+
vec4 transition(vec2 uv, float progress) {
|
|
198
|
+
vec2 dir = uv - vec2(0.5);
|
|
199
|
+
float dist = length(dir);
|
|
200
|
+
vec2 offset = dir * (sin(progress * dist * u_amplitude - progress * u_speed) + 0.5) / 30.0 * progress;
|
|
201
|
+
return mix(
|
|
202
|
+
texture(u_next, uv + offset),
|
|
203
|
+
texture(u_prev, uv),
|
|
204
|
+
smoothstep(0.2, 1.0, progress)
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
void main() {
|
|
209
|
+
// In Remotion's HTML-in-canvas convention, u_prev is bound to the incoming
|
|
210
|
+
// scene and u_next is bound to the outgoing scene, so the gl-transitions
|
|
211
|
+
// "from" → u_next and "to" → u_prev. With this binding, progress = u_time
|
|
212
|
+
// (no inversion) maps to the gl-transitions convention where progress = 0
|
|
213
|
+
// shows the outgoing scene and progress = 1 shows the incoming one.
|
|
214
|
+
float progress = u_time;
|
|
215
|
+
outColor = transition(v_uv, progress);
|
|
216
|
+
}`;
|
|
217
|
+
var compileShader = (gl, source, type) => {
|
|
218
|
+
const shader = gl.createShader(type);
|
|
219
|
+
if (!shader) {
|
|
220
|
+
throw new Error("Failed to create shader");
|
|
221
|
+
}
|
|
222
|
+
gl.shaderSource(shader, source);
|
|
223
|
+
gl.compileShader(shader);
|
|
224
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
225
|
+
const log = gl.getShaderInfoLog(shader);
|
|
226
|
+
gl.deleteShader(shader);
|
|
227
|
+
throw new Error(`Failed to compile shader: ${log}`);
|
|
228
|
+
}
|
|
229
|
+
return shader;
|
|
230
|
+
};
|
|
231
|
+
var createProgram = (gl) => {
|
|
232
|
+
const program = gl.createProgram();
|
|
233
|
+
if (!program) {
|
|
234
|
+
throw new Error("Failed to create WebGL program");
|
|
235
|
+
}
|
|
236
|
+
const vs = compileShader(gl, VERTEX_SHADER, gl.VERTEX_SHADER);
|
|
237
|
+
const fs = compileShader(gl, FRAGMENT_SHADER, gl.FRAGMENT_SHADER);
|
|
238
|
+
gl.attachShader(program, vs);
|
|
239
|
+
gl.attachShader(program, fs);
|
|
240
|
+
gl.linkProgram(program);
|
|
241
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
242
|
+
const log = gl.getProgramInfoLog(program);
|
|
243
|
+
gl.deleteProgram(program);
|
|
244
|
+
throw new Error(`Failed to link program: ${log}`);
|
|
245
|
+
}
|
|
246
|
+
return program;
|
|
247
|
+
};
|
|
248
|
+
var createTexture = (gl) => {
|
|
249
|
+
const tex = gl.createTexture();
|
|
250
|
+
if (!tex) {
|
|
251
|
+
throw new Error("Failed to create texture");
|
|
252
|
+
}
|
|
253
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
254
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
255
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
256
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
257
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
258
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
|
|
259
|
+
return tex;
|
|
260
|
+
};
|
|
261
|
+
var rippleShader = (canvas) => {
|
|
262
|
+
const gl = canvas.getContext("webgl2", { premultipliedAlpha: true });
|
|
263
|
+
if (!gl) {
|
|
264
|
+
throw new Error("Failed to create WebGL2 context");
|
|
265
|
+
}
|
|
266
|
+
const program = createProgram(gl);
|
|
267
|
+
const prevTex = createTexture(gl);
|
|
268
|
+
const nextTex = createTexture(gl);
|
|
269
|
+
const vao = gl.createVertexArray();
|
|
270
|
+
gl.bindVertexArray(vao);
|
|
271
|
+
const buffer = gl.createBuffer();
|
|
272
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
273
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
|
|
274
|
+
const aPos = gl.getAttribLocation(program, "a_pos");
|
|
275
|
+
gl.enableVertexAttribArray(aPos);
|
|
276
|
+
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
|
|
277
|
+
const uTime = gl.getUniformLocation(program, "u_time");
|
|
278
|
+
const uPrev = gl.getUniformLocation(program, "u_prev");
|
|
279
|
+
const uNext = gl.getUniformLocation(program, "u_next");
|
|
280
|
+
const uAmplitude = gl.getUniformLocation(program, "u_amplitude");
|
|
281
|
+
const uSpeed = gl.getUniformLocation(program, "u_speed");
|
|
282
|
+
const cleanup = () => {
|
|
283
|
+
gl.deleteProgram(program);
|
|
284
|
+
gl.deleteTexture(prevTex);
|
|
285
|
+
gl.deleteTexture(nextTex);
|
|
286
|
+
};
|
|
287
|
+
const clear = () => {
|
|
288
|
+
gl.clearColor(0, 0, 0, 0);
|
|
289
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
290
|
+
};
|
|
291
|
+
const draw = ({
|
|
292
|
+
prevImage,
|
|
293
|
+
nextImage,
|
|
294
|
+
width,
|
|
295
|
+
height,
|
|
296
|
+
time,
|
|
297
|
+
passedProps
|
|
298
|
+
}) => {
|
|
299
|
+
const { amplitude = DEFAULT_AMPLITUDE, speed = DEFAULT_SPEED } = passedProps;
|
|
300
|
+
if (!prevImage && !nextImage) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (prevImage && (prevImage.width === 0 || prevImage.height === 0)) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (nextImage && (nextImage.width === 0 || nextImage.height === 0)) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const effectiveTime = !prevImage ? 0 : !nextImage ? 1 : time;
|
|
310
|
+
gl.viewport(0, 0, width, height);
|
|
311
|
+
gl.clearColor(0, 0, 0, 0);
|
|
312
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
313
|
+
gl.useProgram(program);
|
|
314
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
315
|
+
gl.bindTexture(gl.TEXTURE_2D, prevTex);
|
|
316
|
+
if (prevImage) {
|
|
317
|
+
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, prevImage);
|
|
318
|
+
}
|
|
319
|
+
gl.uniform1i(uPrev, 0);
|
|
320
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
321
|
+
gl.bindTexture(gl.TEXTURE_2D, nextTex);
|
|
322
|
+
if (nextImage) {
|
|
323
|
+
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nextImage);
|
|
324
|
+
}
|
|
325
|
+
gl.uniform1i(uNext, 1);
|
|
326
|
+
gl.uniform1f(uTime, effectiveTime);
|
|
327
|
+
gl.uniform1f(uAmplitude, amplitude);
|
|
328
|
+
gl.uniform1f(uSpeed, speed);
|
|
329
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
330
|
+
};
|
|
331
|
+
return {
|
|
332
|
+
clear,
|
|
333
|
+
cleanup,
|
|
334
|
+
draw
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
var ripple = makeHtmlInCanvasPresentation(rippleShader);
|
|
338
|
+
export {
|
|
339
|
+
rippleShader,
|
|
340
|
+
ripple
|
|
341
|
+
};
|