@remotion/transitions 4.0.522 → 4.0.524
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/blur-slide.js +2 -0
- package/dist/esm/blur-slide.mjs +527 -0
- package/dist/esm/index.mjs +305 -38
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/presentations/blur-slide.d.ts +18 -0
- package/dist/presentations/blur-slide.js +282 -0
- package/package.json +17 -8
package/blur-slide.js
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
// src/html-in-canvas-presentation.tsx
|
|
2
|
+
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
AbsoluteFill,
|
|
5
|
+
HTML_IN_CANVAS_UNSUPPORTED_MESSAGE,
|
|
6
|
+
HtmlInCanvas,
|
|
7
|
+
Internals,
|
|
8
|
+
useDelayRender
|
|
9
|
+
} from "remotion";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
var HtmlInCanvasPresentation = ({
|
|
12
|
+
children,
|
|
13
|
+
onElementImage,
|
|
14
|
+
onUnmount,
|
|
15
|
+
presentationProgress,
|
|
16
|
+
presentationDirection,
|
|
17
|
+
shader,
|
|
18
|
+
effects,
|
|
19
|
+
passedProps,
|
|
20
|
+
bothEnteringAndExiting
|
|
21
|
+
}) => {
|
|
22
|
+
if (!HtmlInCanvas.isSupported()) {
|
|
23
|
+
throw new Error(HTML_IN_CANVAS_UNSUPPORTED_MESSAGE);
|
|
24
|
+
}
|
|
25
|
+
const canvasRef = useRef(null);
|
|
26
|
+
const outputCanvasRef = useRef(null);
|
|
27
|
+
const canvasSubtreeStyle = useMemo(() => {
|
|
28
|
+
return {
|
|
29
|
+
width: "100%",
|
|
30
|
+
height: "100%",
|
|
31
|
+
position: "absolute",
|
|
32
|
+
top: 0,
|
|
33
|
+
left: 0,
|
|
34
|
+
right: 0,
|
|
35
|
+
bottom: 0
|
|
36
|
+
};
|
|
37
|
+
}, []);
|
|
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));
|
|
53
|
+
const passedPropsRef = useRef(passedProps);
|
|
54
|
+
passedPropsRef.current = passedProps;
|
|
55
|
+
const memoizedEffects = Internals.useMemoizedEffects({
|
|
56
|
+
effects: effects ?? [],
|
|
57
|
+
overrideId: null
|
|
58
|
+
});
|
|
59
|
+
const effectsRef = useRef(memoizedEffects);
|
|
60
|
+
effectsRef.current = memoizedEffects;
|
|
61
|
+
const [instance] = useState(() => shader(shaderCanvas));
|
|
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;
|
|
76
|
+
return () => {
|
|
77
|
+
instance.cleanup();
|
|
78
|
+
captureCanvasRef.current = null;
|
|
79
|
+
captureContextRef.current = null;
|
|
80
|
+
};
|
|
81
|
+
}, [instance]);
|
|
82
|
+
const chainState = Internals.useEffectChainState();
|
|
83
|
+
const { delayRender, continueRender, cancelRender } = useDelayRender();
|
|
84
|
+
const draw = useCallback(async (prevImage, nextImage, progress) => {
|
|
85
|
+
const outputCanvas = outputCanvasRef.current;
|
|
86
|
+
if (!outputCanvas) {
|
|
87
|
+
throw new Error("Canvas not found");
|
|
88
|
+
}
|
|
89
|
+
const handle = delayRender("onPaint");
|
|
90
|
+
try {
|
|
91
|
+
const clearOutput = () => {
|
|
92
|
+
const context = outputCanvas.getContext("2d");
|
|
93
|
+
if (!context) {
|
|
94
|
+
throw new Error("Failed to create output canvas context");
|
|
95
|
+
}
|
|
96
|
+
context.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
|
|
97
|
+
};
|
|
98
|
+
if (!prevImage && !nextImage) {
|
|
99
|
+
instance.clear();
|
|
100
|
+
clearOutput();
|
|
101
|
+
continueRender(handle);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const width = prevImage?.width ?? nextImage?.width ?? 0;
|
|
105
|
+
const height = prevImage?.height ?? nextImage?.height ?? 0;
|
|
106
|
+
if (width === 0 || height === 0) {
|
|
107
|
+
instance.clear();
|
|
108
|
+
clearOutput();
|
|
109
|
+
continueRender(handle);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
shaderCanvas.width = width;
|
|
113
|
+
shaderCanvas.height = height;
|
|
114
|
+
instance.draw({
|
|
115
|
+
prevImage,
|
|
116
|
+
nextImage,
|
|
117
|
+
width,
|
|
118
|
+
height,
|
|
119
|
+
time: progress,
|
|
120
|
+
passedProps: passedPropsRef.current
|
|
121
|
+
});
|
|
122
|
+
await Internals.runEffectChain({
|
|
123
|
+
state: chainState.get(width, height),
|
|
124
|
+
source: shaderCanvas,
|
|
125
|
+
effects: effectsRef.current ?? [],
|
|
126
|
+
width,
|
|
127
|
+
height,
|
|
128
|
+
output: outputCanvas
|
|
129
|
+
});
|
|
130
|
+
continueRender(handle);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
cancelRender(error);
|
|
133
|
+
}
|
|
134
|
+
}, [
|
|
135
|
+
cancelRender,
|
|
136
|
+
chainState,
|
|
137
|
+
continueRender,
|
|
138
|
+
delayRender,
|
|
139
|
+
instance,
|
|
140
|
+
shaderCanvas
|
|
141
|
+
]);
|
|
142
|
+
const passThrough = bothEnteringAndExiting && presentationDirection === "exiting";
|
|
143
|
+
useLayoutEffect(() => {
|
|
144
|
+
if (passThrough) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const canvas = canvasRef.current;
|
|
148
|
+
if (!canvas) {
|
|
149
|
+
throw new Error("Canvas not found");
|
|
150
|
+
}
|
|
151
|
+
canvas.layoutSubtree = true;
|
|
152
|
+
const onPaint = () => {
|
|
153
|
+
const firstChild = canvas.firstChild;
|
|
154
|
+
const captureCanvas = captureCanvasRef.current;
|
|
155
|
+
const captureContext = captureContextRef.current;
|
|
156
|
+
if (!firstChild || !captureCanvas || !captureContext) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const elementImage = canvas.captureElementImage(firstChild);
|
|
160
|
+
try {
|
|
161
|
+
captureContext.reset();
|
|
162
|
+
captureContext.drawElementImage(elementImage, 0, 0);
|
|
163
|
+
} finally {
|
|
164
|
+
elementImage.close();
|
|
165
|
+
}
|
|
166
|
+
onElementImage(captureCanvas, draw);
|
|
167
|
+
};
|
|
168
|
+
canvas.addEventListener("paint", onPaint);
|
|
169
|
+
return () => {
|
|
170
|
+
canvas.removeEventListener("paint", onPaint);
|
|
171
|
+
};
|
|
172
|
+
}, [onElementImage, presentationDirection, draw, passThrough]);
|
|
173
|
+
useLayoutEffect(() => {
|
|
174
|
+
if (passThrough) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const canvas = canvasRef.current;
|
|
178
|
+
if (!canvas) {
|
|
179
|
+
throw new Error("Canvas not found");
|
|
180
|
+
}
|
|
181
|
+
canvas.requestPaint?.();
|
|
182
|
+
}, [presentationProgress, passThrough, memoizedEffects]);
|
|
183
|
+
useLayoutEffect(() => {
|
|
184
|
+
if (passThrough) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
return () => {
|
|
188
|
+
onUnmount();
|
|
189
|
+
};
|
|
190
|
+
}, [onUnmount, passThrough]);
|
|
191
|
+
useLayoutEffect(() => {
|
|
192
|
+
if (passThrough) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const canvas = canvasRef.current;
|
|
196
|
+
if (!canvas) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
200
|
+
const outputCanvas = outputCanvasRef.current;
|
|
201
|
+
const captureCanvas = captureCanvasRef.current;
|
|
202
|
+
if (!outputCanvas || !captureCanvas) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const width = entry.devicePixelContentBoxSize[0].inlineSize;
|
|
206
|
+
const height = entry.devicePixelContentBoxSize[0].blockSize;
|
|
207
|
+
captureCanvas.width = width;
|
|
208
|
+
captureCanvas.height = height;
|
|
209
|
+
outputCanvas.width = width;
|
|
210
|
+
outputCanvas.height = height;
|
|
211
|
+
canvas.requestPaint?.();
|
|
212
|
+
});
|
|
213
|
+
observer.observe(canvas, { box: "device-pixel-content-box" });
|
|
214
|
+
return () => {
|
|
215
|
+
observer.disconnect();
|
|
216
|
+
};
|
|
217
|
+
}, [passThrough]);
|
|
218
|
+
if (passThrough) {
|
|
219
|
+
return children;
|
|
220
|
+
}
|
|
221
|
+
return /* @__PURE__ */ jsxs(AbsoluteFill, {
|
|
222
|
+
children: [
|
|
223
|
+
/* @__PURE__ */ jsx("canvas", {
|
|
224
|
+
ref: canvasRef,
|
|
225
|
+
style: canvasSubtreeStyle,
|
|
226
|
+
children
|
|
227
|
+
}),
|
|
228
|
+
/* @__PURE__ */ jsx("canvas", {
|
|
229
|
+
ref: outputCanvasRef,
|
|
230
|
+
style: outputCanvasStyle
|
|
231
|
+
})
|
|
232
|
+
]
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
var makeHtmlInCanvasPresentation = (shader) => {
|
|
236
|
+
const CompWithShader = (props) => {
|
|
237
|
+
const { passedProps, ...otherProps } = props;
|
|
238
|
+
const { effects, ...restPassedProps } = props.passedProps;
|
|
239
|
+
return /* @__PURE__ */ jsx(HtmlInCanvasPresentation, {
|
|
240
|
+
shader,
|
|
241
|
+
passedProps: restPassedProps,
|
|
242
|
+
effects,
|
|
243
|
+
...otherProps
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
return (props) => {
|
|
247
|
+
return {
|
|
248
|
+
component: CompWithShader,
|
|
249
|
+
props
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/presentations/upload-element-image.ts
|
|
255
|
+
var uploadElementImage = (gl, elementImage) => {
|
|
256
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, elementImage);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// src/presentations/blur-slide.tsx
|
|
260
|
+
var DEFAULT_DIRECTION = "from-left";
|
|
261
|
+
var DEFAULT_BLUR = 0.5;
|
|
262
|
+
var VALID_DIRECTIONS = [
|
|
263
|
+
"from-left",
|
|
264
|
+
"from-right",
|
|
265
|
+
"from-top",
|
|
266
|
+
"from-bottom"
|
|
267
|
+
];
|
|
268
|
+
var VERTEX_SHADER = `#version 300 es
|
|
269
|
+
in vec2 a_pos;
|
|
270
|
+
out vec2 v_uv;
|
|
271
|
+
void main() {
|
|
272
|
+
v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5);
|
|
273
|
+
gl_Position = vec4(a_pos, 0.0, 1.0);
|
|
274
|
+
}`;
|
|
275
|
+
var SAMPLES = 32;
|
|
276
|
+
var SLIDE_FRAGMENT_SHADER = `#version 300 es
|
|
277
|
+
precision highp float;
|
|
278
|
+
|
|
279
|
+
uniform sampler2D u_prev;
|
|
280
|
+
uniform sampler2D u_next;
|
|
281
|
+
uniform float u_progress;
|
|
282
|
+
uniform vec2 u_direction;
|
|
283
|
+
uniform float u_blur_length;
|
|
284
|
+
|
|
285
|
+
in vec2 v_uv;
|
|
286
|
+
out vec4 outColor;
|
|
287
|
+
|
|
288
|
+
const int SAMPLES = ${SAMPLES};
|
|
289
|
+
|
|
290
|
+
void main() {
|
|
291
|
+
// Both scenes travel together; the exiting scene wraps around the edges
|
|
292
|
+
// while the entering scene takes over through a crossfade in the middle.
|
|
293
|
+
vec2 slidUv = v_uv - u_direction * u_progress;
|
|
294
|
+
float mixFactor = smoothstep(0.3, 0.7, u_progress);
|
|
295
|
+
|
|
296
|
+
vec4 color = vec4(0.0);
|
|
297
|
+
for (int i = 0; i < SAMPLES; i++) {
|
|
298
|
+
float t = (float(i) / float(SAMPLES - 1) - 0.5) * u_blur_length;
|
|
299
|
+
vec2 uv = slidUv + u_direction * t;
|
|
300
|
+
color += mix(texture(u_prev, uv), texture(u_next, uv), mixFactor);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
outColor = color / float(SAMPLES);
|
|
304
|
+
}`;
|
|
305
|
+
var BLUR_FRAGMENT_SHADER = `#version 300 es
|
|
306
|
+
precision highp float;
|
|
307
|
+
|
|
308
|
+
uniform sampler2D u_source;
|
|
309
|
+
uniform vec2 u_direction;
|
|
310
|
+
uniform float u_blur_length;
|
|
311
|
+
|
|
312
|
+
in vec2 v_uv;
|
|
313
|
+
out vec4 outColor;
|
|
314
|
+
|
|
315
|
+
const int SAMPLES = ${SAMPLES};
|
|
316
|
+
|
|
317
|
+
void main() {
|
|
318
|
+
vec4 color = vec4(0.0);
|
|
319
|
+
for (int i = 0; i < SAMPLES; i++) {
|
|
320
|
+
float t = ((float(i) + 0.5) / float(SAMPLES) - 0.5) * u_blur_length;
|
|
321
|
+
vec2 uv = v_uv + u_direction * t;
|
|
322
|
+
// The framebuffer texture is stored bottom-up, so flip Y when sampling it.
|
|
323
|
+
color += texture(u_source, vec2(uv.x, 1.0 - uv.y));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
outColor = color / float(SAMPLES);
|
|
327
|
+
}`;
|
|
328
|
+
var compileShader = (gl, source, type) => {
|
|
329
|
+
const shader = gl.createShader(type);
|
|
330
|
+
if (!shader) {
|
|
331
|
+
throw new Error("Failed to create shader");
|
|
332
|
+
}
|
|
333
|
+
gl.shaderSource(shader, source);
|
|
334
|
+
gl.compileShader(shader);
|
|
335
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
336
|
+
const log = gl.getShaderInfoLog(shader);
|
|
337
|
+
gl.deleteShader(shader);
|
|
338
|
+
throw new Error(`Failed to compile shader: ${log}`);
|
|
339
|
+
}
|
|
340
|
+
return shader;
|
|
341
|
+
};
|
|
342
|
+
var createProgram = (gl, fragmentShader) => {
|
|
343
|
+
const program = gl.createProgram();
|
|
344
|
+
if (!program) {
|
|
345
|
+
throw new Error("Failed to create WebGL program");
|
|
346
|
+
}
|
|
347
|
+
const vs = compileShader(gl, VERTEX_SHADER, gl.VERTEX_SHADER);
|
|
348
|
+
const fs = compileShader(gl, fragmentShader, gl.FRAGMENT_SHADER);
|
|
349
|
+
gl.attachShader(program, vs);
|
|
350
|
+
gl.attachShader(program, fs);
|
|
351
|
+
gl.linkProgram(program);
|
|
352
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
353
|
+
const log = gl.getProgramInfoLog(program);
|
|
354
|
+
gl.deleteProgram(program);
|
|
355
|
+
throw new Error(`Failed to link program: ${log}`);
|
|
356
|
+
}
|
|
357
|
+
return program;
|
|
358
|
+
};
|
|
359
|
+
var createTexture = (gl) => {
|
|
360
|
+
const tex = gl.createTexture();
|
|
361
|
+
if (!tex) {
|
|
362
|
+
throw new Error("Failed to create texture");
|
|
363
|
+
}
|
|
364
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
365
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
|
366
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
|
367
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
368
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
369
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
|
|
370
|
+
return tex;
|
|
371
|
+
};
|
|
372
|
+
var getDirectionVector = (direction) => {
|
|
373
|
+
switch (direction) {
|
|
374
|
+
case "from-left":
|
|
375
|
+
return [1, 0];
|
|
376
|
+
case "from-right":
|
|
377
|
+
return [-1, 0];
|
|
378
|
+
case "from-top":
|
|
379
|
+
return [0, 1];
|
|
380
|
+
case "from-bottom":
|
|
381
|
+
return [0, -1];
|
|
382
|
+
default:
|
|
383
|
+
throw new Error(`Invalid direction: ${direction}`);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
var validateProps = (props) => {
|
|
387
|
+
const direction = props.direction ?? DEFAULT_DIRECTION;
|
|
388
|
+
const blur = props.blur ?? DEFAULT_BLUR;
|
|
389
|
+
if (!VALID_DIRECTIONS.includes(direction)) {
|
|
390
|
+
throw new TypeError(`direction passed to blurSlide() must be one of ${VALID_DIRECTIONS.map((d) => `"${d}"`).join(", ")}, received ${JSON.stringify(direction)}`);
|
|
391
|
+
}
|
|
392
|
+
if (typeof blur !== "number" || !Number.isFinite(blur)) {
|
|
393
|
+
throw new TypeError(`blur passed to blurSlide() must be a finite number, received ${blur}`);
|
|
394
|
+
}
|
|
395
|
+
if (blur < 0) {
|
|
396
|
+
throw new TypeError(`blur passed to blurSlide() must be greater than or equal to 0, received ${blur}`);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
var blurSlideShader = (canvas) => {
|
|
400
|
+
const gl = canvas.getContext("webgl2", { premultipliedAlpha: true });
|
|
401
|
+
if (!gl) {
|
|
402
|
+
throw new Error("Failed to create WebGL2 context");
|
|
403
|
+
}
|
|
404
|
+
const slideProgram = createProgram(gl, SLIDE_FRAGMENT_SHADER);
|
|
405
|
+
const blurProgram = createProgram(gl, BLUR_FRAGMENT_SHADER);
|
|
406
|
+
const prevTex = createTexture(gl);
|
|
407
|
+
const nextTex = createTexture(gl);
|
|
408
|
+
const intermediateTex = createTexture(gl);
|
|
409
|
+
const framebuffer = gl.createFramebuffer();
|
|
410
|
+
if (!framebuffer) {
|
|
411
|
+
throw new Error("Failed to create framebuffer");
|
|
412
|
+
}
|
|
413
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
414
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, intermediateTex, 0);
|
|
415
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
416
|
+
let intermediateWidth = 1;
|
|
417
|
+
let intermediateHeight = 1;
|
|
418
|
+
const vao = gl.createVertexArray();
|
|
419
|
+
gl.bindVertexArray(vao);
|
|
420
|
+
const buffer = gl.createBuffer();
|
|
421
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
422
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
|
|
423
|
+
const aPos = gl.getAttribLocation(slideProgram, "a_pos");
|
|
424
|
+
gl.enableVertexAttribArray(aPos);
|
|
425
|
+
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
|
|
426
|
+
const uPrev = gl.getUniformLocation(slideProgram, "u_prev");
|
|
427
|
+
const uNext = gl.getUniformLocation(slideProgram, "u_next");
|
|
428
|
+
const uProgress = gl.getUniformLocation(slideProgram, "u_progress");
|
|
429
|
+
const uSlideDirection = gl.getUniformLocation(slideProgram, "u_direction");
|
|
430
|
+
const uSlideBlurLength = gl.getUniformLocation(slideProgram, "u_blur_length");
|
|
431
|
+
const uSource = gl.getUniformLocation(blurProgram, "u_source");
|
|
432
|
+
const uBlurDirection = gl.getUniformLocation(blurProgram, "u_direction");
|
|
433
|
+
const uBlurBlurLength = gl.getUniformLocation(blurProgram, "u_blur_length");
|
|
434
|
+
const cleanup = () => {
|
|
435
|
+
gl.deleteProgram(slideProgram);
|
|
436
|
+
gl.deleteProgram(blurProgram);
|
|
437
|
+
gl.deleteTexture(prevTex);
|
|
438
|
+
gl.deleteTexture(nextTex);
|
|
439
|
+
gl.deleteTexture(intermediateTex);
|
|
440
|
+
gl.deleteFramebuffer(framebuffer);
|
|
441
|
+
gl.deleteBuffer(buffer);
|
|
442
|
+
gl.deleteVertexArray(vao);
|
|
443
|
+
};
|
|
444
|
+
const clear = () => {
|
|
445
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
446
|
+
gl.clearColor(0, 0, 0, 0);
|
|
447
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
448
|
+
};
|
|
449
|
+
const draw = ({
|
|
450
|
+
prevImage,
|
|
451
|
+
nextImage,
|
|
452
|
+
width,
|
|
453
|
+
height,
|
|
454
|
+
time,
|
|
455
|
+
passedProps
|
|
456
|
+
}) => {
|
|
457
|
+
const { direction = DEFAULT_DIRECTION, blur = DEFAULT_BLUR } = passedProps;
|
|
458
|
+
if (!prevImage && !nextImage) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (prevImage && (prevImage.width === 0 || prevImage.height === 0)) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (nextImage && (nextImage.width === 0 || nextImage.height === 0)) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const effectiveTime = !prevImage ? 0 : !nextImage ? 1 : time;
|
|
468
|
+
const p = 1 - effectiveTime;
|
|
469
|
+
const progress = p * p * p * (p * (p * 6 - 15) + 10);
|
|
470
|
+
const blurLength = blur * 2 * Math.min(progress, 1 - progress);
|
|
471
|
+
const coarseTapSpacing = blurLength / (SAMPLES - 1);
|
|
472
|
+
const [dirX, dirY] = getDirectionVector(direction);
|
|
473
|
+
gl.bindVertexArray(vao);
|
|
474
|
+
if (intermediateWidth !== width || intermediateHeight !== height) {
|
|
475
|
+
gl.bindTexture(gl.TEXTURE_2D, intermediateTex);
|
|
476
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
477
|
+
intermediateWidth = width;
|
|
478
|
+
intermediateHeight = height;
|
|
479
|
+
}
|
|
480
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
481
|
+
gl.viewport(0, 0, width, height);
|
|
482
|
+
gl.clearColor(0, 0, 0, 0);
|
|
483
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
484
|
+
gl.useProgram(slideProgram);
|
|
485
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
486
|
+
gl.bindTexture(gl.TEXTURE_2D, prevTex);
|
|
487
|
+
if (prevImage) {
|
|
488
|
+
uploadElementImage(gl, prevImage);
|
|
489
|
+
}
|
|
490
|
+
gl.uniform1i(uPrev, 0);
|
|
491
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
492
|
+
gl.bindTexture(gl.TEXTURE_2D, nextTex);
|
|
493
|
+
if (nextImage) {
|
|
494
|
+
uploadElementImage(gl, nextImage);
|
|
495
|
+
}
|
|
496
|
+
gl.uniform1i(uNext, 1);
|
|
497
|
+
gl.uniform1f(uProgress, progress);
|
|
498
|
+
gl.uniform2f(uSlideDirection, dirX, dirY);
|
|
499
|
+
gl.uniform1f(uSlideBlurLength, blurLength);
|
|
500
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
501
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
502
|
+
gl.viewport(0, 0, width, height);
|
|
503
|
+
gl.clearColor(0, 0, 0, 0);
|
|
504
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
505
|
+
gl.useProgram(blurProgram);
|
|
506
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
507
|
+
gl.bindTexture(gl.TEXTURE_2D, intermediateTex);
|
|
508
|
+
gl.uniform1i(uSource, 0);
|
|
509
|
+
gl.uniform2f(uBlurDirection, dirX, dirY);
|
|
510
|
+
gl.uniform1f(uBlurBlurLength, coarseTapSpacing);
|
|
511
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
512
|
+
};
|
|
513
|
+
return {
|
|
514
|
+
clear,
|
|
515
|
+
cleanup,
|
|
516
|
+
draw
|
|
517
|
+
};
|
|
518
|
+
};
|
|
519
|
+
var makeBlurSlide = makeHtmlInCanvasPresentation(blurSlideShader);
|
|
520
|
+
var blurSlide = (props = {}) => {
|
|
521
|
+
validateProps(props);
|
|
522
|
+
return makeBlurSlide(props);
|
|
523
|
+
};
|
|
524
|
+
export {
|
|
525
|
+
blurSlideShader,
|
|
526
|
+
blurSlide
|
|
527
|
+
};
|