@remotion/web-renderer 4.0.388 → 4.0.391
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/audio.js +5 -1
- package/dist/compose.js +26 -1
- package/dist/create-scaffold.js +1 -0
- package/dist/drawing/calculate-transforms.d.ts +1 -0
- package/dist/drawing/calculate-transforms.js +8 -0
- package/dist/drawing/draw-element-to-canvas.d.ts +2 -1
- package/dist/drawing/draw-element-to-canvas.js +3 -14
- package/dist/drawing/text/find-line-breaks.text.d.ts +5 -0
- package/dist/drawing/text/find-line-breaks.text.js +67 -0
- package/dist/drawing/text/get-collapsed-text.d.ts +1 -0
- package/dist/drawing/text/get-collapsed-text.js +46 -0
- package/dist/drawing/text/handle-text-node.d.ts +1 -0
- package/dist/drawing/text/handle-text-node.js +81 -0
- package/dist/esm/index.mjs +247 -49
- package/package.json +5 -5
- package/dist/calculate-transforms.d.ts +0 -9
- package/dist/calculate-transforms.js +0 -74
- package/dist/composable.d.ts +0 -10
- package/dist/composable.js +0 -1
- package/dist/compose-canvas.d.ts +0 -1
- package/dist/compose-canvas.js +0 -12
- package/dist/compose-svg.d.ts +0 -1
- package/dist/compose-svg.js +0 -34
- package/dist/find-capturable-elements.d.ts +0 -2
- package/dist/find-capturable-elements.js +0 -28
- package/dist/parse-transform-origin.d.ts +0 -4
- package/dist/parse-transform-origin.js +0 -7
package/dist/audio.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
const TARGET_NUMBER_OF_CHANNELS = 2;
|
|
2
2
|
const TARGET_SAMPLE_RATE = 48000;
|
|
3
3
|
function mixAudio(waves, length) {
|
|
4
|
-
if (waves.length === 1) {
|
|
4
|
+
if (waves.length === 1 && waves[0].length === length) {
|
|
5
5
|
return waves[0];
|
|
6
6
|
}
|
|
7
7
|
const mixed = new Int16Array(length);
|
|
8
|
+
if (waves.length === 1) {
|
|
9
|
+
mixed.set(waves[0].subarray(0, length));
|
|
10
|
+
return mixed;
|
|
11
|
+
}
|
|
8
12
|
for (let i = 0; i < length; i++) {
|
|
9
13
|
const sum = waves.reduce((acc, wave) => {
|
|
10
14
|
var _a;
|
package/dist/compose.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { drawElementToCanvas } from './drawing/draw-element-to-canvas';
|
|
2
|
+
import { handleTextNode } from './drawing/text/handle-text-node';
|
|
3
|
+
import { turnSvgIntoDrawable } from './drawing/turn-svg-into-drawable';
|
|
2
4
|
export const compose = async (element, context) => {
|
|
3
5
|
const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
|
|
4
6
|
if (node instanceof Element) {
|
|
7
|
+
// SVG does have children, but we process SVG elements in its
|
|
8
|
+
// entirety
|
|
9
|
+
if (node.parentElement instanceof SVGSVGElement) {
|
|
10
|
+
return NodeFilter.FILTER_REJECT;
|
|
11
|
+
}
|
|
5
12
|
const computedStyle = getComputedStyle(node);
|
|
6
13
|
return computedStyle.display === 'none'
|
|
7
14
|
? NodeFilter.FILTER_REJECT
|
|
@@ -12,7 +19,25 @@ export const compose = async (element, context) => {
|
|
|
12
19
|
while (treeWalker.nextNode()) {
|
|
13
20
|
const node = treeWalker.currentNode;
|
|
14
21
|
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
15
|
-
await drawElementToCanvas({
|
|
22
|
+
await drawElementToCanvas({
|
|
23
|
+
element: node,
|
|
24
|
+
context,
|
|
25
|
+
draw: async (dimensions) => {
|
|
26
|
+
const drawable = await (node instanceof SVGSVGElement
|
|
27
|
+
? turnSvgIntoDrawable(node)
|
|
28
|
+
: node instanceof HTMLImageElement
|
|
29
|
+
? node
|
|
30
|
+
: node instanceof HTMLCanvasElement
|
|
31
|
+
? node
|
|
32
|
+
: null);
|
|
33
|
+
if (drawable) {
|
|
34
|
+
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else if (node instanceof Text) {
|
|
40
|
+
await handleTextNode(node, context);
|
|
16
41
|
}
|
|
17
42
|
}
|
|
18
43
|
};
|
package/dist/create-scaffold.js
CHANGED
|
@@ -34,6 +34,7 @@ export async function createScaffold({ width, height, delayRenderTimeoutInMillis
|
|
|
34
34
|
remotion_delayRenderTimeouts: {},
|
|
35
35
|
remotion_puppeteerTimeout: delayRenderTimeoutInMilliseconds,
|
|
36
36
|
remotion_attempt: 0,
|
|
37
|
+
remotion_delayRenderHandles: [],
|
|
37
38
|
};
|
|
38
39
|
const timeUpdater = createRef();
|
|
39
40
|
const collectAssets = createRef();
|
|
@@ -22,6 +22,7 @@ export const calculateTransforms = (element) => {
|
|
|
22
22
|
const transforms = [];
|
|
23
23
|
const toReset = [];
|
|
24
24
|
let opacity = 1;
|
|
25
|
+
let elementComputedStyle = null;
|
|
25
26
|
while (parent) {
|
|
26
27
|
const computedStyle = getComputedStyle(parent);
|
|
27
28
|
// Multiply opacity values from element and all parents
|
|
@@ -29,6 +30,9 @@ export const calculateTransforms = (element) => {
|
|
|
29
30
|
if (parentOpacity && parentOpacity !== '') {
|
|
30
31
|
opacity *= parseFloat(parentOpacity);
|
|
31
32
|
}
|
|
33
|
+
if (parent === element) {
|
|
34
|
+
elementComputedStyle = computedStyle;
|
|
35
|
+
}
|
|
32
36
|
if ((computedStyle.transform && computedStyle.transform !== 'none') ||
|
|
33
37
|
parent === element) {
|
|
34
38
|
const toParse = computedStyle.transform === 'none' || computedStyle.transform === ''
|
|
@@ -67,6 +71,9 @@ export const calculateTransforms = (element) => {
|
|
|
67
71
|
.translate(-globalTransformOrigin.x, -globalTransformOrigin.y);
|
|
68
72
|
totalMatrix.multiplySelf(transformMatrix);
|
|
69
73
|
}
|
|
74
|
+
if (!elementComputedStyle) {
|
|
75
|
+
throw new Error('Element computed style not found');
|
|
76
|
+
}
|
|
70
77
|
return {
|
|
71
78
|
dimensions,
|
|
72
79
|
totalMatrix,
|
|
@@ -77,5 +84,6 @@ export const calculateTransforms = (element) => {
|
|
|
77
84
|
},
|
|
78
85
|
nativeTransformOrigin,
|
|
79
86
|
opacity,
|
|
87
|
+
computedStyle: elementComputedStyle,
|
|
80
88
|
};
|
|
81
89
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export declare const drawElementToCanvas: ({ element, context, }: {
|
|
1
|
+
export declare const drawElementToCanvas: ({ element, context, draw, }: {
|
|
2
2
|
element: HTMLElement | SVGElement;
|
|
3
3
|
context: OffscreenCanvasRenderingContext2D;
|
|
4
|
+
draw: (dimensions: DOMRect, computedStyle: CSSStyleDeclaration) => Promise<void> | void;
|
|
4
5
|
}) => Promise<void>;
|
|
@@ -3,9 +3,8 @@ import { calculateTransforms } from './calculate-transforms';
|
|
|
3
3
|
import { drawBorder } from './draw-border';
|
|
4
4
|
import { setOpacity } from './opacity';
|
|
5
5
|
import { setTransform } from './transform';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const { totalMatrix, reset, dimensions, opacity } = calculateTransforms(element);
|
|
6
|
+
export const drawElementToCanvas = async ({ element, context, draw, }) => {
|
|
7
|
+
const { totalMatrix, reset, dimensions, opacity, computedStyle } = calculateTransforms(element);
|
|
9
8
|
if (opacity === 0) {
|
|
10
9
|
reset();
|
|
11
10
|
return;
|
|
@@ -14,7 +13,6 @@ export const drawElementToCanvas = async ({ element, context, }) => {
|
|
|
14
13
|
reset();
|
|
15
14
|
return;
|
|
16
15
|
}
|
|
17
|
-
const computedStyle = getComputedStyle(element);
|
|
18
16
|
const background = computedStyle.backgroundColor;
|
|
19
17
|
const borderRadius = parseBorderRadius({
|
|
20
18
|
borderRadius: computedStyle.borderRadius,
|
|
@@ -37,13 +35,6 @@ export const drawElementToCanvas = async ({ element, context, }) => {
|
|
|
37
35
|
ctx: context,
|
|
38
36
|
opacity,
|
|
39
37
|
});
|
|
40
|
-
const drawable = element instanceof SVGSVGElement
|
|
41
|
-
? await turnSvgIntoDrawable(element)
|
|
42
|
-
: element instanceof HTMLImageElement
|
|
43
|
-
? element
|
|
44
|
-
: element instanceof HTMLCanvasElement
|
|
45
|
-
? element
|
|
46
|
-
: null;
|
|
47
38
|
if (background &&
|
|
48
39
|
background !== 'transparent' &&
|
|
49
40
|
!(background.startsWith('rgba') &&
|
|
@@ -53,9 +44,7 @@ export const drawElementToCanvas = async ({ element, context, }) => {
|
|
|
53
44
|
context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
54
45
|
context.fillStyle = originalFillStyle;
|
|
55
46
|
}
|
|
56
|
-
|
|
57
|
-
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
58
|
-
}
|
|
47
|
+
await draw(dimensions, computedStyle);
|
|
59
48
|
drawBorder({
|
|
60
49
|
ctx: context,
|
|
61
50
|
x: dimensions.left,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getCollapsedText } from './get-collapsed-text';
|
|
2
|
+
export function findLineBreaks(span, rtl) {
|
|
3
|
+
const textNode = span.childNodes[0];
|
|
4
|
+
const originalText = textNode.textContent;
|
|
5
|
+
const originalRect = span.getBoundingClientRect();
|
|
6
|
+
const computedStyle = getComputedStyle(span);
|
|
7
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'word' });
|
|
8
|
+
const segments = segmenter.segment(originalText);
|
|
9
|
+
const words = Array.from(segments).map((s) => s.segment);
|
|
10
|
+
const lines = [];
|
|
11
|
+
let currentLine = '';
|
|
12
|
+
let testText = '';
|
|
13
|
+
let previousRect = originalRect;
|
|
14
|
+
textNode.textContent = '';
|
|
15
|
+
for (let i = 0; i < words.length; i += 1) {
|
|
16
|
+
const word = words[i];
|
|
17
|
+
testText += word;
|
|
18
|
+
let wordsToAdd = word;
|
|
19
|
+
while (typeof words[i + 1] !== 'undefined' && words[i + 1].trim() === '') {
|
|
20
|
+
testText += words[i + 1];
|
|
21
|
+
wordsToAdd += words[i + 1];
|
|
22
|
+
i++;
|
|
23
|
+
}
|
|
24
|
+
previousRect = span.getBoundingClientRect();
|
|
25
|
+
textNode.textContent = testText;
|
|
26
|
+
const collapsedText = getCollapsedText(span);
|
|
27
|
+
textNode.textContent = collapsedText;
|
|
28
|
+
const rect = span.getBoundingClientRect();
|
|
29
|
+
const currentHeight = rect.height;
|
|
30
|
+
// If height changed significantly, we had a line break
|
|
31
|
+
if (previousRect &&
|
|
32
|
+
previousRect.height !== 0 &&
|
|
33
|
+
Math.abs(currentHeight - previousRect.height) > 2) {
|
|
34
|
+
const offsetHorizontal = rtl
|
|
35
|
+
? previousRect.right - originalRect.right
|
|
36
|
+
: previousRect.left - originalRect.left;
|
|
37
|
+
const shouldCollapse = !computedStyle.whiteSpaceCollapse.includes('preserve');
|
|
38
|
+
lines.push({
|
|
39
|
+
text: shouldCollapse ? currentLine.trim() : currentLine,
|
|
40
|
+
offsetTop: currentHeight - previousRect.height,
|
|
41
|
+
offsetHorizontal,
|
|
42
|
+
});
|
|
43
|
+
currentLine = wordsToAdd;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
currentLine += wordsToAdd;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Add the last line
|
|
50
|
+
if (currentLine) {
|
|
51
|
+
textNode.textContent = testText;
|
|
52
|
+
const rects = span.getClientRects();
|
|
53
|
+
const rect = span.getBoundingClientRect();
|
|
54
|
+
const lastRect = rects[rects.length - 1];
|
|
55
|
+
const offsetHorizontal = rtl
|
|
56
|
+
? lastRect.right - originalRect.right
|
|
57
|
+
: lastRect.left - originalRect.left;
|
|
58
|
+
lines.push({
|
|
59
|
+
text: currentLine,
|
|
60
|
+
offsetTop: rect.height - previousRect.height,
|
|
61
|
+
offsetHorizontal,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Reset to original text
|
|
65
|
+
textNode.textContent = originalText;
|
|
66
|
+
return lines;
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getCollapsedText: (span: HTMLSpanElement) => string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const getCollapsedText = (span) => {
|
|
2
|
+
const textNode = span.firstChild;
|
|
3
|
+
if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
|
|
4
|
+
throw new Error('Span must contain a single text node');
|
|
5
|
+
}
|
|
6
|
+
const originalText = textNode.textContent || '';
|
|
7
|
+
let collapsedText = originalText;
|
|
8
|
+
// Helper to measure width
|
|
9
|
+
const measureWidth = (text) => {
|
|
10
|
+
textNode.textContent = text;
|
|
11
|
+
return span.getBoundingClientRect().width;
|
|
12
|
+
};
|
|
13
|
+
const originalWidth = measureWidth(originalText);
|
|
14
|
+
// Test leading whitespace
|
|
15
|
+
if (/^\s/.test(collapsedText)) {
|
|
16
|
+
const trimmedLeading = collapsedText.replace(/^\s+/, '');
|
|
17
|
+
const newWidth = measureWidth(trimmedLeading);
|
|
18
|
+
if (newWidth === originalWidth) {
|
|
19
|
+
// Whitespace was collapsed by the browser
|
|
20
|
+
collapsedText = trimmedLeading;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Test trailing whitespace (on current collapsed text)
|
|
24
|
+
if (/\s$/.test(collapsedText)) {
|
|
25
|
+
const currentWidth = measureWidth(collapsedText);
|
|
26
|
+
const trimmedTrailing = collapsedText.replace(/\s+$/, '');
|
|
27
|
+
const newWidth = measureWidth(trimmedTrailing);
|
|
28
|
+
if (newWidth === currentWidth) {
|
|
29
|
+
// Whitespace was collapsed by the browser
|
|
30
|
+
collapsedText = trimmedTrailing;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Test internal duplicate whitespace (on current collapsed text)
|
|
34
|
+
if (/\s\s/.test(collapsedText)) {
|
|
35
|
+
const currentWidth = measureWidth(collapsedText);
|
|
36
|
+
const collapsedInternal = collapsedText.replace(/\s\s+/g, ' ');
|
|
37
|
+
const newWidth = measureWidth(collapsedInternal);
|
|
38
|
+
if (newWidth === currentWidth) {
|
|
39
|
+
// Whitespace was collapsed by the browser
|
|
40
|
+
collapsedText = collapsedInternal;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Restore original text
|
|
44
|
+
textNode.textContent = originalText;
|
|
45
|
+
return collapsedText;
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const handleTextNode: (node: Text, context: OffscreenCanvasRenderingContext2D) => Promise<void>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Supported:
|
|
2
|
+
// - fontFamily
|
|
3
|
+
// - fontSize
|
|
4
|
+
// - fontWeight
|
|
5
|
+
// - color
|
|
6
|
+
// - lineHeight
|
|
7
|
+
// - direction
|
|
8
|
+
// - letterSpacing
|
|
9
|
+
// - textTransform
|
|
10
|
+
// Not supported:
|
|
11
|
+
// - writingMode
|
|
12
|
+
// - textDecoration
|
|
13
|
+
import { Internals } from 'remotion';
|
|
14
|
+
import { drawElementToCanvas } from '../draw-element-to-canvas';
|
|
15
|
+
import { findLineBreaks } from './find-line-breaks.text';
|
|
16
|
+
import { getCollapsedText } from './get-collapsed-text';
|
|
17
|
+
const applyTextTransform = (text, transform) => {
|
|
18
|
+
if (transform === 'uppercase') {
|
|
19
|
+
return text.toUpperCase();
|
|
20
|
+
}
|
|
21
|
+
if (transform === 'lowercase') {
|
|
22
|
+
return text.toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
if (transform === 'capitalize') {
|
|
25
|
+
return text.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
26
|
+
}
|
|
27
|
+
return text;
|
|
28
|
+
};
|
|
29
|
+
export const handleTextNode = async (node, context) => {
|
|
30
|
+
const span = document.createElement('span');
|
|
31
|
+
const parent = node.parentNode;
|
|
32
|
+
if (!parent) {
|
|
33
|
+
throw new Error('Text node has no parent');
|
|
34
|
+
}
|
|
35
|
+
parent.insertBefore(span, node);
|
|
36
|
+
span.appendChild(node);
|
|
37
|
+
await drawElementToCanvas({
|
|
38
|
+
context,
|
|
39
|
+
element: span,
|
|
40
|
+
draw(rect, style) {
|
|
41
|
+
const { fontFamily, fontSize, fontWeight, color, lineHeight, direction, writingMode, letterSpacing, textTransform, } = style;
|
|
42
|
+
const isVertical = writingMode !== 'horizontal-tb';
|
|
43
|
+
if (isVertical) {
|
|
44
|
+
// TODO: Only warn once per render.
|
|
45
|
+
Internals.Log.warn({
|
|
46
|
+
logLevel: 'warn',
|
|
47
|
+
tag: '@remotion/web-renderer',
|
|
48
|
+
}, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
context.save();
|
|
52
|
+
context.font = `${fontWeight} ${fontSize} ${fontFamily}`;
|
|
53
|
+
context.fillStyle = color;
|
|
54
|
+
context.letterSpacing = letterSpacing;
|
|
55
|
+
const fontSizePx = parseFloat(fontSize);
|
|
56
|
+
// TODO: This is not necessarily correct, need to create text and measure to know for sure
|
|
57
|
+
const lineHeightPx = lineHeight === 'normal' ? 1.2 * fontSizePx : parseFloat(lineHeight);
|
|
58
|
+
const baselineOffset = (lineHeightPx - fontSizePx) / 2;
|
|
59
|
+
const isRTL = direction === 'rtl';
|
|
60
|
+
context.textAlign = isRTL ? 'right' : 'left';
|
|
61
|
+
context.textBaseline = 'top';
|
|
62
|
+
const originalText = span.textContent;
|
|
63
|
+
const collapsedText = getCollapsedText(span);
|
|
64
|
+
const transformedText = applyTextTransform(collapsedText, textTransform);
|
|
65
|
+
span.textContent = transformedText;
|
|
66
|
+
// For RTL text, fill from the right edge instead of left
|
|
67
|
+
const xPosition = isRTL ? rect.right : rect.left;
|
|
68
|
+
const lines = findLineBreaks(span, isRTL);
|
|
69
|
+
let offsetTop = 0;
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
context.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + baselineOffset + offsetTop);
|
|
72
|
+
offsetTop += line.offsetTop;
|
|
73
|
+
}
|
|
74
|
+
span.textContent = originalText;
|
|
75
|
+
context.restore();
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
// Undo the layout manipulation
|
|
79
|
+
parent.insertBefore(node, span);
|
|
80
|
+
parent.removeChild(span);
|
|
81
|
+
};
|
package/dist/esm/index.mjs
CHANGED
|
@@ -8259,7 +8259,7 @@ class Output {
|
|
|
8259
8259
|
*/
|
|
8260
8260
|
|
|
8261
8261
|
// src/render-media-on-web.tsx
|
|
8262
|
-
import { Internals as
|
|
8262
|
+
import { Internals as Internals4 } from "remotion";
|
|
8263
8263
|
|
|
8264
8264
|
// src/add-sample.ts
|
|
8265
8265
|
var addVideoSampleAndCloseFrame = async (frameToEncode, videoSampleSource) => {
|
|
@@ -8345,10 +8345,14 @@ var handleArtifacts = () => {
|
|
|
8345
8345
|
var TARGET_NUMBER_OF_CHANNELS = 2;
|
|
8346
8346
|
var TARGET_SAMPLE_RATE = 48000;
|
|
8347
8347
|
function mixAudio(waves, length) {
|
|
8348
|
-
if (waves.length === 1) {
|
|
8348
|
+
if (waves.length === 1 && waves[0].length === length) {
|
|
8349
8349
|
return waves[0];
|
|
8350
8350
|
}
|
|
8351
8351
|
const mixed = new Int16Array(length);
|
|
8352
|
+
if (waves.length === 1) {
|
|
8353
|
+
mixed.set(waves[0].subarray(0, length));
|
|
8354
|
+
return mixed;
|
|
8355
|
+
}
|
|
8352
8356
|
for (let i = 0;i < length; i++) {
|
|
8353
8357
|
const sum = waves.reduce((acc, wave2) => {
|
|
8354
8358
|
return acc + (wave2[i] ?? 0);
|
|
@@ -8480,7 +8484,8 @@ async function createScaffold({
|
|
|
8480
8484
|
remotion_renderReady: true,
|
|
8481
8485
|
remotion_delayRenderTimeouts: {},
|
|
8482
8486
|
remotion_puppeteerTimeout: delayRenderTimeoutInMilliseconds,
|
|
8483
|
-
remotion_attempt: 0
|
|
8487
|
+
remotion_attempt: 0,
|
|
8488
|
+
remotion_delayRenderHandles: []
|
|
8484
8489
|
};
|
|
8485
8490
|
const timeUpdater = createRef();
|
|
8486
8491
|
const collectAssets = createRef();
|
|
@@ -8826,12 +8831,16 @@ var calculateTransforms = (element) => {
|
|
|
8826
8831
|
const transforms = [];
|
|
8827
8832
|
const toReset = [];
|
|
8828
8833
|
let opacity = 1;
|
|
8834
|
+
let elementComputedStyle = null;
|
|
8829
8835
|
while (parent) {
|
|
8830
8836
|
const computedStyle = getComputedStyle(parent);
|
|
8831
8837
|
const parentOpacity = computedStyle.opacity;
|
|
8832
8838
|
if (parentOpacity && parentOpacity !== "") {
|
|
8833
8839
|
opacity *= parseFloat(parentOpacity);
|
|
8834
8840
|
}
|
|
8841
|
+
if (parent === element) {
|
|
8842
|
+
elementComputedStyle = computedStyle;
|
|
8843
|
+
}
|
|
8835
8844
|
if (computedStyle.transform && computedStyle.transform !== "none" || parent === element) {
|
|
8836
8845
|
const toParse = computedStyle.transform === "none" || computedStyle.transform === "" ? undefined : computedStyle.transform;
|
|
8837
8846
|
const matrix = new DOMMatrix(toParse);
|
|
@@ -8864,6 +8873,9 @@ var calculateTransforms = (element) => {
|
|
|
8864
8873
|
const transformMatrix = new DOMMatrix().translate(globalTransformOrigin.x, globalTransformOrigin.y).multiply(transform.matrix).translate(-globalTransformOrigin.x, -globalTransformOrigin.y);
|
|
8865
8874
|
totalMatrix.multiplySelf(transformMatrix);
|
|
8866
8875
|
}
|
|
8876
|
+
if (!elementComputedStyle) {
|
|
8877
|
+
throw new Error("Element computed style not found");
|
|
8878
|
+
}
|
|
8867
8879
|
return {
|
|
8868
8880
|
dimensions,
|
|
8869
8881
|
totalMatrix,
|
|
@@ -8873,7 +8885,8 @@ var calculateTransforms = (element) => {
|
|
|
8873
8885
|
}
|
|
8874
8886
|
},
|
|
8875
8887
|
nativeTransformOrigin,
|
|
8876
|
-
opacity
|
|
8888
|
+
opacity,
|
|
8889
|
+
computedStyle: elementComputedStyle
|
|
8877
8890
|
};
|
|
8878
8891
|
};
|
|
8879
8892
|
|
|
@@ -8987,46 +9000,13 @@ var setTransform = ({
|
|
|
8987
9000
|
};
|
|
8988
9001
|
};
|
|
8989
9002
|
|
|
8990
|
-
// src/drawing/turn-svg-into-drawable.ts
|
|
8991
|
-
var turnSvgIntoDrawable = (svg) => {
|
|
8992
|
-
const originalTransform = svg.style.transform;
|
|
8993
|
-
const originalTransformOrigin = svg.style.transformOrigin;
|
|
8994
|
-
const originalMarginLeft = svg.style.marginLeft;
|
|
8995
|
-
const originalMarginRight = svg.style.marginRight;
|
|
8996
|
-
const originalMarginTop = svg.style.marginTop;
|
|
8997
|
-
const originalMarginBottom = svg.style.marginBottom;
|
|
8998
|
-
svg.style.transform = "none";
|
|
8999
|
-
svg.style.transformOrigin = "";
|
|
9000
|
-
svg.style.marginLeft = "0";
|
|
9001
|
-
svg.style.marginRight = "0";
|
|
9002
|
-
svg.style.marginTop = "0";
|
|
9003
|
-
svg.style.marginBottom = "0";
|
|
9004
|
-
const svgData = new XMLSerializer().serializeToString(svg);
|
|
9005
|
-
svg.style.marginLeft = originalMarginLeft;
|
|
9006
|
-
svg.style.marginRight = originalMarginRight;
|
|
9007
|
-
svg.style.marginTop = originalMarginTop;
|
|
9008
|
-
svg.style.marginBottom = originalMarginBottom;
|
|
9009
|
-
svg.style.transform = originalTransform;
|
|
9010
|
-
svg.style.transformOrigin = originalTransformOrigin;
|
|
9011
|
-
return new Promise((resolve, reject) => {
|
|
9012
|
-
const image = new Image;
|
|
9013
|
-
const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
|
|
9014
|
-
image.onload = function() {
|
|
9015
|
-
resolve(image);
|
|
9016
|
-
};
|
|
9017
|
-
image.onerror = () => {
|
|
9018
|
-
reject(new Error("Failed to convert SVG to image"));
|
|
9019
|
-
};
|
|
9020
|
-
image.src = url2;
|
|
9021
|
-
});
|
|
9022
|
-
};
|
|
9023
|
-
|
|
9024
9003
|
// src/drawing/draw-element-to-canvas.ts
|
|
9025
9004
|
var drawElementToCanvas = async ({
|
|
9026
9005
|
element,
|
|
9027
|
-
context
|
|
9006
|
+
context,
|
|
9007
|
+
draw
|
|
9028
9008
|
}) => {
|
|
9029
|
-
const { totalMatrix, reset, dimensions, opacity } = calculateTransforms(element);
|
|
9009
|
+
const { totalMatrix, reset, dimensions, opacity, computedStyle } = calculateTransforms(element);
|
|
9030
9010
|
if (opacity === 0) {
|
|
9031
9011
|
reset();
|
|
9032
9012
|
return;
|
|
@@ -9035,7 +9015,6 @@ var drawElementToCanvas = async ({
|
|
|
9035
9015
|
reset();
|
|
9036
9016
|
return;
|
|
9037
9017
|
}
|
|
9038
|
-
const computedStyle = getComputedStyle(element);
|
|
9039
9018
|
const background = computedStyle.backgroundColor;
|
|
9040
9019
|
const borderRadius = parseBorderRadius({
|
|
9041
9020
|
borderRadius: computedStyle.borderRadius,
|
|
@@ -9058,16 +9037,13 @@ var drawElementToCanvas = async ({
|
|
|
9058
9037
|
ctx: context,
|
|
9059
9038
|
opacity
|
|
9060
9039
|
});
|
|
9061
|
-
const drawable = element instanceof SVGSVGElement ? await turnSvgIntoDrawable(element) : element instanceof HTMLImageElement ? element : element instanceof HTMLCanvasElement ? element : null;
|
|
9062
9040
|
if (background && background !== "transparent" && !(background.startsWith("rgba") && (background.endsWith(", 0)") || background.endsWith(",0")))) {
|
|
9063
9041
|
const originalFillStyle = context.fillStyle;
|
|
9064
9042
|
context.fillStyle = background;
|
|
9065
9043
|
context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
9066
9044
|
context.fillStyle = originalFillStyle;
|
|
9067
9045
|
}
|
|
9068
|
-
|
|
9069
|
-
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
9070
|
-
}
|
|
9046
|
+
await draw(dimensions, computedStyle);
|
|
9071
9047
|
drawBorder({
|
|
9072
9048
|
ctx: context,
|
|
9073
9049
|
x: dimensions.left,
|
|
@@ -9083,10 +9059,221 @@ var drawElementToCanvas = async ({
|
|
|
9083
9059
|
reset();
|
|
9084
9060
|
};
|
|
9085
9061
|
|
|
9062
|
+
// src/drawing/text/handle-text-node.ts
|
|
9063
|
+
import { Internals as Internals3 } from "remotion";
|
|
9064
|
+
|
|
9065
|
+
// src/drawing/text/get-collapsed-text.ts
|
|
9066
|
+
var getCollapsedText = (span) => {
|
|
9067
|
+
const textNode = span.firstChild;
|
|
9068
|
+
if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
|
|
9069
|
+
throw new Error("Span must contain a single text node");
|
|
9070
|
+
}
|
|
9071
|
+
const originalText = textNode.textContent || "";
|
|
9072
|
+
let collapsedText = originalText;
|
|
9073
|
+
const measureWidth = (text) => {
|
|
9074
|
+
textNode.textContent = text;
|
|
9075
|
+
return span.getBoundingClientRect().width;
|
|
9076
|
+
};
|
|
9077
|
+
const originalWidth = measureWidth(originalText);
|
|
9078
|
+
if (/^\s/.test(collapsedText)) {
|
|
9079
|
+
const trimmedLeading = collapsedText.replace(/^\s+/, "");
|
|
9080
|
+
const newWidth = measureWidth(trimmedLeading);
|
|
9081
|
+
if (newWidth === originalWidth) {
|
|
9082
|
+
collapsedText = trimmedLeading;
|
|
9083
|
+
}
|
|
9084
|
+
}
|
|
9085
|
+
if (/\s$/.test(collapsedText)) {
|
|
9086
|
+
const currentWidth = measureWidth(collapsedText);
|
|
9087
|
+
const trimmedTrailing = collapsedText.replace(/\s+$/, "");
|
|
9088
|
+
const newWidth = measureWidth(trimmedTrailing);
|
|
9089
|
+
if (newWidth === currentWidth) {
|
|
9090
|
+
collapsedText = trimmedTrailing;
|
|
9091
|
+
}
|
|
9092
|
+
}
|
|
9093
|
+
if (/\s\s/.test(collapsedText)) {
|
|
9094
|
+
const currentWidth = measureWidth(collapsedText);
|
|
9095
|
+
const collapsedInternal = collapsedText.replace(/\s\s+/g, " ");
|
|
9096
|
+
const newWidth = measureWidth(collapsedInternal);
|
|
9097
|
+
if (newWidth === currentWidth) {
|
|
9098
|
+
collapsedText = collapsedInternal;
|
|
9099
|
+
}
|
|
9100
|
+
}
|
|
9101
|
+
textNode.textContent = originalText;
|
|
9102
|
+
return collapsedText;
|
|
9103
|
+
};
|
|
9104
|
+
|
|
9105
|
+
// src/drawing/text/find-line-breaks.text.ts
|
|
9106
|
+
function findLineBreaks(span, rtl) {
|
|
9107
|
+
const textNode = span.childNodes[0];
|
|
9108
|
+
const originalText = textNode.textContent;
|
|
9109
|
+
const originalRect = span.getBoundingClientRect();
|
|
9110
|
+
const computedStyle = getComputedStyle(span);
|
|
9111
|
+
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
|
|
9112
|
+
const segments = segmenter.segment(originalText);
|
|
9113
|
+
const words = Array.from(segments).map((s) => s.segment);
|
|
9114
|
+
const lines = [];
|
|
9115
|
+
let currentLine = "";
|
|
9116
|
+
let testText = "";
|
|
9117
|
+
let previousRect = originalRect;
|
|
9118
|
+
textNode.textContent = "";
|
|
9119
|
+
for (let i = 0;i < words.length; i += 1) {
|
|
9120
|
+
const word = words[i];
|
|
9121
|
+
testText += word;
|
|
9122
|
+
let wordsToAdd = word;
|
|
9123
|
+
while (typeof words[i + 1] !== "undefined" && words[i + 1].trim() === "") {
|
|
9124
|
+
testText += words[i + 1];
|
|
9125
|
+
wordsToAdd += words[i + 1];
|
|
9126
|
+
i++;
|
|
9127
|
+
}
|
|
9128
|
+
previousRect = span.getBoundingClientRect();
|
|
9129
|
+
textNode.textContent = testText;
|
|
9130
|
+
const collapsedText = getCollapsedText(span);
|
|
9131
|
+
textNode.textContent = collapsedText;
|
|
9132
|
+
const rect = span.getBoundingClientRect();
|
|
9133
|
+
const currentHeight = rect.height;
|
|
9134
|
+
if (previousRect && previousRect.height !== 0 && Math.abs(currentHeight - previousRect.height) > 2) {
|
|
9135
|
+
const offsetHorizontal = rtl ? previousRect.right - originalRect.right : previousRect.left - originalRect.left;
|
|
9136
|
+
const shouldCollapse = !computedStyle.whiteSpaceCollapse.includes("preserve");
|
|
9137
|
+
lines.push({
|
|
9138
|
+
text: shouldCollapse ? currentLine.trim() : currentLine,
|
|
9139
|
+
offsetTop: currentHeight - previousRect.height,
|
|
9140
|
+
offsetHorizontal
|
|
9141
|
+
});
|
|
9142
|
+
currentLine = wordsToAdd;
|
|
9143
|
+
} else {
|
|
9144
|
+
currentLine += wordsToAdd;
|
|
9145
|
+
}
|
|
9146
|
+
}
|
|
9147
|
+
if (currentLine) {
|
|
9148
|
+
textNode.textContent = testText;
|
|
9149
|
+
const rects = span.getClientRects();
|
|
9150
|
+
const rect = span.getBoundingClientRect();
|
|
9151
|
+
const lastRect = rects[rects.length - 1];
|
|
9152
|
+
const offsetHorizontal = rtl ? lastRect.right - originalRect.right : lastRect.left - originalRect.left;
|
|
9153
|
+
lines.push({
|
|
9154
|
+
text: currentLine,
|
|
9155
|
+
offsetTop: rect.height - previousRect.height,
|
|
9156
|
+
offsetHorizontal
|
|
9157
|
+
});
|
|
9158
|
+
}
|
|
9159
|
+
textNode.textContent = originalText;
|
|
9160
|
+
return lines;
|
|
9161
|
+
}
|
|
9162
|
+
|
|
9163
|
+
// src/drawing/text/handle-text-node.ts
|
|
9164
|
+
var applyTextTransform = (text, transform) => {
|
|
9165
|
+
if (transform === "uppercase") {
|
|
9166
|
+
return text.toUpperCase();
|
|
9167
|
+
}
|
|
9168
|
+
if (transform === "lowercase") {
|
|
9169
|
+
return text.toLowerCase();
|
|
9170
|
+
}
|
|
9171
|
+
if (transform === "capitalize") {
|
|
9172
|
+
return text.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
9173
|
+
}
|
|
9174
|
+
return text;
|
|
9175
|
+
};
|
|
9176
|
+
var handleTextNode = async (node, context) => {
|
|
9177
|
+
const span = document.createElement("span");
|
|
9178
|
+
const parent = node.parentNode;
|
|
9179
|
+
if (!parent) {
|
|
9180
|
+
throw new Error("Text node has no parent");
|
|
9181
|
+
}
|
|
9182
|
+
parent.insertBefore(span, node);
|
|
9183
|
+
span.appendChild(node);
|
|
9184
|
+
await drawElementToCanvas({
|
|
9185
|
+
context,
|
|
9186
|
+
element: span,
|
|
9187
|
+
draw(rect, style) {
|
|
9188
|
+
const {
|
|
9189
|
+
fontFamily,
|
|
9190
|
+
fontSize,
|
|
9191
|
+
fontWeight,
|
|
9192
|
+
color,
|
|
9193
|
+
lineHeight,
|
|
9194
|
+
direction,
|
|
9195
|
+
writingMode,
|
|
9196
|
+
letterSpacing,
|
|
9197
|
+
textTransform
|
|
9198
|
+
} = style;
|
|
9199
|
+
const isVertical = writingMode !== "horizontal-tb";
|
|
9200
|
+
if (isVertical) {
|
|
9201
|
+
Internals3.Log.warn({
|
|
9202
|
+
logLevel: "warn",
|
|
9203
|
+
tag: "@remotion/web-renderer"
|
|
9204
|
+
}, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
|
|
9205
|
+
return;
|
|
9206
|
+
}
|
|
9207
|
+
context.save();
|
|
9208
|
+
context.font = `${fontWeight} ${fontSize} ${fontFamily}`;
|
|
9209
|
+
context.fillStyle = color;
|
|
9210
|
+
context.letterSpacing = letterSpacing;
|
|
9211
|
+
const fontSizePx = parseFloat(fontSize);
|
|
9212
|
+
const lineHeightPx = lineHeight === "normal" ? 1.2 * fontSizePx : parseFloat(lineHeight);
|
|
9213
|
+
const baselineOffset = (lineHeightPx - fontSizePx) / 2;
|
|
9214
|
+
const isRTL = direction === "rtl";
|
|
9215
|
+
context.textAlign = isRTL ? "right" : "left";
|
|
9216
|
+
context.textBaseline = "top";
|
|
9217
|
+
const originalText = span.textContent;
|
|
9218
|
+
const collapsedText = getCollapsedText(span);
|
|
9219
|
+
const transformedText = applyTextTransform(collapsedText, textTransform);
|
|
9220
|
+
span.textContent = transformedText;
|
|
9221
|
+
const xPosition = isRTL ? rect.right : rect.left;
|
|
9222
|
+
const lines = findLineBreaks(span, isRTL);
|
|
9223
|
+
let offsetTop = 0;
|
|
9224
|
+
for (const line of lines) {
|
|
9225
|
+
context.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + baselineOffset + offsetTop);
|
|
9226
|
+
offsetTop += line.offsetTop;
|
|
9227
|
+
}
|
|
9228
|
+
span.textContent = originalText;
|
|
9229
|
+
context.restore();
|
|
9230
|
+
}
|
|
9231
|
+
});
|
|
9232
|
+
parent.insertBefore(node, span);
|
|
9233
|
+
parent.removeChild(span);
|
|
9234
|
+
};
|
|
9235
|
+
|
|
9236
|
+
// src/drawing/turn-svg-into-drawable.ts
|
|
9237
|
+
var turnSvgIntoDrawable = (svg) => {
|
|
9238
|
+
const originalTransform = svg.style.transform;
|
|
9239
|
+
const originalTransformOrigin = svg.style.transformOrigin;
|
|
9240
|
+
const originalMarginLeft = svg.style.marginLeft;
|
|
9241
|
+
const originalMarginRight = svg.style.marginRight;
|
|
9242
|
+
const originalMarginTop = svg.style.marginTop;
|
|
9243
|
+
const originalMarginBottom = svg.style.marginBottom;
|
|
9244
|
+
svg.style.transform = "none";
|
|
9245
|
+
svg.style.transformOrigin = "";
|
|
9246
|
+
svg.style.marginLeft = "0";
|
|
9247
|
+
svg.style.marginRight = "0";
|
|
9248
|
+
svg.style.marginTop = "0";
|
|
9249
|
+
svg.style.marginBottom = "0";
|
|
9250
|
+
const svgData = new XMLSerializer().serializeToString(svg);
|
|
9251
|
+
svg.style.marginLeft = originalMarginLeft;
|
|
9252
|
+
svg.style.marginRight = originalMarginRight;
|
|
9253
|
+
svg.style.marginTop = originalMarginTop;
|
|
9254
|
+
svg.style.marginBottom = originalMarginBottom;
|
|
9255
|
+
svg.style.transform = originalTransform;
|
|
9256
|
+
svg.style.transformOrigin = originalTransformOrigin;
|
|
9257
|
+
return new Promise((resolve, reject) => {
|
|
9258
|
+
const image = new Image;
|
|
9259
|
+
const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
|
|
9260
|
+
image.onload = function() {
|
|
9261
|
+
resolve(image);
|
|
9262
|
+
};
|
|
9263
|
+
image.onerror = () => {
|
|
9264
|
+
reject(new Error("Failed to convert SVG to image"));
|
|
9265
|
+
};
|
|
9266
|
+
image.src = url2;
|
|
9267
|
+
});
|
|
9268
|
+
};
|
|
9269
|
+
|
|
9086
9270
|
// src/compose.ts
|
|
9087
9271
|
var compose = async (element, context) => {
|
|
9088
9272
|
const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
|
|
9089
9273
|
if (node instanceof Element) {
|
|
9274
|
+
if (node.parentElement instanceof SVGSVGElement) {
|
|
9275
|
+
return NodeFilter.FILTER_REJECT;
|
|
9276
|
+
}
|
|
9090
9277
|
const computedStyle = getComputedStyle(node);
|
|
9091
9278
|
return computedStyle.display === "none" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
|
9092
9279
|
}
|
|
@@ -9095,7 +9282,18 @@ var compose = async (element, context) => {
|
|
|
9095
9282
|
while (treeWalker.nextNode()) {
|
|
9096
9283
|
const node = treeWalker.currentNode;
|
|
9097
9284
|
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
9098
|
-
await drawElementToCanvas({
|
|
9285
|
+
await drawElementToCanvas({
|
|
9286
|
+
element: node,
|
|
9287
|
+
context,
|
|
9288
|
+
draw: async (dimensions) => {
|
|
9289
|
+
const drawable = await (node instanceof SVGSVGElement ? turnSvgIntoDrawable(node) : node instanceof HTMLImageElement ? node : node instanceof HTMLCanvasElement ? node : null);
|
|
9290
|
+
if (drawable) {
|
|
9291
|
+
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
9292
|
+
}
|
|
9293
|
+
}
|
|
9294
|
+
});
|
|
9295
|
+
} else if (node instanceof Text) {
|
|
9296
|
+
await handleTextNode(node, context);
|
|
9099
9297
|
}
|
|
9100
9298
|
}
|
|
9101
9299
|
};
|
|
@@ -9256,7 +9454,7 @@ var internalRenderMediaOnWeb = async ({
|
|
|
9256
9454
|
if (codec && !format.getSupportedCodecs().includes(codecToMediabunnyCodec(codec))) {
|
|
9257
9455
|
return Promise.reject(new Error(`Codec ${codec} is not supported for container ${container}`));
|
|
9258
9456
|
}
|
|
9259
|
-
const resolved = await
|
|
9457
|
+
const resolved = await Internals4.resolveVideoConfig({
|
|
9260
9458
|
calculateMetadata: composition.calculateMetadata ?? null,
|
|
9261
9459
|
signal: signal ?? new AbortController().signal,
|
|
9262
9460
|
defaultProps: composition.defaultProps ?? {},
|
|
@@ -9445,7 +9643,7 @@ var renderMediaOnWeb = (options) => {
|
|
|
9445
9643
|
};
|
|
9446
9644
|
// src/render-still-on-web.tsx
|
|
9447
9645
|
import {
|
|
9448
|
-
Internals as
|
|
9646
|
+
Internals as Internals5
|
|
9449
9647
|
} from "remotion";
|
|
9450
9648
|
async function internalRenderStillOnWeb({
|
|
9451
9649
|
frame,
|
|
@@ -9459,7 +9657,7 @@ async function internalRenderStillOnWeb({
|
|
|
9459
9657
|
signal,
|
|
9460
9658
|
onArtifact
|
|
9461
9659
|
}) {
|
|
9462
|
-
const resolved = await
|
|
9660
|
+
const resolved = await Internals5.resolveVideoConfig({
|
|
9463
9661
|
calculateMetadata: composition.calculateMetadata ?? null,
|
|
9464
9662
|
signal: signal ?? new AbortController().signal,
|
|
9465
9663
|
defaultProps: composition.defaultProps ?? {},
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/web-renderer"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/web-renderer",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.391",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"scripts": {
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
"author": "Remotion <jonny@remotion.dev>",
|
|
17
17
|
"license": "UNLICENSED",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"remotion": "4.0.
|
|
19
|
+
"remotion": "4.0.391",
|
|
20
20
|
"mediabunny": "1.25.8"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
24
|
-
"@remotion/player": "4.0.
|
|
25
|
-
"@remotion/media": "4.0.
|
|
23
|
+
"@remotion/eslint-config-internal": "4.0.391",
|
|
24
|
+
"@remotion/player": "4.0.391",
|
|
25
|
+
"@remotion/media": "4.0.391",
|
|
26
26
|
"@vitejs/plugin-react": "4.1.0",
|
|
27
27
|
"@vitest/browser-playwright": "4.0.9",
|
|
28
28
|
"playwright": "1.55.1",
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { parseTransformOrigin } from './parse-transform-origin';
|
|
2
|
-
const getInternalTransformOrigin = (transform) => {
|
|
3
|
-
var _a;
|
|
4
|
-
const centerX = transform.boundingClientRect.width / 2;
|
|
5
|
-
const centerY = transform.boundingClientRect.height / 2;
|
|
6
|
-
const origin = (_a = parseTransformOrigin(transform.transformOrigin)) !== null && _a !== void 0 ? _a : {
|
|
7
|
-
x: centerX,
|
|
8
|
-
y: centerY,
|
|
9
|
-
};
|
|
10
|
-
return origin;
|
|
11
|
-
};
|
|
12
|
-
const getGlobalTransformOrigin = (transform) => {
|
|
13
|
-
const { x: originX, y: originY } = getInternalTransformOrigin(transform);
|
|
14
|
-
return {
|
|
15
|
-
x: originX + transform.boundingClientRect.left,
|
|
16
|
-
y: originY + transform.boundingClientRect.top,
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
export const calculateTransforms = (element) => {
|
|
20
|
-
// Compute the cumulative transform by traversing parent nodes
|
|
21
|
-
let parent = element;
|
|
22
|
-
const transforms = [];
|
|
23
|
-
const toReset = [];
|
|
24
|
-
while (parent) {
|
|
25
|
-
const computedStyle = getComputedStyle(parent);
|
|
26
|
-
if ((computedStyle.transform && computedStyle.transform !== 'none') ||
|
|
27
|
-
parent === element) {
|
|
28
|
-
const toParse = computedStyle.transform === 'none' || computedStyle.transform === ''
|
|
29
|
-
? undefined
|
|
30
|
-
: computedStyle.transform;
|
|
31
|
-
const matrix = new DOMMatrix(toParse);
|
|
32
|
-
const { transform } = parent.style;
|
|
33
|
-
parent.style.transform = 'none';
|
|
34
|
-
transforms.push({
|
|
35
|
-
matrix,
|
|
36
|
-
rect: parent,
|
|
37
|
-
transformOrigin: computedStyle.transformOrigin,
|
|
38
|
-
boundingClientRect: null,
|
|
39
|
-
});
|
|
40
|
-
const parentRef = parent;
|
|
41
|
-
toReset.push(() => {
|
|
42
|
-
parentRef.style.transform = transform;
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
parent = parent.parentElement;
|
|
46
|
-
}
|
|
47
|
-
for (const transform of transforms) {
|
|
48
|
-
transform.boundingClientRect = transform.rect.getBoundingClientRect();
|
|
49
|
-
}
|
|
50
|
-
const dimensions = transforms[0].boundingClientRect;
|
|
51
|
-
const nativeTransformOrigin = getInternalTransformOrigin(transforms[0]);
|
|
52
|
-
const totalMatrix = new DOMMatrix();
|
|
53
|
-
for (const transform of transforms.slice().reverse()) {
|
|
54
|
-
if (!transform.boundingClientRect) {
|
|
55
|
-
throw new Error('Bounding client rect not found');
|
|
56
|
-
}
|
|
57
|
-
const globalTransformOrigin = getGlobalTransformOrigin(transform);
|
|
58
|
-
const transformMatrix = new DOMMatrix()
|
|
59
|
-
.translate(globalTransformOrigin.x, globalTransformOrigin.y)
|
|
60
|
-
.multiply(transform.matrix)
|
|
61
|
-
.translate(-globalTransformOrigin.x, -globalTransformOrigin.y);
|
|
62
|
-
totalMatrix.multiplySelf(transformMatrix);
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
dimensions,
|
|
66
|
-
totalMatrix,
|
|
67
|
-
reset: () => {
|
|
68
|
-
for (const reset of toReset) {
|
|
69
|
-
reset();
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
nativeTransformOrigin,
|
|
73
|
-
};
|
|
74
|
-
};
|
package/dist/composable.d.ts
DELETED
package/dist/composable.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/compose-canvas.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const composeCanvas: (canvas: HTMLCanvasElement | HTMLImageElement | SVGSVGElement, context: OffscreenCanvasRenderingContext2D) => Promise<void>;
|
package/dist/compose-canvas.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { calculateTransforms } from './calculate-transforms';
|
|
2
|
-
import { turnSvgIntoDrawable } from './compose-svg';
|
|
3
|
-
export const composeCanvas = async (canvas, context) => {
|
|
4
|
-
const { totalMatrix, reset, dimensions } = calculateTransforms(canvas);
|
|
5
|
-
context.setTransform(totalMatrix);
|
|
6
|
-
const drawable = canvas instanceof SVGSVGElement
|
|
7
|
-
? await turnSvgIntoDrawable(canvas)
|
|
8
|
-
: canvas;
|
|
9
|
-
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
10
|
-
context.setTransform(new DOMMatrix());
|
|
11
|
-
reset();
|
|
12
|
-
};
|
package/dist/compose-svg.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const turnSvgIntoDrawable: (svg: SVGSVGElement) => Promise<HTMLImageElement>;
|
package/dist/compose-svg.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export const findCapturableElements = (element) => {
|
|
2
|
-
const canvasAndSvgElements = element.querySelectorAll('svg,canvas,img');
|
|
3
|
-
const composables = [];
|
|
4
|
-
Array.from(canvasAndSvgElements).forEach((capturableElement) => {
|
|
5
|
-
if (capturableElement.tagName.toLocaleLowerCase() === 'canvas') {
|
|
6
|
-
const canvas = capturableElement;
|
|
7
|
-
composables.push({
|
|
8
|
-
type: 'canvas',
|
|
9
|
-
element: canvas,
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
else if (capturableElement.tagName.toLocaleLowerCase() === 'svg') {
|
|
13
|
-
const svg = capturableElement;
|
|
14
|
-
composables.push({
|
|
15
|
-
type: 'svg',
|
|
16
|
-
element: svg,
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
else if (capturableElement.tagName.toLocaleLowerCase() === 'img') {
|
|
20
|
-
const img = capturableElement;
|
|
21
|
-
composables.push({
|
|
22
|
-
type: 'img',
|
|
23
|
-
element: img,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
return composables;
|
|
28
|
-
};
|