@remotion/web-renderer 4.0.395 → 4.0.397
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/dist/border-radius.d.ts +31 -0
- package/dist/border-radius.js +152 -0
- package/dist/calculate-transforms.d.ts +2 -0
- package/dist/calculate-transforms.js +17 -0
- package/dist/composable.d.ts +2 -8
- package/dist/compose-canvas.js +28 -4
- package/dist/compose.d.ts +4 -3
- package/dist/compose.js +45 -12
- package/dist/create-scaffold.js +10 -3
- package/dist/drawing/border-radius.d.ts +3 -5
- package/dist/drawing/border-radius.js +12 -34
- package/dist/drawing/calculate-transforms.d.ts +12 -2
- package/dist/drawing/calculate-transforms.js +38 -19
- package/dist/drawing/canvas-offset-from-rect.d.ts +8 -0
- package/dist/drawing/canvas-offset-from-rect.js +12 -0
- package/dist/drawing/clamp-rect-to-parent-bounds.d.ts +8 -0
- package/dist/drawing/clamp-rect-to-parent-bounds.js +18 -0
- package/dist/drawing/compose-canvas.d.ts +1 -0
- package/dist/drawing/compose-canvas.js +36 -0
- package/dist/drawing/compose-svg.d.ts +1 -0
- package/dist/drawing/compose-svg.js +34 -0
- package/dist/drawing/compose.d.ts +5 -0
- package/dist/drawing/compose.js +6 -0
- package/dist/drawing/do-rects-intersect.d.ts +1 -0
- package/dist/drawing/do-rects-intersect.js +6 -0
- package/dist/drawing/draw-border.d.ts +2 -5
- package/dist/drawing/draw-border.js +37 -37
- package/dist/drawing/draw-box-shadow.d.ts +18 -0
- package/dist/drawing/draw-box-shadow.js +103 -0
- package/dist/drawing/draw-element-to-canvas.d.ts +2 -1
- package/dist/drawing/draw-element-to-canvas.js +8 -36
- package/dist/drawing/draw-element.d.ts +8 -3
- package/dist/drawing/draw-element.js +64 -18
- package/dist/drawing/draw-outline.d.ts +9 -0
- package/dist/drawing/draw-outline.js +93 -0
- package/dist/drawing/draw-rounded.d.ts +9 -0
- package/dist/drawing/draw-rounded.js +34 -0
- package/dist/drawing/get-bounding-box-including-shadow.d.ts +1 -0
- package/dist/drawing/get-bounding-box-including-shadow.js +6 -0
- package/dist/drawing/get-computed-style-cache.d.ts +0 -0
- package/dist/drawing/get-computed-style-cache.js +1 -0
- package/dist/drawing/get-pretransform-rect.d.ts +1 -0
- package/dist/drawing/get-pretransform-rect.js +36 -0
- package/dist/drawing/handle-3d-transform.d.ts +11 -0
- package/dist/drawing/handle-3d-transform.js +25 -0
- package/dist/drawing/handle-mask.d.ts +8 -0
- package/dist/drawing/handle-mask.js +19 -0
- package/dist/drawing/has-transform.d.ts +4 -0
- package/dist/drawing/has-transform.js +14 -0
- package/dist/drawing/mask-image.d.ts +3 -0
- package/dist/drawing/mask-image.js +14 -0
- package/dist/drawing/overflow.d.ts +7 -0
- package/dist/drawing/overflow.js +12 -0
- package/dist/drawing/parse-linear-gradient.d.ts +14 -0
- package/dist/drawing/parse-linear-gradient.js +260 -0
- package/dist/drawing/precompose.d.ts +11 -0
- package/dist/drawing/precompose.js +13 -0
- package/dist/drawing/process-node.d.ts +18 -0
- package/dist/drawing/process-node.js +116 -0
- package/dist/drawing/round-to-expand-rect.d.ts +1 -0
- package/dist/drawing/round-to-expand-rect.js +7 -0
- package/dist/drawing/text/draw-text.d.ts +5 -1
- package/dist/drawing/text/draw-text.js +10 -5
- package/dist/drawing/text/find-line-breaks.text.d.ts +1 -1
- package/dist/drawing/text/find-line-breaks.text.js +2 -2
- package/dist/drawing/text/handle-text-node.d.ts +7 -5
- package/dist/drawing/text/handle-text-node.js +7 -6
- package/dist/drawing/transform-in-3d.d.ts +9 -7
- package/dist/drawing/transform-in-3d.js +41 -31
- package/dist/drawing/transform-rect-with-matrix.d.ts +4 -0
- package/dist/drawing/transform-rect-with-matrix.js +19 -0
- package/dist/drawing/transform.d.ts +2 -1
- package/dist/drawing/transform.js +6 -2
- package/dist/esm/index.mjs +1138 -278
- package/dist/find-canvas-elements.d.ts +1 -0
- package/dist/find-canvas-elements.js +13 -0
- package/dist/find-capturable-elements.d.ts +1 -1
- package/dist/find-capturable-elements.js +20 -22
- package/dist/get-biggest-bounding-client-rect.js +29 -4
- package/dist/internal-state.d.ts +9 -0
- package/dist/internal-state.js +12 -0
- package/dist/opacity.d.ts +4 -0
- package/dist/opacity.js +7 -0
- package/dist/render-media-on-web.d.ts +3 -0
- package/dist/render-media-on-web.js +27 -14
- package/dist/render-still-on-web.d.ts +5 -1
- package/dist/render-still-on-web.js +4 -1
- package/dist/take-screenshot.d.ts +5 -2
- package/dist/take-screenshot.js +16 -4
- package/dist/transform.d.ts +4 -0
- package/dist/transform.js +6 -0
- package/package.json +6 -6
- package/dist/drawing/text/get-base-height.d.ts +0 -1
- package/dist/drawing/text/get-base-height.js +0 -13
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const canvasOffsetFromRect = ({ rect }) => {
|
|
2
|
+
const canvasOffsetLeft = Math.min(rect.left, 0);
|
|
3
|
+
const canvasOffsetTop = Math.min(rect.top, 0);
|
|
4
|
+
const canvasWidth = Math.max(rect.width, rect.right);
|
|
5
|
+
const canvasHeight = Math.max(rect.height, rect.bottom);
|
|
6
|
+
return {
|
|
7
|
+
offsetLeft: canvasOffsetLeft,
|
|
8
|
+
offsetTop: canvasOffsetTop,
|
|
9
|
+
canvasWidth,
|
|
10
|
+
canvasHeight,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const getNarrowerRect: ({ firstRect, secondRect, }: {
|
|
2
|
+
firstRect: DOMRect;
|
|
3
|
+
secondRect: DOMRect;
|
|
4
|
+
}) => DOMRect;
|
|
5
|
+
export declare const getWiderRectAndExpand: ({ firstRect, secondRect, }: {
|
|
6
|
+
firstRect: DOMRect | null;
|
|
7
|
+
secondRect: DOMRect;
|
|
8
|
+
}) => DOMRect;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { roundToExpandRect } from './round-to-expand-rect';
|
|
2
|
+
export const getNarrowerRect = ({ firstRect, secondRect, }) => {
|
|
3
|
+
const left = Math.max(firstRect.left, secondRect.left);
|
|
4
|
+
const top = Math.max(firstRect.top, secondRect.top);
|
|
5
|
+
const bottom = Math.min(firstRect.bottom, secondRect.bottom);
|
|
6
|
+
const right = Math.min(firstRect.right, secondRect.right);
|
|
7
|
+
return new DOMRect(left, top, right - left, bottom - top);
|
|
8
|
+
};
|
|
9
|
+
export const getWiderRectAndExpand = ({ firstRect, secondRect, }) => {
|
|
10
|
+
if (firstRect === null) {
|
|
11
|
+
return roundToExpandRect(secondRect);
|
|
12
|
+
}
|
|
13
|
+
const left = Math.min(firstRect.left, secondRect.left);
|
|
14
|
+
const top = Math.min(firstRect.top, secondRect.top);
|
|
15
|
+
const bottom = Math.max(firstRect.bottom, secondRect.bottom);
|
|
16
|
+
const right = Math.max(firstRect.right, secondRect.right);
|
|
17
|
+
return roundToExpandRect(new DOMRect(left, top, right - left, bottom - top));
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const drawElementToCanvas: (canvas: HTMLCanvasElement | HTMLImageElement | SVGSVGElement, context: OffscreenCanvasRenderingContext2D) => Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { setBorderRadius } from './border-radius';
|
|
2
|
+
import { calculateTransforms } from './calculate-transforms';
|
|
3
|
+
import { turnSvgIntoDrawable } from './compose-svg';
|
|
4
|
+
import { setOpacity } from './opacity';
|
|
5
|
+
import { setTransform } from './transform';
|
|
6
|
+
export const drawElementToCanvas = async (canvas, context) => {
|
|
7
|
+
const { totalMatrix, reset, dimensions, borderRadius, opacity } = calculateTransforms(canvas);
|
|
8
|
+
if (opacity === 0) {
|
|
9
|
+
reset();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const drawable = canvas instanceof SVGSVGElement
|
|
13
|
+
? await turnSvgIntoDrawable(canvas)
|
|
14
|
+
: canvas;
|
|
15
|
+
const finishTransform = setTransform({
|
|
16
|
+
ctx: context,
|
|
17
|
+
transform: totalMatrix,
|
|
18
|
+
});
|
|
19
|
+
const finishBorderRadius = setBorderRadius({
|
|
20
|
+
ctx: context,
|
|
21
|
+
x: dimensions.left,
|
|
22
|
+
y: dimensions.top,
|
|
23
|
+
width: dimensions.width,
|
|
24
|
+
height: dimensions.height,
|
|
25
|
+
borderRadius,
|
|
26
|
+
});
|
|
27
|
+
const finishOpacity = setOpacity({
|
|
28
|
+
ctx: context,
|
|
29
|
+
opacity,
|
|
30
|
+
});
|
|
31
|
+
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
32
|
+
finishOpacity();
|
|
33
|
+
finishBorderRadius();
|
|
34
|
+
finishTransform();
|
|
35
|
+
reset();
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const turnSvgIntoDrawable: (svg: SVGSVGElement) => Promise<HTMLImageElement>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const turnSvgIntoDrawable = (svg) => {
|
|
2
|
+
const originalTransform = svg.style.transform;
|
|
3
|
+
const originalTransformOrigin = svg.style.transformOrigin;
|
|
4
|
+
const originalMarginLeft = svg.style.marginLeft;
|
|
5
|
+
const originalMarginRight = svg.style.marginRight;
|
|
6
|
+
const originalMarginTop = svg.style.marginTop;
|
|
7
|
+
const originalMarginBottom = svg.style.marginBottom;
|
|
8
|
+
svg.style.transform = 'none';
|
|
9
|
+
svg.style.transformOrigin = '';
|
|
10
|
+
// Margins were already included in the positioning calculation,
|
|
11
|
+
// so we need to remove them to avoid double counting.
|
|
12
|
+
svg.style.marginLeft = '0';
|
|
13
|
+
svg.style.marginRight = '0';
|
|
14
|
+
svg.style.marginTop = '0';
|
|
15
|
+
svg.style.marginBottom = '0';
|
|
16
|
+
const svgData = new XMLSerializer().serializeToString(svg);
|
|
17
|
+
svg.style.marginLeft = originalMarginLeft;
|
|
18
|
+
svg.style.marginRight = originalMarginRight;
|
|
19
|
+
svg.style.marginTop = originalMarginTop;
|
|
20
|
+
svg.style.marginBottom = originalMarginBottom;
|
|
21
|
+
svg.style.transform = originalTransform;
|
|
22
|
+
svg.style.transformOrigin = originalTransformOrigin;
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const image = new Image();
|
|
25
|
+
const url = `data:image/svg+xml;base64,${btoa(svgData)}`;
|
|
26
|
+
image.onload = function () {
|
|
27
|
+
resolve(image);
|
|
28
|
+
};
|
|
29
|
+
image.onerror = () => {
|
|
30
|
+
reject(new Error('Failed to convert SVG to image'));
|
|
31
|
+
};
|
|
32
|
+
image.src = url;
|
|
33
|
+
});
|
|
34
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function doRectsIntersect(rect1: DOMRect, rect2: DOMRect): boolean;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { BorderRadiusCorners } from './border-radius';
|
|
2
|
-
export declare const drawBorder: ({ ctx,
|
|
2
|
+
export declare const drawBorder: ({ ctx, rect, borderRadius, computedStyle, }: {
|
|
3
3
|
ctx: OffscreenCanvasRenderingContext2D;
|
|
4
|
-
|
|
5
|
-
y: number;
|
|
6
|
-
width: number;
|
|
7
|
-
height: number;
|
|
4
|
+
rect: DOMRect;
|
|
8
5
|
borderRadius: BorderRadiusCorners;
|
|
9
6
|
computedStyle: CSSStyleDeclaration;
|
|
10
7
|
}) => void;
|
|
@@ -211,7 +211,7 @@ const drawUniformBorder = ({ ctx, x, y, width, height, borderRadius, borderWidth
|
|
|
211
211
|
ctx.closePath();
|
|
212
212
|
ctx.stroke();
|
|
213
213
|
};
|
|
214
|
-
export const drawBorder = ({ ctx,
|
|
214
|
+
export const drawBorder = ({ ctx, rect, borderRadius, computedStyle, }) => {
|
|
215
215
|
const borders = getBorderSideProperties(computedStyle);
|
|
216
216
|
// Check if we have any visible border
|
|
217
217
|
const hasBorder = borders.top.width > 0 ||
|
|
@@ -240,10 +240,10 @@ export const drawBorder = ({ ctx, x, y, width, height, borderRadius, computedSty
|
|
|
240
240
|
// Draw as a single continuous border for continuous dashing
|
|
241
241
|
drawUniformBorder({
|
|
242
242
|
ctx,
|
|
243
|
-
x,
|
|
244
|
-
y,
|
|
245
|
-
width,
|
|
246
|
-
height,
|
|
243
|
+
x: rect.left,
|
|
244
|
+
y: rect.top,
|
|
245
|
+
width: rect.width,
|
|
246
|
+
height: rect.height,
|
|
247
247
|
borderRadius,
|
|
248
248
|
borderWidth: borders.top.width,
|
|
249
249
|
borderColor: borders.top.color,
|
|
@@ -255,10 +255,10 @@ export const drawBorder = ({ ctx, x, y, width, height, borderRadius, computedSty
|
|
|
255
255
|
drawCorner({
|
|
256
256
|
ctx,
|
|
257
257
|
corner: 'topLeft',
|
|
258
|
-
x,
|
|
259
|
-
y,
|
|
260
|
-
width,
|
|
261
|
-
height,
|
|
258
|
+
x: rect.left,
|
|
259
|
+
y: rect.top,
|
|
260
|
+
width: rect.width,
|
|
261
|
+
height: rect.height,
|
|
262
262
|
borderRadius,
|
|
263
263
|
topBorder: borders.top,
|
|
264
264
|
rightBorder: borders.right,
|
|
@@ -268,10 +268,10 @@ export const drawBorder = ({ ctx, x, y, width, height, borderRadius, computedSty
|
|
|
268
268
|
drawCorner({
|
|
269
269
|
ctx,
|
|
270
270
|
corner: 'topRight',
|
|
271
|
-
x,
|
|
272
|
-
y,
|
|
273
|
-
width,
|
|
274
|
-
height,
|
|
271
|
+
x: rect.left,
|
|
272
|
+
y: rect.top,
|
|
273
|
+
width: rect.width,
|
|
274
|
+
height: rect.height,
|
|
275
275
|
borderRadius,
|
|
276
276
|
topBorder: borders.top,
|
|
277
277
|
rightBorder: borders.right,
|
|
@@ -281,10 +281,10 @@ export const drawBorder = ({ ctx, x, y, width, height, borderRadius, computedSty
|
|
|
281
281
|
drawCorner({
|
|
282
282
|
ctx,
|
|
283
283
|
corner: 'bottomRight',
|
|
284
|
-
x,
|
|
285
|
-
y,
|
|
286
|
-
width,
|
|
287
|
-
height,
|
|
284
|
+
x: rect.left,
|
|
285
|
+
y: rect.top,
|
|
286
|
+
width: rect.width,
|
|
287
|
+
height: rect.height,
|
|
288
288
|
borderRadius,
|
|
289
289
|
topBorder: borders.top,
|
|
290
290
|
rightBorder: borders.right,
|
|
@@ -294,10 +294,10 @@ export const drawBorder = ({ ctx, x, y, width, height, borderRadius, computedSty
|
|
|
294
294
|
drawCorner({
|
|
295
295
|
ctx,
|
|
296
296
|
corner: 'bottomLeft',
|
|
297
|
-
x,
|
|
298
|
-
y,
|
|
299
|
-
width,
|
|
300
|
-
height,
|
|
297
|
+
x: rect.left,
|
|
298
|
+
y: rect.top,
|
|
299
|
+
width: rect.width,
|
|
300
|
+
height: rect.height,
|
|
301
301
|
borderRadius,
|
|
302
302
|
topBorder: borders.top,
|
|
303
303
|
rightBorder: borders.right,
|
|
@@ -308,40 +308,40 @@ export const drawBorder = ({ ctx, x, y, width, height, borderRadius, computedSty
|
|
|
308
308
|
drawBorderSide({
|
|
309
309
|
ctx,
|
|
310
310
|
side: 'top',
|
|
311
|
-
x,
|
|
312
|
-
y,
|
|
313
|
-
width,
|
|
314
|
-
height,
|
|
311
|
+
x: rect.left,
|
|
312
|
+
y: rect.top,
|
|
313
|
+
width: rect.width,
|
|
314
|
+
height: rect.height,
|
|
315
315
|
borderRadius,
|
|
316
316
|
borderProperties: borders.top,
|
|
317
317
|
});
|
|
318
318
|
drawBorderSide({
|
|
319
319
|
ctx,
|
|
320
320
|
side: 'right',
|
|
321
|
-
x,
|
|
322
|
-
y,
|
|
323
|
-
width,
|
|
324
|
-
height,
|
|
321
|
+
x: rect.left,
|
|
322
|
+
y: rect.top,
|
|
323
|
+
width: rect.width,
|
|
324
|
+
height: rect.height,
|
|
325
325
|
borderRadius,
|
|
326
326
|
borderProperties: borders.right,
|
|
327
327
|
});
|
|
328
328
|
drawBorderSide({
|
|
329
329
|
ctx,
|
|
330
330
|
side: 'bottom',
|
|
331
|
-
x,
|
|
332
|
-
y,
|
|
333
|
-
width,
|
|
334
|
-
height,
|
|
331
|
+
x: rect.left,
|
|
332
|
+
y: rect.top,
|
|
333
|
+
width: rect.width,
|
|
334
|
+
height: rect.height,
|
|
335
335
|
borderRadius,
|
|
336
336
|
borderProperties: borders.bottom,
|
|
337
337
|
});
|
|
338
338
|
drawBorderSide({
|
|
339
339
|
ctx,
|
|
340
340
|
side: 'left',
|
|
341
|
-
x,
|
|
342
|
-
y,
|
|
343
|
-
width,
|
|
344
|
-
height,
|
|
341
|
+
x: rect.left,
|
|
342
|
+
y: rect.top,
|
|
343
|
+
width: rect.width,
|
|
344
|
+
height: rect.height,
|
|
345
345
|
borderRadius,
|
|
346
346
|
borderProperties: borders.left,
|
|
347
347
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LogLevel } from 'remotion';
|
|
2
|
+
import type { BorderRadiusCorners } from './border-radius';
|
|
3
|
+
interface BoxShadow {
|
|
4
|
+
offsetX: number;
|
|
5
|
+
offsetY: number;
|
|
6
|
+
blurRadius: number;
|
|
7
|
+
color: string;
|
|
8
|
+
inset: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const parseBoxShadow: (boxShadowValue: string) => BoxShadow[];
|
|
11
|
+
export declare const setBoxShadow: ({ ctx, rect, borderRadius, computedStyle, logLevel, }: {
|
|
12
|
+
ctx: OffscreenCanvasRenderingContext2D;
|
|
13
|
+
rect: DOMRect;
|
|
14
|
+
borderRadius: BorderRadiusCorners;
|
|
15
|
+
computedStyle: CSSStyleDeclaration;
|
|
16
|
+
logLevel: LogLevel;
|
|
17
|
+
}) => void;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Internals } from 'remotion';
|
|
2
|
+
import { drawRoundedRectPath } from './draw-rounded';
|
|
3
|
+
export const parseBoxShadow = (boxShadowValue) => {
|
|
4
|
+
if (!boxShadowValue || boxShadowValue === 'none') {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
const shadows = [];
|
|
8
|
+
// Split by comma, but respect rgba() colors
|
|
9
|
+
const shadowStrings = boxShadowValue.split(/,(?![^(]*\))/);
|
|
10
|
+
for (const shadowStr of shadowStrings) {
|
|
11
|
+
const trimmed = shadowStr.trim();
|
|
12
|
+
if (!trimmed || trimmed === 'none') {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const shadow = {
|
|
16
|
+
offsetX: 0,
|
|
17
|
+
offsetY: 0,
|
|
18
|
+
blurRadius: 0,
|
|
19
|
+
color: 'rgba(0, 0, 0, 0.5)',
|
|
20
|
+
inset: false,
|
|
21
|
+
};
|
|
22
|
+
// Check for inset
|
|
23
|
+
shadow.inset = /\binset\b/i.test(trimmed);
|
|
24
|
+
// Remove 'inset' keyword
|
|
25
|
+
let remaining = trimmed.replace(/\binset\b/gi, '').trim();
|
|
26
|
+
// Extract color (can be rgb(), rgba(), hsl(), hsla(), hex, or named color)
|
|
27
|
+
const colorMatch = remaining.match(/(rgba?\([^)]+\)|hsla?\([^)]+\)|#[0-9a-f]{3,8}|[a-z]+)/i);
|
|
28
|
+
if (colorMatch) {
|
|
29
|
+
shadow.color = colorMatch[0];
|
|
30
|
+
remaining = remaining.replace(colorMatch[0], '').trim();
|
|
31
|
+
}
|
|
32
|
+
// Parse remaining numeric values (offset-x offset-y blur spread)
|
|
33
|
+
const numbers = remaining.match(/[+-]?\d*\.?\d+(?:px|em|rem|%)?/gi) || [];
|
|
34
|
+
const values = numbers.map((n) => parseFloat(n) || 0);
|
|
35
|
+
if (values.length >= 2) {
|
|
36
|
+
shadow.offsetX = values[0];
|
|
37
|
+
shadow.offsetY = values[1];
|
|
38
|
+
if (values.length >= 3) {
|
|
39
|
+
shadow.blurRadius = Math.max(0, values[2]); // Blur cannot be negative
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
shadows.push(shadow);
|
|
43
|
+
}
|
|
44
|
+
return shadows;
|
|
45
|
+
};
|
|
46
|
+
export const setBoxShadow = ({ ctx, rect, borderRadius, computedStyle, logLevel, }) => {
|
|
47
|
+
const shadows = parseBoxShadow(computedStyle.boxShadow);
|
|
48
|
+
if (shadows.length === 0) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// Draw shadows from last to first (so first shadow appears on top)
|
|
52
|
+
for (let i = shadows.length - 1; i >= 0; i--) {
|
|
53
|
+
const shadow = shadows[i];
|
|
54
|
+
const newLeft = rect.left + Math.min(shadow.offsetX, 0) - shadow.blurRadius;
|
|
55
|
+
const newRight = rect.right + Math.max(shadow.offsetX, 0) + shadow.blurRadius;
|
|
56
|
+
const newTop = rect.top + Math.min(shadow.offsetY, 0) - shadow.blurRadius;
|
|
57
|
+
const newBottom = rect.bottom + Math.max(shadow.offsetY, 0) + shadow.blurRadius;
|
|
58
|
+
const newRect = new DOMRect(newLeft, newTop, newRight - newLeft, newBottom - newTop);
|
|
59
|
+
const leftOffset = rect.left - newLeft;
|
|
60
|
+
const topOffset = rect.top - newTop;
|
|
61
|
+
const newCanvas = new OffscreenCanvas(newRect.width, newRect.height);
|
|
62
|
+
const newCtx = newCanvas.getContext('2d');
|
|
63
|
+
if (!newCtx) {
|
|
64
|
+
throw new Error('Failed to get context');
|
|
65
|
+
}
|
|
66
|
+
if (shadow.inset) {
|
|
67
|
+
// TODO: Only warn once per render.
|
|
68
|
+
Internals.Log.warn({
|
|
69
|
+
logLevel,
|
|
70
|
+
tag: '@remotion/web-renderer',
|
|
71
|
+
}, 'Detected "box-shadow" with "inset". This is not yet supported in @remotion/web-renderer');
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// Apply shadow properties to canvas
|
|
75
|
+
newCtx.shadowBlur = shadow.blurRadius;
|
|
76
|
+
newCtx.shadowColor = shadow.color;
|
|
77
|
+
newCtx.shadowOffsetX = shadow.offsetX;
|
|
78
|
+
newCtx.shadowOffsetY = shadow.offsetY;
|
|
79
|
+
newCtx.fillStyle = 'black';
|
|
80
|
+
drawRoundedRectPath({
|
|
81
|
+
ctx: newCtx,
|
|
82
|
+
x: leftOffset,
|
|
83
|
+
y: topOffset,
|
|
84
|
+
width: rect.width,
|
|
85
|
+
height: rect.height,
|
|
86
|
+
borderRadius,
|
|
87
|
+
});
|
|
88
|
+
newCtx.fill();
|
|
89
|
+
// Cut out the shape, leaving only shadow
|
|
90
|
+
newCtx.shadowColor = 'transparent';
|
|
91
|
+
newCtx.globalCompositeOperation = 'destination-out';
|
|
92
|
+
drawRoundedRectPath({
|
|
93
|
+
ctx: newCtx,
|
|
94
|
+
x: leftOffset,
|
|
95
|
+
y: topOffset,
|
|
96
|
+
width: rect.width,
|
|
97
|
+
height: rect.height,
|
|
98
|
+
borderRadius,
|
|
99
|
+
});
|
|
100
|
+
newCtx.fill();
|
|
101
|
+
ctx.drawImage(newCanvas, rect.left - leftOffset, rect.top - topOffset);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { LogLevel } from 'remotion';
|
|
2
2
|
import type { DrawFn } from './drawn-fn';
|
|
3
3
|
export type DrawElementToCanvasReturnValue = 'continue' | 'skip-children';
|
|
4
|
-
export declare const drawElementToCanvas: ({ element, context, draw, offsetLeft, offsetTop, logLevel, }: {
|
|
4
|
+
export declare const drawElementToCanvas: ({ element, context, draw, offsetLeft, offsetTop, logLevel, parentRect, }: {
|
|
5
5
|
element: HTMLElement | SVGElement;
|
|
6
6
|
context: OffscreenCanvasRenderingContext2D;
|
|
7
7
|
draw: DrawFn;
|
|
8
8
|
offsetLeft: number;
|
|
9
9
|
offsetTop: number;
|
|
10
10
|
logLevel: LogLevel;
|
|
11
|
+
parentRect: DOMRect;
|
|
11
12
|
}) => Promise<DrawElementToCanvasReturnValue>;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { Internals } from 'remotion';
|
|
2
|
-
import { compose } from '../compose';
|
|
3
|
-
import { getBiggestBoundingClientRect } from '../get-biggest-bounding-client-rect';
|
|
4
1
|
import { calculateTransforms } from './calculate-transforms';
|
|
5
2
|
import { drawElement } from './draw-element';
|
|
6
|
-
import {
|
|
7
|
-
export const drawElementToCanvas = async ({ element, context, draw, offsetLeft, offsetTop, logLevel, }) => {
|
|
8
|
-
const
|
|
3
|
+
import { handle3dTransform } from './handle-3d-transform';
|
|
4
|
+
export const drawElementToCanvas = async ({ element, context, draw, offsetLeft, offsetTop, logLevel, parentRect, }) => {
|
|
5
|
+
const transforms = calculateTransforms({ element, offsetLeft, offsetTop });
|
|
6
|
+
const { totalMatrix, reset, dimensions, opacity, computedStyle } = transforms;
|
|
9
7
|
if (opacity === 0) {
|
|
10
8
|
reset();
|
|
11
9
|
return 'continue';
|
|
@@ -15,40 +13,14 @@ export const drawElementToCanvas = async ({ element, context, draw, offsetLeft,
|
|
|
15
13
|
return 'continue';
|
|
16
14
|
}
|
|
17
15
|
if (!totalMatrix.is2D) {
|
|
18
|
-
|
|
19
|
-
const canvasOffsetLeft = Math.min(biggestBoundingClientRect.left, 0);
|
|
20
|
-
const canvasOffsetTop = Math.min(biggestBoundingClientRect.top, 0);
|
|
21
|
-
const tempCanvasWidth = Math.max(biggestBoundingClientRect.width, biggestBoundingClientRect.right);
|
|
22
|
-
const tempCanvasHeight = Math.max(biggestBoundingClientRect.height, biggestBoundingClientRect.bottom);
|
|
23
|
-
const start = Date.now();
|
|
24
|
-
const tempCanvas = new OffscreenCanvas(tempCanvasWidth, tempCanvasHeight);
|
|
25
|
-
const context2 = tempCanvas.getContext('2d');
|
|
26
|
-
if (!context2) {
|
|
27
|
-
throw new Error('Could not get context');
|
|
28
|
-
}
|
|
29
|
-
await compose({
|
|
16
|
+
await handle3dTransform({
|
|
30
17
|
element,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
18
|
+
totalMatrix,
|
|
19
|
+
parentRect,
|
|
20
|
+
context,
|
|
34
21
|
logLevel,
|
|
35
22
|
});
|
|
36
|
-
const afterCompose = Date.now();
|
|
37
|
-
const transformed = transformIn3d({
|
|
38
|
-
canvasWidth: tempCanvasWidth,
|
|
39
|
-
canvasHeight: tempCanvasHeight,
|
|
40
|
-
matrix: totalMatrix,
|
|
41
|
-
sourceCanvas: tempCanvas,
|
|
42
|
-
offsetLeft: canvasOffsetLeft,
|
|
43
|
-
offsetTop: canvasOffsetTop,
|
|
44
|
-
});
|
|
45
|
-
context.drawImage(transformed, 0, 0);
|
|
46
|
-
const afterDraw = Date.now();
|
|
47
23
|
reset();
|
|
48
|
-
Internals.Log.trace({
|
|
49
|
-
logLevel,
|
|
50
|
-
tag: '@remotion/web-renderer',
|
|
51
|
-
}, `Transforming element in 3D - canvas size: ${tempCanvasWidth}x${tempCanvasHeight} - compose: ${afterCompose - start}ms - draw: ${afterDraw - afterCompose}ms`);
|
|
52
24
|
return 'skip-children';
|
|
53
25
|
}
|
|
54
26
|
await drawElement({
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import type { LogLevel } from 'remotion';
|
|
1
2
|
import type { DrawFn } from './drawn-fn';
|
|
2
|
-
export declare const drawElement: ({
|
|
3
|
-
|
|
3
|
+
export declare const drawElement: ({ rect, computedStyle, context, draw, opacity, totalMatrix, parentRect, logLevel, }: {
|
|
4
|
+
rect: DOMRect;
|
|
4
5
|
computedStyle: CSSStyleDeclaration;
|
|
5
6
|
context: OffscreenCanvasRenderingContext2D;
|
|
6
7
|
opacity: number;
|
|
7
8
|
totalMatrix: DOMMatrix;
|
|
8
9
|
draw: DrawFn;
|
|
9
|
-
|
|
10
|
+
parentRect: DOMRect;
|
|
11
|
+
logLevel: LogLevel;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
cleanupAfterChildren: () => void;
|
|
14
|
+
}>;
|
|
@@ -1,50 +1,96 @@
|
|
|
1
1
|
import { parseBorderRadius, setBorderRadius } from './border-radius';
|
|
2
2
|
import { drawBorder } from './draw-border';
|
|
3
|
+
import { setBoxShadow } from './draw-box-shadow';
|
|
4
|
+
import { drawOutline } from './draw-outline';
|
|
3
5
|
import { setOpacity } from './opacity';
|
|
6
|
+
import { setOverflowHidden } from './overflow';
|
|
7
|
+
import { createCanvasGradient, parseLinearGradient, } from './parse-linear-gradient';
|
|
4
8
|
import { setTransform } from './transform';
|
|
5
|
-
export const drawElement = async ({
|
|
9
|
+
export const drawElement = async ({ rect, computedStyle, context, draw, opacity, totalMatrix, parentRect, logLevel, }) => {
|
|
6
10
|
const background = computedStyle.backgroundColor;
|
|
11
|
+
const { backgroundImage } = computedStyle;
|
|
7
12
|
const borderRadius = parseBorderRadius({
|
|
8
13
|
borderRadius: computedStyle.borderRadius,
|
|
9
|
-
width:
|
|
10
|
-
height:
|
|
14
|
+
width: rect.width,
|
|
15
|
+
height: rect.height,
|
|
11
16
|
});
|
|
12
17
|
const finishTransform = setTransform({
|
|
13
18
|
ctx: context,
|
|
14
19
|
transform: totalMatrix,
|
|
20
|
+
parentRect,
|
|
15
21
|
});
|
|
16
|
-
const
|
|
22
|
+
const finishOpacity = setOpacity({
|
|
23
|
+
ctx: context,
|
|
24
|
+
opacity,
|
|
25
|
+
});
|
|
26
|
+
// Draw box shadow before border radius clip and background
|
|
27
|
+
setBoxShadow({
|
|
17
28
|
ctx: context,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
width: dimensions.width,
|
|
21
|
-
height: dimensions.height,
|
|
29
|
+
computedStyle,
|
|
30
|
+
rect,
|
|
22
31
|
borderRadius,
|
|
32
|
+
logLevel,
|
|
23
33
|
});
|
|
24
|
-
const
|
|
34
|
+
const finishBorderRadius = setBorderRadius({
|
|
25
35
|
ctx: context,
|
|
26
|
-
|
|
36
|
+
rect,
|
|
37
|
+
borderRadius,
|
|
38
|
+
forceClipEvenWhenZero: false,
|
|
27
39
|
});
|
|
28
|
-
|
|
40
|
+
// Try to draw linear gradient first
|
|
41
|
+
let gradientDrawn = false;
|
|
42
|
+
if (backgroundImage && backgroundImage !== 'none') {
|
|
43
|
+
const gradientInfo = parseLinearGradient(backgroundImage);
|
|
44
|
+
if (gradientInfo) {
|
|
45
|
+
const gradient = createCanvasGradient({
|
|
46
|
+
ctx: context,
|
|
47
|
+
rect,
|
|
48
|
+
gradientInfo,
|
|
49
|
+
});
|
|
50
|
+
const originalFillStyle = context.fillStyle;
|
|
51
|
+
context.fillStyle = gradient;
|
|
52
|
+
context.fillRect(rect.left, rect.top, rect.width, rect.height);
|
|
53
|
+
context.fillStyle = originalFillStyle;
|
|
54
|
+
gradientDrawn = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Fallback to solid background color if no gradient was drawn
|
|
58
|
+
if (!gradientDrawn &&
|
|
59
|
+
background &&
|
|
29
60
|
background !== 'transparent' &&
|
|
30
61
|
!(background.startsWith('rgba') &&
|
|
31
62
|
(background.endsWith(', 0)') || background.endsWith(',0')))) {
|
|
32
63
|
const originalFillStyle = context.fillStyle;
|
|
33
64
|
context.fillStyle = background;
|
|
34
|
-
context.fillRect(
|
|
65
|
+
context.fillRect(rect.left, rect.top, rect.width, rect.height);
|
|
35
66
|
context.fillStyle = originalFillStyle;
|
|
36
67
|
}
|
|
37
|
-
await draw({ dimensions, computedStyle, contextToDraw: context });
|
|
68
|
+
await draw({ dimensions: rect, computedStyle, contextToDraw: context });
|
|
38
69
|
drawBorder({
|
|
39
70
|
ctx: context,
|
|
40
|
-
|
|
41
|
-
y: dimensions.top,
|
|
42
|
-
width: dimensions.width,
|
|
43
|
-
height: dimensions.height,
|
|
71
|
+
rect,
|
|
44
72
|
borderRadius,
|
|
45
73
|
computedStyle,
|
|
46
74
|
});
|
|
47
|
-
finishOpacity();
|
|
48
75
|
finishBorderRadius();
|
|
76
|
+
// Drawing outline ignores overflow: hidden, finishing it and starting a new one for the outline
|
|
77
|
+
drawOutline({
|
|
78
|
+
ctx: context,
|
|
79
|
+
rect,
|
|
80
|
+
borderRadius,
|
|
81
|
+
computedStyle,
|
|
82
|
+
});
|
|
83
|
+
const finishOverflowHidden = setOverflowHidden({
|
|
84
|
+
ctx: context,
|
|
85
|
+
rect,
|
|
86
|
+
borderRadius,
|
|
87
|
+
overflowHidden: computedStyle.overflow === 'hidden',
|
|
88
|
+
});
|
|
49
89
|
finishTransform();
|
|
90
|
+
return {
|
|
91
|
+
cleanupAfterChildren: () => {
|
|
92
|
+
finishOpacity();
|
|
93
|
+
finishOverflowHidden();
|
|
94
|
+
},
|
|
95
|
+
};
|
|
50
96
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BorderRadiusCorners } from './border-radius';
|
|
2
|
+
export declare const parseOutlineWidth: (value: string) => number;
|
|
3
|
+
export declare const parseOutlineOffset: (value: string) => number;
|
|
4
|
+
export declare const drawOutline: ({ ctx, rect, borderRadius, computedStyle, }: {
|
|
5
|
+
ctx: OffscreenCanvasRenderingContext2D;
|
|
6
|
+
rect: DOMRect;
|
|
7
|
+
borderRadius: BorderRadiusCorners;
|
|
8
|
+
computedStyle: CSSStyleDeclaration;
|
|
9
|
+
}) => void;
|