@remotion/web-renderer 4.0.384 → 4.0.386

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/audio.d.ts +5 -1
  2. package/dist/audio.js +12 -19
  3. package/dist/border-radius.d.ts +31 -0
  4. package/dist/border-radius.js +152 -0
  5. package/dist/calculate-transforms.d.ts +2 -0
  6. package/dist/calculate-transforms.js +17 -0
  7. package/dist/composable.d.ts +2 -8
  8. package/dist/compose-canvas.js +28 -4
  9. package/dist/compose.d.ts +1 -6
  10. package/dist/compose.js +16 -11
  11. package/dist/drawing/border-radius.d.ts +31 -0
  12. package/dist/drawing/border-radius.js +152 -0
  13. package/dist/drawing/calculate-transforms.d.ts +10 -0
  14. package/dist/drawing/calculate-transforms.js +81 -0
  15. package/dist/drawing/compose-canvas.d.ts +1 -0
  16. package/dist/drawing/compose-canvas.js +36 -0
  17. package/dist/drawing/compose-svg.d.ts +1 -0
  18. package/dist/drawing/compose-svg.js +34 -0
  19. package/dist/drawing/compose.d.ts +5 -0
  20. package/dist/drawing/compose.js +6 -0
  21. package/dist/drawing/draw-border.d.ts +10 -0
  22. package/dist/drawing/draw-border.js +101 -0
  23. package/dist/drawing/draw-element-to-canvas.d.ts +4 -0
  24. package/dist/drawing/draw-element-to-canvas.js +72 -0
  25. package/dist/drawing/get-computed-style-cache.d.ts +0 -0
  26. package/dist/drawing/get-computed-style-cache.js +1 -0
  27. package/dist/drawing/opacity.d.ts +4 -0
  28. package/dist/drawing/opacity.js +7 -0
  29. package/dist/drawing/parse-transform-origin.d.ts +4 -0
  30. package/dist/drawing/parse-transform-origin.js +7 -0
  31. package/dist/drawing/transform.d.ts +4 -0
  32. package/dist/drawing/transform.js +6 -0
  33. package/dist/drawing/turn-svg-into-drawable.d.ts +1 -0
  34. package/dist/drawing/turn-svg-into-drawable.js +34 -0
  35. package/dist/esm/index.mjs +363 -79
  36. package/dist/find-capturable-elements.d.ts +1 -1
  37. package/dist/find-capturable-elements.js +20 -22
  38. package/dist/opacity.d.ts +4 -0
  39. package/dist/opacity.js +7 -0
  40. package/dist/render-media-on-web.js +14 -5
  41. package/dist/take-screenshot.js +7 -8
  42. package/dist/transform.d.ts +4 -0
  43. package/dist/transform.js +6 -0
  44. package/package.json +5 -5
@@ -8350,37 +8350,36 @@ function mixAudio(waves, length) {
8350
8350
  }
8351
8351
  const mixed = new Int16Array(length);
8352
8352
  for (let i = 0;i < length; i++) {
8353
- const sum = waves.reduce((acc, wave2) => acc + wave2[i], 0);
8353
+ const sum = waves.reduce((acc, wave2) => {
8354
+ return acc + (wave2[i] ?? 0);
8355
+ }, 0);
8354
8356
  mixed[i] = Math.max(-32768, Math.min(32767, sum));
8355
8357
  }
8356
8358
  return mixed;
8357
8359
  }
