@zync/zync-screnplay-player 0.1.206 → 0.1.208
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/bundle.js +1 -1
- package/dist/screenplay/RemotionRenderer/RemotionRenderer.js +14 -13
- package/dist/screenplay/RemotionRenderer/components/LottieAnimationGlobal.js +21 -21
- package/dist/screenplay/RemotionRenderer/components/layouts/CreatorCollabColdOpen.js +33 -9
- package/dist/screenplay/RemotionRenderer/components/layouts/Handoff.js +4 -2
- package/dist/screenplay/RemotionRenderer/components/utils/FaceCenteredVideo.js +50 -50
- package/dist/screenplay/RemotionRenderer/components/utils/PausableImg.js +4 -4
- package/dist/screenplay/RemotionRenderer/components/utils/README.md +80 -80
- package/dist/screenplay/RemotionRenderer/components/utils/SafeZones.js +40 -25
- package/dist/screenplay/RemotionRenderer/components/utils/StretchTextDemo.js +3 -3
- package/dist/screenplay/RemotionRenderer/development.js +1 -1
- package/dist/screenplay/RemotionRenderer/helpers/convertToSeconds.js +8 -8
- package/dist/screenplay/RemotionRenderer/helpers/faceBasedVideoStyles.js +4 -4
- package/dist/screenplay/RemotionRenderer/helpers/faceCenteredVideoTransforms.js +46 -46
- package/dist/screenplay/RemotionRenderer/main/screenplaySchema.js +51 -51
- package/dist/screenplay/RemotionRenderer/registeredComponents.js +2 -2
- package/dist/screenplay/RemotionRenderer/theme/themes/default/HandoffNametag.js +9 -2
- package/dist/screenplay/RemotionRenderer/tracks/DynamicVideoComposition.js +3 -1
- package/dist/screenplay/RemotionRenderer/tracks/LayoutVideoTrack.js +20 -20
- package/package.json +47 -47
|
@@ -90,7 +90,7 @@ export var RemotionRenderer = /*#__PURE__*/function () {
|
|
|
90
90
|
}, {
|
|
91
91
|
key: "toRemotionFragment",
|
|
92
92
|
value: function toRemotionFragment(screenPlaySegment, layout) {
|
|
93
|
-
var _screenPlaySegment$da, _screenPlaySegment$da2, _layout$data, _screenPlaySegment$da3, _screenPlaySegment$da4, _layout$data2, _screenPlaySegment$da5, _screenPlaySegment$da6, _layout$data3, _screenPlaySegment$da7, _screenPlaySegment$da8, _layout$data4, _screenPlaySegment$da9, _toFrames, _screenPlaySegment$
|
|
93
|
+
var _screenPlaySegment$da, _screenPlaySegment$da2, _layout$data, _screenPlaySegment$da3, _screenPlaySegment$da4, _layout$data2, _screenPlaySegment$da5, _screenPlaySegment$da6, _layout$data3, _screenPlaySegment$da7, _screenPlaySegment$da8, _layout$data4, _screenPlaySegment$da9, _screenPlaySegment$da10, _layout$data5, _screenPlaySegment$da11, _toFrames, _screenPlaySegment$da12, _screenPlaySegment$da13;
|
|
94
94
|
var trimLeft = 0;
|
|
95
95
|
if (layout) {
|
|
96
96
|
trimLeft = layout.data.trimLeft || 0;
|
|
@@ -99,21 +99,22 @@ export var RemotionRenderer = /*#__PURE__*/function () {
|
|
|
99
99
|
var inheritedProps = component === "HandoffNametag" ? {
|
|
100
100
|
useSquareInLandscape: (_screenPlaySegment$da = (_screenPlaySegment$da2 = screenPlaySegment.data) === null || _screenPlaySegment$da2 === void 0 ? void 0 : _screenPlaySegment$da2.useSquareInLandscape) !== null && _screenPlaySegment$da !== void 0 ? _screenPlaySegment$da : layout === null || layout === void 0 ? void 0 : (_layout$data = layout.data) === null || _layout$data === void 0 ? void 0 : _layout$data.useSquareInLandscape,
|
|
101
101
|
handoffNametagVariant: (_screenPlaySegment$da3 = (_screenPlaySegment$da4 = screenPlaySegment.data) === null || _screenPlaySegment$da4 === void 0 ? void 0 : _screenPlaySegment$da4.handoffNametagVariant) !== null && _screenPlaySegment$da3 !== void 0 ? _screenPlaySegment$da3 : layout === null || layout === void 0 ? void 0 : (_layout$data2 = layout.data) === null || _layout$data2 === void 0 ? void 0 : _layout$data2.handoffNametagVariant,
|
|
102
|
-
useSquareInLandscapeFirstVideo: (_screenPlaySegment$da5 = (_screenPlaySegment$da6 = screenPlaySegment.data) === null || _screenPlaySegment$da6 === void 0 ? void 0 : _screenPlaySegment$da6.useSquareInLandscapeFirstVideo) !== null && _screenPlaySegment$da5 !== void 0 ? _screenPlaySegment$da5 : layout === null || layout === void 0 ? void 0 : (_layout$data3 = layout.data) === null || _layout$data3 === void 0 ? void 0 : _layout$data3.useSquareInLandscapeFirstVideo
|
|
102
|
+
useSquareInLandscapeFirstVideo: (_screenPlaySegment$da5 = (_screenPlaySegment$da6 = screenPlaySegment.data) === null || _screenPlaySegment$da6 === void 0 ? void 0 : _screenPlaySegment$da6.useSquareInLandscapeFirstVideo) !== null && _screenPlaySegment$da5 !== void 0 ? _screenPlaySegment$da5 : layout === null || layout === void 0 ? void 0 : (_layout$data3 = layout.data) === null || _layout$data3 === void 0 ? void 0 : _layout$data3.useSquareInLandscapeFirstVideo,
|
|
103
|
+
useSquareInLandscapeSecondVideo: (_screenPlaySegment$da7 = (_screenPlaySegment$da8 = screenPlaySegment.data) === null || _screenPlaySegment$da8 === void 0 ? void 0 : _screenPlaySegment$da8.useSquareInLandscapeSecondVideo) !== null && _screenPlaySegment$da7 !== void 0 ? _screenPlaySegment$da7 : layout === null || layout === void 0 ? void 0 : (_layout$data4 = layout.data) === null || _layout$data4 === void 0 ? void 0 : _layout$data4.useSquareInLandscapeSecondVideo
|
|
103
104
|
} : {
|
|
104
|
-
useSquareInLandscape: (_screenPlaySegment$
|
|
105
|
+
useSquareInLandscape: (_screenPlaySegment$da9 = (_screenPlaySegment$da10 = screenPlaySegment.data) === null || _screenPlaySegment$da10 === void 0 ? void 0 : _screenPlaySegment$da10.useSquareInLandscape) !== null && _screenPlaySegment$da9 !== void 0 ? _screenPlaySegment$da9 : layout === null || layout === void 0 ? void 0 : (_layout$data5 = layout.data) === null || _layout$data5 === void 0 ? void 0 : _layout$data5.useSquareInLandscape
|
|
105
106
|
};
|
|
106
107
|
var from = this.fps * (screenPlaySegment.data.offset - trimLeft) || 0;
|
|
107
108
|
var durationInFrames = toFrames(this.fps * screenPlaySegment.data.duration);
|
|
108
109
|
var to = from + durationInFrames;
|
|
109
110
|
return _objectSpread(_objectSpread(_objectSpread({}, screenPlaySegment.data), inheritedProps), {}, {
|
|
110
111
|
component: component,
|
|
111
|
-
videoUrl: (_screenPlaySegment$
|
|
112
|
-
startVideoFrom: (_toFrames = toFrames(this.fps * ((_screenPlaySegment$
|
|
112
|
+
videoUrl: (_screenPlaySegment$da11 = screenPlaySegment.data.sourceVideo) === null || _screenPlaySegment$da11 === void 0 ? void 0 : _screenPlaySegment$da11.videoUrl,
|
|
113
|
+
startVideoFrom: (_toFrames = toFrames(this.fps * ((_screenPlaySegment$da12 = screenPlaySegment.data.sourceVideo) === null || _screenPlaySegment$da12 === void 0 ? void 0 : _screenPlaySegment$da12.start), false, true)) !== null && _toFrames !== void 0 ? _toFrames : 0,
|
|
113
114
|
durationInFrames: durationInFrames,
|
|
114
115
|
from: from,
|
|
115
116
|
to: to,
|
|
116
|
-
videoZoom: (_screenPlaySegment$
|
|
117
|
+
videoZoom: (_screenPlaySegment$da13 = screenPlaySegment.data.sourceVideo) === null || _screenPlaySegment$da13 === void 0 ? void 0 : _screenPlaySegment$da13.zoom,
|
|
117
118
|
offset: toFrames(this.fps * (screenPlaySegment.data.offset - trimLeft)) || 0,
|
|
118
119
|
theme: screenPlaySegment.theme,
|
|
119
120
|
themeSettings: screenPlaySegment === null || screenPlaySegment === void 0 ? void 0 : screenPlaySegment.themeSettings
|
|
@@ -122,20 +123,20 @@ export var RemotionRenderer = /*#__PURE__*/function () {
|
|
|
122
123
|
}, {
|
|
123
124
|
key: "toRemotionAudioFragment",
|
|
124
125
|
value: function toRemotionAudioFragment(screenPlaySegment) {
|
|
125
|
-
var _screenPlaySegment$
|
|
126
|
+
var _screenPlaySegment$da14, _toFrames2, _screenPlaySegment$da15, _screenPlaySegment$da16, _screenPlaySegment$da17, _screenPlaySegment$da18, _screenPlaySegment$da19, _screenPlaySegment$da20;
|
|
126
127
|
var from = this.fps * screenPlaySegment.data.offset || 0;
|
|
127
128
|
var durationInFrames = toFrames(this.fps * screenPlaySegment.data.duration) || undefined;
|
|
128
129
|
var to = from + durationInFrames;
|
|
129
130
|
return _objectSpread(_objectSpread({}, screenPlaySegment.data), {}, {
|
|
130
131
|
component: toPascalCase(screenPlaySegment.type),
|
|
131
|
-
src: (_screenPlaySegment$
|
|
132
|
-
startAudioFrom: (_toFrames2 = toFrames(this.fps * ((_screenPlaySegment$
|
|
132
|
+
src: (_screenPlaySegment$da14 = screenPlaySegment.data.sourceAudio) === null || _screenPlaySegment$da14 === void 0 ? void 0 : _screenPlaySegment$da14.url,
|
|
133
|
+
startAudioFrom: (_toFrames2 = toFrames(this.fps * ((_screenPlaySegment$da15 = screenPlaySegment.data.sourceAudio) === null || _screenPlaySegment$da15 === void 0 ? void 0 : _screenPlaySegment$da15.start), false, true)) !== null && _toFrames2 !== void 0 ? _toFrames2 : 0,
|
|
133
134
|
durationInFrames: durationInFrames,
|
|
134
135
|
from: from,
|
|
135
136
|
to: to,
|
|
136
137
|
offset: this.fps * screenPlaySegment.data.offset || 0,
|
|
137
|
-
volume: (_screenPlaySegment$
|
|
138
|
-
loop: ((_screenPlaySegment$
|
|
138
|
+
volume: (_screenPlaySegment$da16 = (_screenPlaySegment$da17 = screenPlaySegment.data) === null || _screenPlaySegment$da17 === void 0 ? void 0 : (_screenPlaySegment$da18 = _screenPlaySegment$da17.sourceAudio) === null || _screenPlaySegment$da18 === void 0 ? void 0 : _screenPlaySegment$da18.volume) !== null && _screenPlaySegment$da16 !== void 0 ? _screenPlaySegment$da16 : 1,
|
|
139
|
+
loop: ((_screenPlaySegment$da19 = screenPlaySegment.data) === null || _screenPlaySegment$da19 === void 0 ? void 0 : (_screenPlaySegment$da20 = _screenPlaySegment$da19.sourceAudio) === null || _screenPlaySegment$da20 === void 0 ? void 0 : _screenPlaySegment$da20.loop) || false
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
142
|
}, {
|
|
@@ -167,10 +168,10 @@ export var RemotionRenderer = /*#__PURE__*/function () {
|
|
|
167
168
|
key: "toRemotionCaptionSegment",
|
|
168
169
|
value: function toRemotionCaptionSegment(screenPlaySegment, screenplayLayout) {
|
|
169
170
|
var _screenplayLayout$dat,
|
|
170
|
-
_screenPlaySegment$
|
|
171
|
+
_screenPlaySegment$da21,
|
|
171
172
|
_this3 = this;
|
|
172
173
|
var trimLeft = (screenplayLayout === null || screenplayLayout === void 0 ? void 0 : (_screenplayLayout$dat = screenplayLayout.data) === null || _screenplayLayout$dat === void 0 ? void 0 : _screenplayLayout$dat.trimLeft) || 0;
|
|
173
|
-
var segmentOffset = Number(screenPlaySegment === null || screenPlaySegment === void 0 ? void 0 : (_screenPlaySegment$
|
|
174
|
+
var segmentOffset = Number(screenPlaySegment === null || screenPlaySegment === void 0 ? void 0 : (_screenPlaySegment$da21 = screenPlaySegment.data) === null || _screenPlaySegment$da21 === void 0 ? void 0 : _screenPlaySegment$da21.offset);
|
|
174
175
|
var from = Number.isFinite(segmentOffset) ? this.fps * (segmentOffset + trimLeft) : 0;
|
|
175
176
|
var transcripts = screenPlaySegment.data.transcript_text;
|
|
176
177
|
if (!transcripts) {
|
|
@@ -8,9 +8,9 @@ import React, { useEffect, useState } from 'react';
|
|
|
8
8
|
import { delayRender, continueRender, cancelRender } from 'remotion';
|
|
9
9
|
import { Lottie } from '@remotion/lottie';
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* Convert a HEX color string (e.g. "#ff8800") to the RGBA float array that
|
|
13
|
-
* Lottie expects: [r, g, b, a] where each component is 0‑1.
|
|
11
|
+
/**
|
|
12
|
+
* Convert a HEX color string (e.g. "#ff8800") to the RGBA float array that
|
|
13
|
+
* Lottie expects: [r, g, b, a] where each component is 0‑1.
|
|
14
14
|
*/
|
|
15
15
|
var hexToRGBA = function hexToRGBA(hex) {
|
|
16
16
|
var clean = hex.replace('#', '');
|
|
@@ -25,9 +25,9 @@ var hexToRGBA = function hexToRGBA(hex) {
|
|
|
25
25
|
];
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* Recursively walks through shape objects and overrides any fill (fl) or
|
|
30
|
-
* stroke (st) color with the provided RGBA array.
|
|
28
|
+
/**
|
|
29
|
+
* Recursively walks through shape objects and overrides any fill (fl) or
|
|
30
|
+
* stroke (st) color with the provided RGBA array.
|
|
31
31
|
*/
|
|
32
32
|
var _applyColor = function applyColor(shapes, rgba) {
|
|
33
33
|
shapes === null || shapes === void 0 ? void 0 : shapes.forEach(function (shape) {
|
|
@@ -40,11 +40,11 @@ var _applyColor = function applyColor(shapes, rgba) {
|
|
|
40
40
|
});
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
/**
|
|
44
|
-
* Traverses all layers (top‑level + asset layers) and applies the primary
|
|
45
|
-
* color to every fill / stroke it encounters. This is useful for Lottie files
|
|
46
|
-
* that were exported with a single static color (e.g. white) and do not use
|
|
47
|
-
* layer naming conventions like "PrimaryColor".
|
|
43
|
+
/**
|
|
44
|
+
* Traverses all layers (top‑level + asset layers) and applies the primary
|
|
45
|
+
* color to every fill / stroke it encounters. This is useful for Lottie files
|
|
46
|
+
* that were exported with a single static color (e.g. white) and do not use
|
|
47
|
+
* layer naming conventions like "PrimaryColor".
|
|
48
48
|
*/
|
|
49
49
|
var replaceGlobalColor = function replaceGlobalColor(data, hex) {
|
|
50
50
|
var _data$assets;
|
|
@@ -60,16 +60,16 @@ var replaceGlobalColor = function replaceGlobalColor(data, hex) {
|
|
|
60
60
|
});
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
/**
|
|
64
|
-
* LottieAnimationGlobal – specialised wrapper to render the provided Lottie
|
|
65
|
-
* (e.g. text‑bubble‑animation.json) while dynamically replacing **all** stroke
|
|
66
|
-
* and fill colors with the supplied `primaryColor`.
|
|
67
|
-
*
|
|
68
|
-
* Props:
|
|
69
|
-
* • animationPath – URL or local path to the json file.
|
|
70
|
-
* • primaryColor – HEX string, defaults to "#ffffff".
|
|
71
|
-
* • autoplay – boolean, whether to start playing immediately.
|
|
72
|
-
* • loop – boolean, whether to loop the animation.
|
|
63
|
+
/**
|
|
64
|
+
* LottieAnimationGlobal – specialised wrapper to render the provided Lottie
|
|
65
|
+
* (e.g. text‑bubble‑animation.json) while dynamically replacing **all** stroke
|
|
66
|
+
* and fill colors with the supplied `primaryColor`.
|
|
67
|
+
*
|
|
68
|
+
* Props:
|
|
69
|
+
* • animationPath – URL or local path to the json file.
|
|
70
|
+
* • primaryColor – HEX string, defaults to "#ffffff".
|
|
71
|
+
* • autoplay – boolean, whether to start playing immediately.
|
|
72
|
+
* • loop – boolean, whether to loop the animation.
|
|
73
73
|
*/
|
|
74
74
|
export var LottieAnimationGlobal = function LottieAnimationGlobal(_ref) {
|
|
75
75
|
var animationPath = _ref.animationPath,
|
|
@@ -10,6 +10,7 @@ import { BlurOverlay } from '../utils/BlurOverlay';
|
|
|
10
10
|
import { CHROME_PADDING } from '../../config.js';
|
|
11
11
|
import { useVirtualBackground } from '../../hooks/useVirtualBackground';
|
|
12
12
|
import { VirtualBackgroundUnderlay } from '../backgrounds/VirtualBackgroundUnderlay';
|
|
13
|
+
import { useSafeZoneInsets } from '../utils/SafeZones';
|
|
13
14
|
var _loadHeadlineFont = loadHeadlineFont(),
|
|
14
15
|
headlineFontFamily = _loadHeadlineFont.fontFamily;
|
|
15
16
|
var _loadBodyFont = loadBodyFont(),
|
|
@@ -94,6 +95,11 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
94
95
|
var _useVirtualBackground = useVirtualBackground(),
|
|
95
96
|
isVirtual = _useVirtualBackground.isVirtual,
|
|
96
97
|
virtualBgUrl = _useVirtualBackground.url;
|
|
98
|
+
var _useSafeZoneInsets = useSafeZoneInsets(),
|
|
99
|
+
insetTop = _useSafeZoneInsets.insetTop,
|
|
100
|
+
insetLeft = _useSafeZoneInsets.insetLeft,
|
|
101
|
+
insetRight = _useSafeZoneInsets.insetRight,
|
|
102
|
+
insetBottom = _useSafeZoneInsets.insetBottom;
|
|
97
103
|
var showVirtual = isVirtual && !!firstNoBackgroundVideoUrl;
|
|
98
104
|
var safeText = sanitizeText(text);
|
|
99
105
|
var safeFullName = sanitizeText(fullName);
|
|
@@ -325,6 +331,7 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
325
331
|
var previewOpacity = previewReveal * (collaboratorActive ? 1 : 0.92);
|
|
326
332
|
var previewLabel = collaboratorActive ? 'RESPONDING' : 'NEXT UP';
|
|
327
333
|
var previewLabelDisplayFontSize = Math.round(previewLabelFontSize * 2.3);
|
|
334
|
+
var previewEnterOvershoot = 28;
|
|
328
335
|
var previewFilter = collaboratorActive ? 'brightness(1.03) saturate(1.06)' : isLandscape ? 'brightness(1) saturate(1)' : 'brightness(0.84) saturate(0.88)';
|
|
329
336
|
var creatorFrameFilter = creatorActive ? 'saturate(1.03)' : 'brightness(0.92) saturate(0.92)';
|
|
330
337
|
var previewRadiusValue = isLandscape ? "".concat(previewRadius, "px 0 0 ").concat(previewRadius, "px") : "".concat(previewRadius, "px");
|
|
@@ -334,6 +341,20 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
334
341
|
var compactPlateRadius = settledTitleNeedsPlate ? isPortrait ? 8 : 6 : 0;
|
|
335
342
|
var compactPlatePadding = settledTitleNeedsPlate ? isPortrait ? '24px 24px 20px' : '22px 22px 18px' : 0;
|
|
336
343
|
var compactPlateRuleWidth = isPortrait ? 56 : 44;
|
|
344
|
+
var introTitleSafeTop = Math.max(introTitleTop, insetTop + 16);
|
|
345
|
+
var introTitleSafeLeft = introTitleCentered ? '50%' : Math.max(introTitleLeft, insetLeft + 12);
|
|
346
|
+
var compactTitleSafeTop = Math.max(compactTitleTop, insetTop + (isPortrait ? 18 : isLandscape ? 12 : 12));
|
|
347
|
+
var compactTitleSafeLeft = compactTitleCentered ? '50%' : Math.max(compactTitleLeft, insetLeft + (isPortrait ? 8 : isLandscape ? 12 : 6));
|
|
348
|
+
var lowerThirdSafeLeft = Math.max(lowerThirdLeft, insetLeft + (isPortrait ? 8 : isLandscape ? 12 : 6));
|
|
349
|
+
var lowerThirdSafeBottom = Math.max(lowerThirdBottom, insetBottom + (isPortrait ? 24 : 18));
|
|
350
|
+
var previewSafeTop = Math.max(previewTop, insetTop + previewLabelDisplayFontSize + 22);
|
|
351
|
+
var previewSafeRight = Math.max(previewRight - previewBleed, insetRight) + previewEnterOvershoot;
|
|
352
|
+
var chromeInsets = {
|
|
353
|
+
top: Math.max(padding, insetTop),
|
|
354
|
+
left: Math.max(padding, insetLeft),
|
|
355
|
+
right: Math.max(padding, insetRight),
|
|
356
|
+
bottom: Math.max(padding, insetBottom)
|
|
357
|
+
};
|
|
337
358
|
var renderCreatorVideo = function renderCreatorVideo(containerWidth, containerHeight, radius) {
|
|
338
359
|
return /*#__PURE__*/React.createElement(React.Fragment, null, showVirtual && virtualBgUrl ? /*#__PURE__*/React.createElement(VirtualBackgroundUnderlay, {
|
|
339
360
|
url: virtualBgUrl,
|
|
@@ -432,8 +453,8 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
432
453
|
}), hasTitle && /*#__PURE__*/React.createElement("div", {
|
|
433
454
|
style: {
|
|
434
455
|
position: 'absolute',
|
|
435
|
-
top:
|
|
436
|
-
left:
|
|
456
|
+
top: introTitleSafeTop,
|
|
457
|
+
left: introTitleSafeLeft,
|
|
437
458
|
width: introTitleMaxWidth,
|
|
438
459
|
opacity: introTitleOpacity,
|
|
439
460
|
transform: "".concat(introTitleCentered ? 'translateX(-50%) ' : '', "translateY(").concat(introTitleTranslateY, "px) scale(").concat(introTitleScale, ")"),
|
|
@@ -475,8 +496,8 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
475
496
|
})), !isLandscape && (safeTopicLabel || hasTitle) && /*#__PURE__*/React.createElement("div", {
|
|
476
497
|
style: {
|
|
477
498
|
position: 'absolute',
|
|
478
|
-
top:
|
|
479
|
-
left:
|
|
499
|
+
top: compactTitleSafeTop,
|
|
500
|
+
left: compactTitleSafeLeft,
|
|
480
501
|
width: compactTitleMaxWidth,
|
|
481
502
|
opacity: compactOpacity,
|
|
482
503
|
transform: "".concat(compactTitleCentered ? 'translateX(-50%) ' : '', "translateY(").concat(compactTranslateY, "px)"),
|
|
@@ -546,8 +567,8 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
546
567
|
}))), hasCreatorInfo && /*#__PURE__*/React.createElement("div", {
|
|
547
568
|
style: {
|
|
548
569
|
position: 'absolute',
|
|
549
|
-
left:
|
|
550
|
-
bottom:
|
|
570
|
+
left: lowerThirdSafeLeft,
|
|
571
|
+
bottom: lowerThirdSafeBottom,
|
|
551
572
|
width: lowerThirdMaxWidth,
|
|
552
573
|
zIndex: 7
|
|
553
574
|
}
|
|
@@ -601,8 +622,8 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
601
622
|
}))), shouldShowResponderTease && /*#__PURE__*/React.createElement("div", {
|
|
602
623
|
style: {
|
|
603
624
|
position: 'absolute',
|
|
604
|
-
top:
|
|
605
|
-
right:
|
|
625
|
+
top: previewSafeTop,
|
|
626
|
+
right: previewSafeRight,
|
|
606
627
|
width: previewWidth,
|
|
607
628
|
height: previewHeight,
|
|
608
629
|
opacity: previewOpacity,
|
|
@@ -727,7 +748,10 @@ export var CreatorCollabColdOpen = function CreatorCollabColdOpen(_ref) {
|
|
|
727
748
|
})))))), /*#__PURE__*/React.createElement("div", {
|
|
728
749
|
style: {
|
|
729
750
|
position: 'absolute',
|
|
730
|
-
|
|
751
|
+
top: chromeInsets.top,
|
|
752
|
+
left: chromeInsets.left,
|
|
753
|
+
right: chromeInsets.right,
|
|
754
|
+
bottom: chromeInsets.bottom,
|
|
731
755
|
border: "1px solid ".concat(withAlpha(collaboratorActive ? accent : textColor, collaboratorActive ? 0.22 : 0.08)),
|
|
732
756
|
pointerEvents: 'none',
|
|
733
757
|
zIndex: 6,
|
|
@@ -86,12 +86,14 @@ export var Handoff = function Handoff(_ref) {
|
|
|
86
86
|
var secondRenderVideoWidth = shouldUseSquareInLandscapeSecondVideo ? Math.min(secondVideoWidth, squareSize) : secondVideoWidth;
|
|
87
87
|
var secondRenderVideoHeight = shouldUseSquareInLandscapeSecondVideo ? Math.min(secondVideoHeight, squareSize) : secondVideoHeight;
|
|
88
88
|
var secondVideoCenteredOffsetX = shouldUseSquareInLandscapeSecondVideo ? Math.max(0, (width - TOTAL_X_PADDING - secondRenderVideoWidth) / 2) : 0;
|
|
89
|
-
var secondVideoTranslateX = useTimeInterpolate(inputRangeInSeconds, [secondVideoCenteredOffsetX, 0, 0,
|
|
89
|
+
var secondVideoTranslateX = useTimeInterpolate(inputRangeInSeconds, [secondVideoCenteredOffsetX, 0, 0, secondVideoCenteredOffsetX], DEFAULT_TIME_INTERPOLATE_OPTIONS);
|
|
90
|
+
var shouldUseBlackBackgroundInLandscape = shouldUseSquareInLandscapeFirstVideo || shouldUseSquareInLandscapeSecondVideo;
|
|
91
|
+
var handoffBackgroundColor = shouldUseBlackBackgroundInLandscape ? "#000000" : primaryColor;
|
|
90
92
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AbsoluteFill, {
|
|
91
93
|
style: {
|
|
92
94
|
width: width,
|
|
93
95
|
height: height,
|
|
94
|
-
backgroundColor:
|
|
96
|
+
backgroundColor: handoffBackgroundColor
|
|
95
97
|
}
|
|
96
98
|
}, /*#__PURE__*/React.createElement(AbsoluteFill, {
|
|
97
99
|
style: {
|
|
@@ -8,34 +8,34 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
|
|
|
8
8
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
9
9
|
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
10
10
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
11
|
-
/**
|
|
12
|
-
* Face-Centered Video Component
|
|
13
|
-
*
|
|
14
|
-
* A reusable component that wraps OffthreadVideo to ensure the video is always
|
|
15
|
-
* centered on a person's face within a flexible container.
|
|
16
|
-
*
|
|
17
|
-
* The video maintains its source dimensions and is positioned using translateX/translateY
|
|
18
|
-
* to center the face within the container, regardless of container size.
|
|
11
|
+
/**
|
|
12
|
+
* Face-Centered Video Component
|
|
13
|
+
*
|
|
14
|
+
* A reusable component that wraps OffthreadVideo to ensure the video is always
|
|
15
|
+
* centered on a person's face within a flexible container.
|
|
16
|
+
*
|
|
17
|
+
* The video maintains its source dimensions and is positioned using translateX/translateY
|
|
18
|
+
* to center the face within the container, regardless of container size.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import React from "react";
|
|
22
22
|
import { OffthreadVideo, useCurrentFrame } from "remotion";
|
|
23
23
|
import { useOrientationBased } from "../../hooks/useOrientationBased.js";
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
* Calculate face-centered translation for flexible container
|
|
27
|
-
* @param {Object} params - Configuration object
|
|
28
|
-
* @param {Object} params.faceMetadata - Face detection metadata
|
|
29
|
-
* @param {number} params.containerWidth - Container width in pixels
|
|
30
|
-
* @param {number} params.containerHeight - Container height in pixels
|
|
31
|
-
* @param {number} params.currentFrame - Current frame number (optional)
|
|
32
|
-
* @param {boolean} params.enableInterpolation - Whether to interpolate between frames
|
|
33
|
-
* @param {boolean} params.useAveragePosition - Whether to use average face position
|
|
34
|
-
* @param {boolean} params.centerHorizontally - Whether to only center horizontally
|
|
35
|
-
* @param {number} params.translateX - Additional translateX offset
|
|
36
|
-
* @param {number} params.translateY - Additional translateY offset
|
|
37
|
-
* @param {string} params.orientation - Viewport orientation (portrait, square, landscape)
|
|
38
|
-
* @returns {Object} Transform styles and debug info
|
|
25
|
+
/**
|
|
26
|
+
* Calculate face-centered translation for flexible container
|
|
27
|
+
* @param {Object} params - Configuration object
|
|
28
|
+
* @param {Object} params.faceMetadata - Face detection metadata
|
|
29
|
+
* @param {number} params.containerWidth - Container width in pixels
|
|
30
|
+
* @param {number} params.containerHeight - Container height in pixels
|
|
31
|
+
* @param {number} params.currentFrame - Current frame number (optional)
|
|
32
|
+
* @param {boolean} params.enableInterpolation - Whether to interpolate between frames
|
|
33
|
+
* @param {boolean} params.useAveragePosition - Whether to use average face position
|
|
34
|
+
* @param {boolean} params.centerHorizontally - Whether to only center horizontally
|
|
35
|
+
* @param {number} params.translateX - Additional translateX offset
|
|
36
|
+
* @param {number} params.translateY - Additional translateY offset
|
|
37
|
+
* @param {string} params.orientation - Viewport orientation (portrait, square, landscape)
|
|
38
|
+
* @returns {Object} Transform styles and debug info
|
|
39
39
|
*/
|
|
40
40
|
var calculateFaceCenteredTranslation = function calculateFaceCenteredTranslation(_ref) {
|
|
41
41
|
var _faceMetadata$metadat, _faceMetadata$metadat2;
|
|
@@ -214,8 +214,8 @@ var calculateFaceCenteredTranslation = function calculateFaceCenteredTranslation
|
|
|
214
214
|
};
|
|
215
215
|
};
|
|
216
216
|
|
|
217
|
-
/**
|
|
218
|
-
* Get face data for a specific frame with optional interpolation
|
|
217
|
+
/**
|
|
218
|
+
* Get face data for a specific frame with optional interpolation
|
|
219
219
|
*/
|
|
220
220
|
function getFaceDataForFrame(frames, frameIndex, enableInterpolation) {
|
|
221
221
|
var _prevFrame, _nextFrame;
|
|
@@ -274,8 +274,8 @@ function getFaceDataForFrame(frames, frameIndex, enableInterpolation) {
|
|
|
274
274
|
return ((_prevFrame = prevFrame) === null || _prevFrame === void 0 ? void 0 : _prevFrame.data) || ((_nextFrame = nextFrame) === null || _nextFrame === void 0 ? void 0 : _nextFrame.data) || null;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
/**
|
|
278
|
-
* Calculate average face position from all frames with face data
|
|
277
|
+
/**
|
|
278
|
+
* Calculate average face position from all frames with face data
|
|
279
279
|
*/
|
|
280
280
|
function getAverageFaceData(frames) {
|
|
281
281
|
if (!frames || frames.length === 0) return null;
|
|
@@ -310,14 +310,14 @@ function getAverageFaceData(frames) {
|
|
|
310
310
|
};
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
/**
|
|
314
|
-
* Calculate the negative space offset when face-centering a video
|
|
315
|
-
*
|
|
316
|
-
* @param {Object} faceMetadata - Face detection metadata object
|
|
317
|
-
* @param {number} containerWidth - Container width in pixels
|
|
318
|
-
* @param {number} containerHeight - Container height in pixels
|
|
319
|
-
* @param {number} scale - Scale factor applied to the video
|
|
320
|
-
* @returns {Object} Object containing horizontal and vertical offsets and which side has negative space
|
|
313
|
+
/**
|
|
314
|
+
* Calculate the negative space offset when face-centering a video
|
|
315
|
+
*
|
|
316
|
+
* @param {Object} faceMetadata - Face detection metadata object
|
|
317
|
+
* @param {number} containerWidth - Container width in pixels
|
|
318
|
+
* @param {number} containerHeight - Container height in pixels
|
|
319
|
+
* @param {number} scale - Scale factor applied to the video
|
|
320
|
+
* @returns {Object} Object containing horizontal and vertical offsets and which side has negative space
|
|
321
321
|
*/
|
|
322
322
|
export var calculateNegativeSpaceOffset = function calculateNegativeSpaceOffset(faceMetadata, containerWidth, containerHeight) {
|
|
323
323
|
var _faceMetadata$metadat4, _faceMetadata$metadat5;
|
|
@@ -415,22 +415,22 @@ export var calculateNegativeSpaceOffset = function calculateNegativeSpaceOffset(
|
|
|
415
415
|
};
|
|
416
416
|
};
|
|
417
417
|
|
|
418
|
-
/**
|
|
419
|
-
* FaceCenteredVideo Component
|
|
420
|
-
* @param {Object} props - Component props
|
|
421
|
-
* @param {string} props.src - Video source URL
|
|
422
|
-
* @param {Object} props.faceMetadata - Face detection metadata
|
|
423
|
-
* @param {number} props.containerWidth - Container width in pixels (required)
|
|
424
|
-
* @param {number} props.containerHeight - Container height in pixels (required)
|
|
425
|
-
* @param {boolean} props.enableInterpolation - Whether to interpolate between frames
|
|
426
|
-
* @param {boolean} props.useAveragePosition - Whether to use average face position for entire video duration
|
|
427
|
-
* @param {boolean} props.centerHorizontally - Whether to only center horizontally (X axis), not vertically (Y axis)
|
|
428
|
-
* @param {number} props.translateX - Additional translateX offset (optional)
|
|
429
|
-
* @param {number} props.translateY - Additional translateY offset (optional)
|
|
430
|
-
* @param {boolean} props.showDebugInfo - Whether to show debug information
|
|
431
|
-
* @param {Object} props.style - Additional styles to apply to video
|
|
432
|
-
* @param {string} props.className - CSS class name
|
|
433
|
-
* @param {...Object} props.otherProps - Other props to pass to OffthreadVideo
|
|
418
|
+
/**
|
|
419
|
+
* FaceCenteredVideo Component
|
|
420
|
+
* @param {Object} props - Component props
|
|
421
|
+
* @param {string} props.src - Video source URL
|
|
422
|
+
* @param {Object} props.faceMetadata - Face detection metadata
|
|
423
|
+
* @param {number} props.containerWidth - Container width in pixels (required)
|
|
424
|
+
* @param {number} props.containerHeight - Container height in pixels (required)
|
|
425
|
+
* @param {boolean} props.enableInterpolation - Whether to interpolate between frames
|
|
426
|
+
* @param {boolean} props.useAveragePosition - Whether to use average face position for entire video duration
|
|
427
|
+
* @param {boolean} props.centerHorizontally - Whether to only center horizontally (X axis), not vertically (Y axis)
|
|
428
|
+
* @param {number} props.translateX - Additional translateX offset (optional)
|
|
429
|
+
* @param {number} props.translateY - Additional translateY offset (optional)
|
|
430
|
+
* @param {boolean} props.showDebugInfo - Whether to show debug information
|
|
431
|
+
* @param {Object} props.style - Additional styles to apply to video
|
|
432
|
+
* @param {string} props.className - CSS class name
|
|
433
|
+
* @param {...Object} props.otherProps - Other props to pass to OffthreadVideo
|
|
434
434
|
*/
|
|
435
435
|
export var FaceCenteredVideo = function FaceCenteredVideo(_ref2) {
|
|
436
436
|
var _faceMetadata$metadat7, _faceMetadata$metadat8, _faceMetadata$metadat9, _faceMetadata$metadat10;
|
|
@@ -15,10 +15,10 @@ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t =
|
|
|
15
15
|
import React, { useEffect, useState } from "react";
|
|
16
16
|
import { Img, delayRender, continueRender } from "remotion";
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* PausableImg component that extends Remotion's Img with fetch validation.
|
|
20
|
-
* It delays rendering until the image source can be successfully fetched.
|
|
21
|
-
* If there's an error fetching the image, it renders nothing.
|
|
18
|
+
/**
|
|
19
|
+
* PausableImg component that extends Remotion's Img with fetch validation.
|
|
20
|
+
* It delays rendering until the image source can be successfully fetched.
|
|
21
|
+
* If there's an error fetching the image, it renders nothing.
|
|
22
22
|
*/
|
|
23
23
|
export var PausableImg = /*#__PURE__*/React.memo(function (_ref) {
|
|
24
24
|
var src = _ref.src,
|
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
# StretchText Component
|
|
2
|
-
|
|
3
|
-
A React component that renders text that stretches to fit the parent's width. Words with fewer characters will have a larger font size than words with more characters. The component uses SVG for rendering text, which ensures proper text-stroke rendering in headless mode.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```jsx
|
|
8
|
-
import StretchText from './StretchText';
|
|
9
|
-
|
|
10
|
-
// Basic usage
|
|
11
|
-
<StretchText text="Hello World" />
|
|
12
|
-
|
|
13
|
-
// With custom styling
|
|
14
|
-
<StretchText
|
|
15
|
-
text="Contribution"
|
|
16
|
-
color="#1a73e8"
|
|
17
|
-
fontFamily="Arial"
|
|
18
|
-
maxFontSize={200}
|
|
19
|
-
minFontSize={10}
|
|
20
|
-
style={{ fontWeight: 'bold' }}
|
|
21
|
-
/>
|
|
22
|
-
|
|
23
|
-
// With text stroke
|
|
24
|
-
<StretchText
|
|
25
|
-
text="Outlined Text"
|
|
26
|
-
color="transparent"
|
|
27
|
-
textStrokeColor="white"
|
|
28
|
-
textStrokeWidth="2px"
|
|
29
|
-
style={{ fontWeight: 'bold' }}
|
|
30
|
-
/>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Props
|
|
34
|
-
|
|
35
|
-
| Prop | Type | Default | Description |
|
|
36
|
-
|------|------|---------|-------------|
|
|
37
|
-
| `text` | string | required | The text to display |
|
|
38
|
-
| `color` | string | '#000000' | Text color |
|
|
39
|
-
| `fontFamily` | string | 'Arial' | Font family |
|
|
40
|
-
| `maxFontSize` | number | 100 | Maximum font size in pixels |
|
|
41
|
-
| `minFontSize` | number | 10 | Minimum font size in pixels |
|
|
42
|
-
| `textStrokeColor` | string | undefined | Color of the text stroke |
|
|
43
|
-
| `textStrokeWidth` | string | undefined | Width of the text stroke (e.g., '2px') |
|
|
44
|
-
| `textFillColor` | string | undefined | Color of the text fill (overrides `color` if provided) |
|
|
45
|
-
| `style` | object | {} | Additional style properties |
|
|
46
|
-
|
|
47
|
-
## Examples
|
|
48
|
-
|
|
49
|
-
The component automatically calculates the appropriate font size to make the text stretch to fit the parent width. For example:
|
|
50
|
-
|
|
51
|
-
- "Work" (4 characters) will have a larger font size than "Contribution" (12 characters) when both are rendered in containers of the same width.
|
|
52
|
-
- The text will always be contained within the parent container and will not overflow.
|
|
53
|
-
- The component recalculates the font size when the window is resized.
|
|
54
|
-
|
|
55
|
-
## Demo
|
|
56
|
-
|
|
57
|
-
You can see a demo of the StretchText component by running the development environment and looking at the first segment in the video timeline.
|
|
58
|
-
|
|
59
|
-
The demo shows several words of different lengths rendered in containers of the same width:
|
|
60
|
-
|
|
61
|
-
- "Contribution" (longer word, smaller font)
|
|
62
|
-
- "Work" (shorter word, larger font)
|
|
63
|
-
- "Hello" (medium length word, medium font)
|
|
64
|
-
- "Supercalifragilisticexpialidocious" (very long word, very small font)
|
|
65
|
-
|
|
66
|
-
Each word stretches to fill the entire width of its container, demonstrating how the component automatically adjusts the font size based on the text length.
|
|
67
|
-
|
|
68
|
-
## Implementation Details
|
|
69
|
-
|
|
70
|
-
The component uses a binary search algorithm to find the optimal font size that makes the text fit the parent width. It uses a simple approximation for text width calculation (character count * fontSize * 0.75) which works well for most fonts.
|
|
71
|
-
|
|
72
|
-
The component uses SVG for rendering text, which provides several advantages:
|
|
73
|
-
- Proper text-stroke rendering in headless mode
|
|
74
|
-
- Better control over text positioning and alignment
|
|
75
|
-
- Consistent rendering across different browsers and environments
|
|
76
|
-
- Support for advanced SVG features like filters for text shadows
|
|
77
|
-
|
|
78
|
-
For text with stroke effects, the component uses SVG's native stroke and fill attributes, which are more reliable than CSS text-stroke properties, especially in headless rendering environments.
|
|
79
|
-
|
|
80
|
-
The component also includes a resize listener to recalculate the font size when the window size changes.
|
|
1
|
+
# StretchText Component
|
|
2
|
+
|
|
3
|
+
A React component that renders text that stretches to fit the parent's width. Words with fewer characters will have a larger font size than words with more characters. The component uses SVG for rendering text, which ensures proper text-stroke rendering in headless mode.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```jsx
|
|
8
|
+
import StretchText from './StretchText';
|
|
9
|
+
|
|
10
|
+
// Basic usage
|
|
11
|
+
<StretchText text="Hello World" />
|
|
12
|
+
|
|
13
|
+
// With custom styling
|
|
14
|
+
<StretchText
|
|
15
|
+
text="Contribution"
|
|
16
|
+
color="#1a73e8"
|
|
17
|
+
fontFamily="Arial"
|
|
18
|
+
maxFontSize={200}
|
|
19
|
+
minFontSize={10}
|
|
20
|
+
style={{ fontWeight: 'bold' }}
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
// With text stroke
|
|
24
|
+
<StretchText
|
|
25
|
+
text="Outlined Text"
|
|
26
|
+
color="transparent"
|
|
27
|
+
textStrokeColor="white"
|
|
28
|
+
textStrokeWidth="2px"
|
|
29
|
+
style={{ fontWeight: 'bold' }}
|
|
30
|
+
/>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Default | Description |
|
|
36
|
+
|------|------|---------|-------------|
|
|
37
|
+
| `text` | string | required | The text to display |
|
|
38
|
+
| `color` | string | '#000000' | Text color |
|
|
39
|
+
| `fontFamily` | string | 'Arial' | Font family |
|
|
40
|
+
| `maxFontSize` | number | 100 | Maximum font size in pixels |
|
|
41
|
+
| `minFontSize` | number | 10 | Minimum font size in pixels |
|
|
42
|
+
| `textStrokeColor` | string | undefined | Color of the text stroke |
|
|
43
|
+
| `textStrokeWidth` | string | undefined | Width of the text stroke (e.g., '2px') |
|
|
44
|
+
| `textFillColor` | string | undefined | Color of the text fill (overrides `color` if provided) |
|
|
45
|
+
| `style` | object | {} | Additional style properties |
|
|
46
|
+
|
|
47
|
+
## Examples
|
|
48
|
+
|
|
49
|
+
The component automatically calculates the appropriate font size to make the text stretch to fit the parent width. For example:
|
|
50
|
+
|
|
51
|
+
- "Work" (4 characters) will have a larger font size than "Contribution" (12 characters) when both are rendered in containers of the same width.
|
|
52
|
+
- The text will always be contained within the parent container and will not overflow.
|
|
53
|
+
- The component recalculates the font size when the window is resized.
|
|
54
|
+
|
|
55
|
+
## Demo
|
|
56
|
+
|
|
57
|
+
You can see a demo of the StretchText component by running the development environment and looking at the first segment in the video timeline.
|
|
58
|
+
|
|
59
|
+
The demo shows several words of different lengths rendered in containers of the same width:
|
|
60
|
+
|
|
61
|
+
- "Contribution" (longer word, smaller font)
|
|
62
|
+
- "Work" (shorter word, larger font)
|
|
63
|
+
- "Hello" (medium length word, medium font)
|
|
64
|
+
- "Supercalifragilisticexpialidocious" (very long word, very small font)
|
|
65
|
+
|
|
66
|
+
Each word stretches to fill the entire width of its container, demonstrating how the component automatically adjusts the font size based on the text length.
|
|
67
|
+
|
|
68
|
+
## Implementation Details
|
|
69
|
+
|
|
70
|
+
The component uses a binary search algorithm to find the optimal font size that makes the text fit the parent width. It uses a simple approximation for text width calculation (character count * fontSize * 0.75) which works well for most fonts.
|
|
71
|
+
|
|
72
|
+
The component uses SVG for rendering text, which provides several advantages:
|
|
73
|
+
- Proper text-stroke rendering in headless mode
|
|
74
|
+
- Better control over text positioning and alignment
|
|
75
|
+
- Consistent rendering across different browsers and environments
|
|
76
|
+
- Support for advanced SVG features like filters for text shadows
|
|
77
|
+
|
|
78
|
+
For text with stroke effects, the component uses SVG's native stroke and fill attributes, which are more reliable than CSS text-stroke properties, especially in headless rendering environments.
|
|
79
|
+
|
|
80
|
+
The component also includes a resize listener to recalculate the font size when the window size changes.
|