8358
- var onlyInlineAudio = (assets) => {
8360
+ var onlyInlineAudio = ({
8361
+ assets,
8362
+ fps,
8363
+ frame
8364
+ }) => {
8359
8365
  const inlineAudio = assets.filter((asset) => asset.type === "inline-audio");
8360
- let length = null;
8366
+ if (inlineAudio.length === 0) {
8367
+ return null;
8368
+ }
8369
+ const expectedLength = Math.round(TARGET_NUMBER_OF_CHANNELS * TARGET_SAMPLE_RATE / fps);
8361
8370
  for (const asset of inlineAudio) {
8362
8371
  if (asset.toneFrequency !== 1) {
8363
8372
  throw new Error("Setting the toneFrequency is not supported yet in web rendering.");
8364
8373
  }
8365
- if (length === null) {
8366
- length = asset.audio.length;
8367
- } else if (Math.abs(length - asset.audio.length) > TARGET_NUMBER_OF_CHANNELS) {
8368
- throw new Error("All inline audio must have the same length");
8369
- } else {
8370
- length = Math.min(length, asset.audio.length);
8371
- }
8372
8374
  }
8373
- if (length === null) {
8374
- return null;
8375
- }
8376
- const mixedAudio = mixAudio(inlineAudio.map((asset) => asset.audio), length);
8375
+ const mixedAudio = mixAudio(inlineAudio.map((asset) => asset.audio), expectedLength);
8377
8376
  return new AudioData({
8378
8377
  data: mixedAudio,
8379
8378
  format: "s16",
8380
8379
  numberOfChannels: TARGET_NUMBER_OF_CHANNELS,
8381
- numberOfFrames: length / TARGET_NUMBER_OF_CHANNELS,
8380
+ numberOfFrames: expectedLength / TARGET_NUMBER_OF_CHANNELS,
8382
8381
  sampleRate: TARGET_SAMPLE_RATE,
8383
- timestamp: inlineAudio[0].timestamp
8382
+ timestamp: frame / fps * 1e6
8384
8383
  });
8385
8384
  };
8386
8385
 
@@ -8656,7 +8655,143 @@ var getQualityForWebRendererQuality = (quality) => {
8656
8655
  }
8657
8656
  };
8658
8657
 
8659
- // src/parse-transform-origin.ts
8658
+ // src/drawing/border-radius.ts
8659
+ function parseValue({
8660
+ value,
8661
+ reference
8662
+ }) {
8663
+ value = value.trim();
8664
+ if (value.endsWith("%")) {
8665
+ const percentage = parseFloat(value);
8666
+ return percentage / 100 * reference;
8667
+ }
8668
+ if (value.endsWith("px")) {
8669
+ return parseFloat(value);
8670
+ }
8671
+ return parseFloat(value);
8672
+ }
8673
+ function expandShorthand(values) {
8674
+ if (values.length === 1) {
8675
+ return [values[0], values[0], values[0], values[0]];
8676
+ }
8677
+ if (values.length === 2) {
8678
+ return [values[0], values[1], values[0], values[1]];
8679
+ }
8680
+ if (values.length === 3) {
8681
+ return [values[0], values[1], values[2], values[1]];
8682
+ }
8683
+ return [values[0], values[1], values[2], values[3]];
8684
+ }
8685
+ function clampBorderRadius({
8686
+ borderRadius,
8687
+ width,
8688
+ height
8689
+ }) {
8690
+ const clamped = {
8691
+ topLeft: { ...borderRadius.topLeft },
8692
+ topRight: { ...borderRadius.topRight },
8693
+ bottomRight: { ...borderRadius.bottomRight },
8694
+ bottomLeft: { ...borderRadius.bottomLeft }
8695
+ };
8696
+ const topSum = clamped.topLeft.horizontal + clamped.topRight.horizontal;
8697
+ if (topSum > width) {
8698
+ const factor = width / topSum;
8699
+ clamped.topLeft.horizontal *= factor;
8700
+ clamped.topRight.horizontal *= factor;
8701
+ }
8702
+ const rightSum = clamped.topRight.vertical + clamped.bottomRight.vertical;
8703
+ if (rightSum > height) {
8704
+ const factor = height / rightSum;
8705
+ clamped.topRight.vertical *= factor;
8706
+ clamped.bottomRight.vertical *= factor;
8707
+ }
8708
+ const bottomSum = clamped.bottomRight.horizontal + clamped.bottomLeft.horizontal;
8709
+ if (bottomSum > width) {
8710
+ const factor = width / bottomSum;
8711
+ clamped.bottomRight.horizontal *= factor;
8712
+ clamped.bottomLeft.horizontal *= factor;
8713
+ }
8714
+ const leftSum = clamped.bottomLeft.vertical + clamped.topLeft.vertical;
8715
+ if (leftSum > height) {
8716
+ const factor = height / leftSum;
8717
+ clamped.bottomLeft.vertical *= factor;
8718
+ clamped.topLeft.vertical *= factor;
8719
+ }
8720
+ return clamped;
8721
+ }
8722
+ function parseBorderRadius({
8723
+ borderRadius,
8724
+ width,
8725
+ height
8726
+ }) {
8727
+ const parts = borderRadius.split("/").map((part) => part.trim());
8728
+ const horizontalPart = parts[0];
8729
+ const verticalPart = parts[1];
8730
+ const horizontalValues = horizontalPart.split(/\s+/).filter((v) => v);
8731
+ const verticalValues = verticalPart ? verticalPart.split(/\s+/).filter((v) => v) : horizontalValues;
8732
+ const [hTopLeft, hTopRight, hBottomRight, hBottomLeft] = expandShorthand(horizontalValues);
8733
+ const [vTopLeft, vTopRight, vBottomRight, vBottomLeft] = expandShorthand(verticalValues);
8734
+ return clampBorderRadius({
8735
+ borderRadius: {
8736
+ topLeft: {
8737
+ horizontal: parseValue({ value: hTopLeft, reference: width }),
8738
+ vertical: parseValue({ value: vTopLeft, reference: height })
8739
+ },
8740
+ topRight: {
8741
+ horizontal: parseValue({ value: hTopRight, reference: width }),
8742
+ vertical: parseValue({ value: vTopRight, reference: height })
8743
+ },
8744
+ bottomRight: {
8745
+ horizontal: parseValue({ value: hBottomRight, reference: width }),
8746
+ vertical: parseValue({ value: vBottomRight, reference: height })
8747
+ },
8748
+ bottomLeft: {
8749
+ horizontal: parseValue({ value: hBottomLeft, reference: width }),
8750
+ vertical: parseValue({ value: vBottomLeft, reference: height })
8751
+ }
8752
+ },
8753
+ width,
8754
+ height
8755
+ });
8756
+ }
8757
+ function setBorderRadius({
8758
+ ctx,
8759
+ x,
8760
+ y,
8761
+ width,
8762
+ height,
8763
+ borderRadius
8764
+ }) {
8765
+ if (borderRadius.topLeft.horizontal === 0 && borderRadius.topLeft.vertical === 0 && borderRadius.topRight.horizontal === 0 && borderRadius.topRight.vertical === 0 && borderRadius.bottomRight.horizontal === 0 && borderRadius.bottomRight.vertical === 0 && borderRadius.bottomLeft.horizontal === 0 && borderRadius.bottomLeft.vertical === 0) {
8766
+ return () => {};
8767
+ }
8768
+ ctx.save();
8769
+ ctx.beginPath();
8770
+ ctx.moveTo(x + borderRadius.topLeft.horizontal, y);
8771
+ ctx.lineTo(x + width - borderRadius.topRight.horizontal, y);
8772
+ if (borderRadius.topRight.horizontal > 0 || borderRadius.topRight.vertical > 0) {
8773
+ ctx.ellipse(x + width - borderRadius.topRight.horizontal, y + borderRadius.topRight.vertical, borderRadius.topRight.horizontal, borderRadius.topRight.vertical, 0, -Math.PI / 2, 0);
8774
+ }
8775
+ ctx.lineTo(x + width, y + height - borderRadius.bottomRight.vertical);
8776
+ if (borderRadius.bottomRight.horizontal > 0 || borderRadius.bottomRight.vertical > 0) {
8777
+ ctx.ellipse(x + width - borderRadius.bottomRight.horizontal, y + height - borderRadius.bottomRight.vertical, borderRadius.bottomRight.horizontal, borderRadius.bottomRight.vertical, 0, 0, Math.PI / 2);
8778
+ }
8779
+ ctx.lineTo(x + borderRadius.bottomLeft.horizontal, y + height);
8780
+ if (borderRadius.bottomLeft.horizontal > 0 || borderRadius.bottomLeft.vertical > 0) {
8781
+ ctx.ellipse(x + borderRadius.bottomLeft.horizontal, y + height - borderRadius.bottomLeft.vertical, borderRadius.bottomLeft.horizontal, borderRadius.bottomLeft.vertical, 0, Math.PI / 2, Math.PI);
8782
+ }
8783
+ ctx.lineTo(x, y + borderRadius.topLeft.vertical);
8784
+ if (borderRadius.topLeft.horizontal > 0 || borderRadius.topLeft.vertical > 0) {
8785
+ ctx.ellipse(x + borderRadius.topLeft.horizontal, y + borderRadius.topLeft.vertical, borderRadius.topLeft.horizontal, borderRadius.topLeft.vertical, 0, Math.PI, Math.PI * 3 / 2);
8786
+ }
8787
+ ctx.closePath();
8788
+ ctx.clip();
8789
+ return () => {
8790
+ ctx.restore();
8791
+ };
8792
+ }
8793
+
8794
+ // src/drawing/parse-transform-origin.ts
8660
8795
  var parseTransformOrigin = (transformOrigin) => {
8661
8796
  if (transformOrigin.trim() === "") {
8662
8797
  return null;
@@ -8665,7 +8800,7 @@ var parseTransformOrigin = (transformOrigin) => {
8665
8800
  return { x: parseFloat(x), y: parseFloat(y) };
8666
8801
  };
8667
8802
 
8668
- // src/calculate-transforms.ts
8803
+ // src/drawing/calculate-transforms.ts
8669
8804
  var getInternalTransformOrigin = (transform) => {
8670
8805
  const centerX = transform.boundingClientRect.width / 2;
8671
8806
  const centerY = transform.boundingClientRect.height / 2;
@@ -8686,8 +8821,13 @@ var calculateTransforms = (element) => {
8686
8821
  let parent = element;
8687
8822
  const transforms = [];
8688
8823
  const toReset = [];
8824
+ let opacity = 1;
8689
8825
  while (parent) {
8690
8826
  const computedStyle = getComputedStyle(parent);
8827
+ const parentOpacity = computedStyle.opacity;
8828
+ if (parentOpacity && parentOpacity !== "") {
8829
+ opacity *= parseFloat(parentOpacity);
8830
+ }
8691
8831
  if (computedStyle.transform && computedStyle.transform !== "none" || parent === element) {
8692
8832
  const toParse = computedStyle.transform === "none" || computedStyle.transform === "" ? undefined : computedStyle.transform;
8693
8833
  const matrix = new DOMMatrix(toParse);
@@ -8728,11 +8868,122 @@ var calculateTransforms = (element) => {
8728
8868
  reset();
8729
8869
  }
8730
8870
  },
8731
- nativeTransformOrigin
8871
+ nativeTransformOrigin,
8872
+ opacity
8873
+ };
8874
+ };
8875
+
8876
+ // src/drawing/draw-border.ts
8877
+ var drawBorder = ({
8878
+ ctx,
8879
+ x,
8880
+ y,
8881
+ width,
8882
+ height,
8883
+ borderRadius,
8884
+ computedStyle
8885
+ }) => {
8886
+ const {
8887
+ borderStyle,
8888
+ borderColor,
8889
+ borderWidth: computedBorderWidth
8890
+ } = computedStyle;
8891
+ const borderWidths = computedBorderWidth.split(/\s+/).map((w) => parseFloat(w));
8892
+ const borderTop = borderWidths[0] || 0;
8893
+ const borderRight = borderWidths[1] || borderTop;
8894
+ const borderBottom = borderWidths[2] || borderTop;
8895
+ const borderLeft = borderWidths[3] || borderRight;
8896
+ const hasBorder = borderStyle && borderStyle !== "none" && borderStyle !== "hidden" && (borderTop > 0 || borderRight > 0 || borderBottom > 0 || borderLeft > 0);
8897
+ if (!hasBorder) {
8898
+ return;
8899
+ }
8900
+ const originalStrokeStyle = ctx.strokeStyle;
8901
+ const originalLineWidth = ctx.lineWidth;
8902
+ const originalLineDash = ctx.getLineDash();
8903
+ ctx.strokeStyle = borderColor;
8904
+ if (borderStyle === "dashed") {
8905
+ const max = Math.max(borderTop, borderRight, borderBottom, borderLeft);
8906
+ ctx.setLineDash([max * 2, max]);
8907
+ } else if (borderStyle === "dotted") {
8908
+ ctx.setLineDash([
8909
+ Math.max(borderTop, borderRight, borderBottom, borderLeft)
8910
+ ]);
8911
+ } else {
8912
+ ctx.setLineDash([]);
8913
+ }
8914
+ const maxBorderWidth = Math.max(borderTop, borderRight, borderBottom, borderLeft);
8915
+ ctx.beginPath();
8916
+ const borderX = x + maxBorderWidth / 2;
8917
+ const borderY = y + maxBorderWidth / 2;
8918
+ const borderWidth = width - maxBorderWidth;
8919
+ const borderHeight = height - maxBorderWidth;
8920
+ const adjustedBorderRadius = {
8921
+ topLeft: {
8922
+ horizontal: Math.max(0, borderRadius.topLeft.horizontal - maxBorderWidth / 2),
8923
+ vertical: Math.max(0, borderRadius.topLeft.vertical - maxBorderWidth / 2)
8924
+ },
8925
+ topRight: {
8926
+ horizontal: Math.max(0, borderRadius.topRight.horizontal - maxBorderWidth / 2),
8927
+ vertical: Math.max(0, borderRadius.topRight.vertical - maxBorderWidth / 2)
8928
+ },
8929
+ bottomRight: {
8930
+ horizontal: Math.max(0, borderRadius.bottomRight.horizontal - maxBorderWidth / 2),
8931
+ vertical: Math.max(0, borderRadius.bottomRight.vertical - maxBorderWidth / 2)
8932
+ },
8933
+ bottomLeft: {
8934
+ horizontal: Math.max(0, borderRadius.bottomLeft.horizontal - maxBorderWidth / 2),
8935
+ vertical: Math.max(0, borderRadius.bottomLeft.vertical - maxBorderWidth / 2)
8936
+ }
8937
+ };
8938
+ ctx.moveTo(borderX + adjustedBorderRadius.topLeft.horizontal, borderY);
8939
+ ctx.lineTo(borderX + borderWidth - adjustedBorderRadius.topRight.horizontal, borderY);
8940
+ if (adjustedBorderRadius.topRight.horizontal > 0 || adjustedBorderRadius.topRight.vertical > 0) {
8941
+ ctx.ellipse(borderX + borderWidth - adjustedBorderRadius.topRight.horizontal, borderY + adjustedBorderRadius.topRight.vertical, adjustedBorderRadius.topRight.horizontal, adjustedBorderRadius.topRight.vertical, 0, -Math.PI / 2, 0);
8942
+ }
8943
+ ctx.lineTo(borderX + borderWidth, borderY + borderHeight - adjustedBorderRadius.bottomRight.vertical);
8944
+ if (adjustedBorderRadius.bottomRight.horizontal > 0 || adjustedBorderRadius.bottomRight.vertical > 0) {
8945
+ ctx.ellipse(borderX + borderWidth - adjustedBorderRadius.bottomRight.horizontal, borderY + borderHeight - adjustedBorderRadius.bottomRight.vertical, adjustedBorderRadius.bottomRight.horizontal, adjustedBorderRadius.bottomRight.vertical, 0, 0, Math.PI / 2);
8946
+ }
8947
+ ctx.lineTo(borderX + adjustedBorderRadius.bottomLeft.horizontal, borderY + borderHeight);
8948
+ if (adjustedBorderRadius.bottomLeft.horizontal > 0 || adjustedBorderRadius.bottomLeft.vertical > 0) {
8949
+ ctx.ellipse(borderX + adjustedBorderRadius.bottomLeft.horizontal, borderY + borderHeight - adjustedBorderRadius.bottomLeft.vertical, adjustedBorderRadius.bottomLeft.horizontal, adjustedBorderRadius.bottomLeft.vertical, 0, Math.PI / 2, Math.PI);
8950
+ }
8951
+ ctx.lineTo(borderX, borderY + adjustedBorderRadius.topLeft.vertical);
8952
+ if (adjustedBorderRadius.topLeft.horizontal > 0 || adjustedBorderRadius.topLeft.vertical > 0) {
8953
+ ctx.ellipse(borderX + adjustedBorderRadius.topLeft.horizontal, borderY + adjustedBorderRadius.topLeft.vertical, adjustedBorderRadius.topLeft.horizontal, adjustedBorderRadius.topLeft.vertical, 0, Math.PI, Math.PI * 3 / 2);
8954
+ }
8955
+ ctx.closePath();
8956
+ ctx.lineWidth = maxBorderWidth;
8957
+ ctx.stroke();
8958
+ ctx.strokeStyle = originalStrokeStyle;
8959
+ ctx.lineWidth = originalLineWidth;
8960
+ ctx.setLineDash(originalLineDash);
8961
+ };
8962
+
8963
+ // src/drawing/opacity.ts
8964
+ var setOpacity = ({
8965
+ ctx,
8966
+ opacity
8967
+ }) => {
8968
+ const previousAlpha = ctx.globalAlpha;
8969
+ ctx.globalAlpha = previousAlpha * opacity;
8970
+ return () => {
8971
+ ctx.globalAlpha = previousAlpha;
8732
8972
  };
8733
8973
  };
8734
8974
 
8735
- // src/compose-svg.ts
8975
+ // src/drawing/transform.ts
8976
+ var setTransform = ({
8977
+ ctx,
8978
+ transform
8979
+ }) => {
8980
+ ctx.setTransform(transform);
8981
+ return () => {
8982
+ ctx.setTransform(new DOMMatrix);
8983
+ };
8984
+ };
8985
+
8986
+ // src/drawing/turn-svg-into-drawable.ts
8736
8987
  var turnSvgIntoDrawable = (svg) => {
8737
8988
  const originalTransform = svg.style.transform;
8738
8989
  const originalTransformOrigin = svg.style.transformOrigin;
@@ -8766,59 +9017,83 @@ var turnSvgIntoDrawable = (svg) => {
8766
9017
  });
8767
9018
  };
8768
9019
 
8769
- // src/compose-canvas.ts
8770
- var composeCanvas = async (canvas, context) => {
8771
- const { totalMatrix, reset, dimensions } = calculateTransforms(canvas);
8772
- context.setTransform(totalMatrix);
8773
- const drawable = canvas instanceof SVGSVGElement ? await turnSvgIntoDrawable(canvas) : canvas;
8774
- context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
8775
- context.setTransform(new DOMMatrix);
9020
+ // src/drawing/draw-element-to-canvas.ts
9021
+ var drawElementToCanvas = async ({
9022
+ element,
9023
+ context
9024
+ }) => {
9025
+ const { totalMatrix, reset, dimensions, opacity } = calculateTransforms(element);
9026
+ if (opacity === 0) {
9027
+ reset();
9028
+ return;
9029
+ }
9030
+ if (dimensions.width <= 0 || dimensions.height <= 0) {
9031
+ reset();
9032
+ return;
9033
+ }
9034
+ const computedStyle = getComputedStyle(element);
9035
+ const background = computedStyle.backgroundColor;
9036
+ const borderRadius = parseBorderRadius({
9037
+ borderRadius: computedStyle.borderRadius,
9038
+ width: dimensions.width,
9039
+ height: dimensions.height
9040
+ });
9041
+ const finishTransform = setTransform({
9042
+ ctx: context,
9043
+ transform: totalMatrix
9044
+ });
9045
+ const finishBorderRadius = setBorderRadius({
9046
+ ctx: context,
9047
+ x: dimensions.left,
9048
+ y: dimensions.top,
9049
+ width: dimensions.width,
9050
+ height: dimensions.height,
9051
+ borderRadius
9052
+ });
9053
+ const finishOpacity = setOpacity({
9054
+ ctx: context,
9055
+ opacity
9056
+ });
9057
+ const drawable = element instanceof SVGSVGElement ? await turnSvgIntoDrawable(element) : element instanceof HTMLImageElement ? element : element instanceof HTMLCanvasElement ? element : null;
9058
+ if (background && background !== "transparent" && !(background.startsWith("rgba") && (background.endsWith(", 0)") || background.endsWith(",0")))) {
9059
+ const originalFillStyle = context.fillStyle;
9060
+ context.fillStyle = background;
9061
+ context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
9062
+ context.fillStyle = originalFillStyle;
9063
+ }
9064
+ if (drawable) {
9065
+ context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
9066
+ }
9067
+ drawBorder({
9068
+ ctx: context,
9069
+ x: dimensions.left,
9070
+ y: dimensions.top,
9071
+ width: dimensions.width,
9072
+ height: dimensions.height,
9073
+ borderRadius,
9074
+ computedStyle
9075
+ });
9076
+ finishOpacity();
9077
+ finishBorderRadius();
9078
+ finishTransform();
8776
9079
  reset();
8777
9080
  };
8778
9081
 
8779
9082
  // src/compose.ts
8780
- var compose = async ({
8781
- composables,
8782
- width,
8783
- height
8784
- }) => {
8785
- const canvas = new OffscreenCanvas(width, height);
8786
- const context = canvas.getContext("2d");
8787
- if (!context) {
8788
- throw new Error("Could not get context");
8789
- }
8790
- for (const composable of composables) {
8791
- await composeCanvas(composable.element, context);
8792
- }
8793
- return canvas;
8794
- };
8795
-
8796
- // src/find-capturable-elements.ts
8797
- var findCapturableElements = (element) => {
8798
- const canvasAndSvgElements = element.querySelectorAll("svg,canvas,img");
8799
- const composables = [];
8800
- Array.from(canvasAndSvgElements).forEach((capturableElement) => {
8801
- if (capturableElement.tagName.toLocaleLowerCase() === "canvas") {
8802
- const canvas = capturableElement;
8803
- composables.push({
8804
- type: "canvas",
8805
- element: canvas
8806
- });
8807
- } else if (capturableElement.tagName.toLocaleLowerCase() === "svg") {
8808
- const svg = capturableElement;
8809
- composables.push({
8810
- type: "svg",
8811
- element: svg
8812
- });
8813
- } else if (capturableElement.tagName.toLocaleLowerCase() === "img") {
8814
- const img = capturableElement;
8815
- composables.push({
8816
- type: "img",
8817
- element: img
8818
- });
9083
+ var compose = async (element, context) => {
9084
+ const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
9085
+ if (node instanceof Element) {
9086
+ const computedStyle = getComputedStyle(node);
9087
+ return computedStyle.display === "none" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
8819
9088
  }
9089
+ return NodeFilter.FILTER_ACCEPT;
8820
9090
  });
8821
- return composables;
9091
+ while (treeWalker.nextNode()) {
9092
+ const node = treeWalker.currentNode;
9093
+ if (node instanceof HTMLElement || node instanceof SVGElement) {
9094
+ await drawElementToCanvas({ element: node, context });
9095
+ }
9096
+ }
8822
9097
  };
8823
9098
 
8824
9099
  // src/take-screenshot.ts
@@ -8827,13 +9102,13 @@ var createFrame = async ({
8827
9102
  width,
8828
9103
  height
8829
9104
  }) => {
8830
- const composables = findCapturableElements(div);
8831
- const composed = await compose({
8832
- composables,
8833
- width,
8834
- height
8835
- });
8836
- return composed;
9105
+ const canvas = new OffscreenCanvas(width, height);
9106
+ const context = canvas.getContext("2d");
9107
+ if (!context) {
9108
+ throw new Error("Could not get context");
9109
+ }
9110
+ await compose(div, context);
9111
+ return canvas;
8837
9112
  };
8838
9113
  var takeScreenshot = async ({
8839
9114
  div,
@@ -9068,8 +9343,11 @@ var internalRenderMediaOnWeb = async ({
9068
9343
  encodedFrames: 0
9069
9344
  };
9070
9345
  const throttledOnProgress = createThrottledProgressCallback(onProgress);
9071
- for (let i = realFrameRange[0];i <= realFrameRange[1]; i++) {
9072
- timeUpdater.current?.update(i);
9346
+ for (let frame = realFrameRange[0];frame <= realFrameRange[1]; frame++) {
9347
+ if (signal?.aborted) {
9348
+ throw new Error("renderMediaOnWeb() was cancelled");
9349
+ }
9350
+ timeUpdater.current?.update(frame);
9073
9351
  await waitForReady({
9074
9352
  timeoutInMilliseconds: delayRenderTimeoutInMilliseconds,
9075
9353
  scope: delayRenderScope,
@@ -9084,20 +9362,23 @@ var internalRenderMediaOnWeb = async ({
9084
9362
  width: resolved.width,
9085
9363
  height: resolved.height
9086
9364
  });
9365
+ if (signal?.aborted) {
9366
+ throw new Error("renderMediaOnWeb() was cancelled");
9367
+ }
9087
9368
  const assets = collectAssets.current.collectAssets();
9088
9369
  if (onArtifact) {
9089
9370
  await artifactsHandler.handle({
9090
9371
  imageData,
9091
- frame: i,
9372
+ frame,
9092
9373
  assets,
9093
9374
  onArtifact
9094
9375
  });
9095
9376
  }
9096
- const audio = onlyInlineAudio(assets);
9097
9377
  if (signal?.aborted) {
9098
9378
  throw new Error("renderMediaOnWeb() was cancelled");
9099
9379
  }
9100
- const timestamp = Math.round((i - realFrameRange[0]) / resolved.fps * 1e6);
9380
+ const audio = onlyInlineAudio({ assets, fps: resolved.fps, frame });
9381
+ const timestamp = Math.round((frame - realFrameRange[0]) / resolved.fps * 1e6);
9101
9382
  const videoFrame = new VideoFrame(imageData, {
9102
9383
  timestamp
9103
9384
  });
@@ -9106,6 +9387,9 @@ var internalRenderMediaOnWeb = async ({
9106
9387
  let frameToEncode = videoFrame;
9107
9388
  if (onFrame) {
9108
9389
  const returnedFrame = await onFrame(videoFrame);
9390
+ if (signal?.aborted) {
9391
+ throw new Error("renderMediaOnWeb() was cancelled");
9392
+ }
9109
9393
  frameToEncode = validateVideoFrame({
9110
9394
  originalFrame: videoFrame,
9111
9395
  returnedFrame,
@@ -1,2 +1,2 @@
1
1
  import type { Composable } from './composable';
2
- export declare const findCapturableElements: (element: HTMLDivElement) => Composable[];
2
+ export declare const findCapturableElements: (element: HTMLDivElement, context: OffscreenCanvasRenderingContext2D) => Promise<Composable[]>;
@@ -1,28 +1,26 @@
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
- });
1
+ import { drawElementToCanvas } from './drawing/draw-element-to-canvas';
2
+ export const findCapturableElements = async (element, context) => {
3
+ const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
4
+ if (node instanceof Element) {
5
+ const computedStyle = getComputedStyle(node);
6
+ return computedStyle.display === 'none'
7
+ ? NodeFilter.FILTER_REJECT
8
+ : NodeFilter.FILTER_ACCEPT;
11
9
  }
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;
10
+ return NodeFilter.FILTER_ACCEPT;
11
+ });
12
+ const composables = [];
13
+ while (treeWalker.nextNode()) {
14
+ const node = treeWalker.currentNode;
15
+ if (node instanceof HTMLCanvasElement ||
16
+ node instanceof SVGSVGElement ||
17
+ node instanceof HTMLImageElement) {
18
+ await drawElementToCanvas(node, context);
21
19
  composables.push({
22
- type: 'img',
23
- element: img,
20
+ type: 'element',
21
+ element: node,
24
22
  });
25
23
  }
26
- });
24
+ }
27
25
  return composables;
28
26
  };
@@ -0,0 +1,4 @@
1
+ export declare const setOpacity: ({ ctx, opacity, }: {
2
+ ctx: OffscreenCanvasRenderingContext2D;
3
+ opacity: number;
4
+ }) => () => void;
@@ -0,0 +1,7 @@
1
+ export const setOpacity = ({ ctx, opacity, }) => {
2
+ const previousAlpha = ctx.globalAlpha;
3
+ ctx.globalAlpha = opacity;
4
+ return () => {
5
+ ctx.globalAlpha = previousAlpha;
6
+ };
7
+ };
@@ -119,8 +119,11 @@ const internalRenderMediaOnWeb = async ({ composition, inputProps, delayRenderTi
119
119
  encodedFrames: 0,
120
120
  };
121
121
  const throttledOnProgress = createThrottledProgressCallback(onProgress);
122
- for (let i = realFrameRange[0]; i <= realFrameRange[1]; i++) {
123
- (_g = timeUpdater.current) === null || _g === void 0 ? void 0 : _g.update(i);
122
+ for (let frame = realFrameRange[0]; frame <= realFrameRange[1]; frame++) {
123
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
124
+ throw new Error('renderMediaOnWeb() was cancelled');
125
+ }
126
+ (_g = timeUpdater.current) === null || _g === void 0 ? void 0 : _g.update(frame);
124
127
  await waitForReady({
125
128
  timeoutInMilliseconds: delayRenderTimeoutInMilliseconds,
126
129
  scope: delayRenderScope,
@@ -135,20 +138,23 @@ const internalRenderMediaOnWeb = async ({ composition, inputProps, delayRenderTi
135
138
  width: resolved.width,
136
139
  height: resolved.height,
137
140
  });
141
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
142
+ throw new Error('renderMediaOnWeb() was cancelled');
143
+ }
138
144
  const assets = collectAssets.current.collectAssets();
139
145
  if (onArtifact) {
140
146
  await artifactsHandler.handle({
141
147
  imageData,
142
- frame: i,
148
+ frame,
143
149
  assets,
144
150
  onArtifact,
145
151
  });
146
152
  }
147
- const audio = onlyInlineAudio(assets);
148
153
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
149
154
  throw new Error('renderMediaOnWeb() was cancelled');
150
155
  }
151
- const timestamp = Math.round(((i - realFrameRange[0]) / resolved.fps) * 1000000);
156
+ const audio = onlyInlineAudio({ assets, fps: resolved.fps, frame });
157
+ const timestamp = Math.round(((frame - realFrameRange[0]) / resolved.fps) * 1000000);
152
158
  const videoFrame = new VideoFrame(imageData, {
153
159
  timestamp,
154
160
  });
@@ -158,6 +164,9 @@ const internalRenderMediaOnWeb = async ({ composition, inputProps, delayRenderTi
158
164
  let frameToEncode = videoFrame;
159
165
  if (onFrame) {
160
166
  const returnedFrame = await onFrame(videoFrame);
167
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
168
+ throw new Error('renderMediaOnWeb() was cancelled');
169
+ }
161
170
  frameToEncode = validateVideoFrame({
162
171
  originalFrame: videoFrame,
163
172
  returnedFrame,