@bendyline/squisq-react 1.3.2 → 1.4.0
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/index.d.ts +63 -7
- package/dist/index.js +1171 -666
- package/dist/index.js.map +1 -1
- package/dist/squisq-player.css +1 -1
- package/dist/squisq-player.css.map +1 -1
- package/dist/squisq-player.global.js +17 -13
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/package.json +3 -2
- package/src/BlockRenderer.tsx +15 -7
- package/src/DocPlayer.tsx +65 -12
- package/src/DocProgressBar.tsx +21 -3
- package/src/LinearDocView.tsx +11 -197
- package/src/MarkdownRenderer.tsx +165 -41
- package/src/MediaClipLayer.tsx +135 -0
- package/src/__tests__/DocPlayer.test.tsx +51 -0
- package/src/__tests__/DocProgressBar.test.tsx +76 -0
- package/src/__tests__/MarkdownRenderer.test.tsx +95 -1
- package/src/__tests__/PathLayer.test.tsx +73 -0
- package/src/__tests__/fillStyle.test.tsx +112 -0
- package/src/__tests__/transitionStyles.test.ts +125 -0
- package/src/__tests__/useDocPlayback.transition.test.ts +70 -0
- package/src/hooks/useAudioSync.ts +14 -1
- package/src/hooks/useDocPlayback.ts +81 -100
- package/src/hooks/useMediaSchedule.ts +39 -0
- package/src/index.ts +7 -0
- package/src/layers/ImageLayer.tsx +11 -1
- package/src/layers/PathLayer.tsx +146 -0
- package/src/layers/ShapeLayer.tsx +27 -5
- package/src/layers/TextLayer.tsx +395 -22
- package/src/layers/VideoLayer.tsx +16 -9
- package/src/layers/index.ts +1 -0
- package/src/styles/doc-animations.css +1857 -2
- package/src/utils/fillStyle.tsx +148 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fill / border helpers for the SVG layer renderers (shape, path,
|
|
3
|
+
* and the text-box background). Keeping the gradient `<defs>` markup and
|
|
4
|
+
* the dash-pattern math in one place means rect, circle, path, and text
|
|
5
|
+
* all paint fills and borders identically.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
LinearGradient,
|
|
10
|
+
BorderStyle,
|
|
11
|
+
ShapePattern,
|
|
12
|
+
ShapeFilter,
|
|
13
|
+
} from '@bendyline/squisq/schemas';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SVG `stroke-dasharray` for a border style, scaled by stroke width so the
|
|
17
|
+
* pattern stays proportional. `solid`/undefined → no dashes.
|
|
18
|
+
*/
|
|
19
|
+
export function borderDashArray(
|
|
20
|
+
style: BorderStyle | undefined,
|
|
21
|
+
strokeWidth: number | undefined,
|
|
22
|
+
): string | undefined {
|
|
23
|
+
if (!style || style === 'solid') return undefined;
|
|
24
|
+
const w = Math.max(1, strokeWidth ?? 1);
|
|
25
|
+
if (style === 'dotted') return `${w} ${w * 2}`;
|
|
26
|
+
return `${w * 3} ${w * 2}`; // dashed
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Gradient line endpoints in objectBoundingBox units (0–1) for an angle in
|
|
31
|
+
* degrees: 0 = top→bottom, 90 = left→right, increasing clockwise.
|
|
32
|
+
*/
|
|
33
|
+
function gradientVector(angle = 0): { x1: number; y1: number; x2: number; y2: number } {
|
|
34
|
+
const a = (angle * Math.PI) / 180;
|
|
35
|
+
const dx = Math.sin(a);
|
|
36
|
+
const dy = Math.cos(a);
|
|
37
|
+
return { x1: 0.5 - dx / 2, y1: 0.5 - dy / 2, x2: 0.5 + dx / 2, y2: 0.5 + dy / 2 };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Stable id for a layer's gradient def. */
|
|
41
|
+
export function gradientDefId(layerId: string): string {
|
|
42
|
+
return `squisq-grad-${layerId}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a fill to an SVG `fill` value plus the gradient `<defs>` to
|
|
47
|
+
* render (or null). When a gradient is present it wins over the solid
|
|
48
|
+
* color; otherwise the color (or undefined) is returned verbatim.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveFill(
|
|
51
|
+
layerId: string,
|
|
52
|
+
color: string | undefined,
|
|
53
|
+
gradient: LinearGradient | undefined,
|
|
54
|
+
pattern?: ShapePattern,
|
|
55
|
+
): { fill: string | undefined; def: JSX.Element | null } {
|
|
56
|
+
if (pattern) {
|
|
57
|
+
const id = `squisq-pattern-${layerId}`;
|
|
58
|
+
return { fill: `url(#${id})`, def: patternDef(id, pattern) };
|
|
59
|
+
}
|
|
60
|
+
if (gradient) {
|
|
61
|
+
const id = gradientDefId(layerId);
|
|
62
|
+
const v = gradientVector(gradient.angle);
|
|
63
|
+
return {
|
|
64
|
+
fill: `url(#${id})`,
|
|
65
|
+
def: (
|
|
66
|
+
<linearGradient id={id} x1={v.x1} y1={v.y1} x2={v.x2} y2={v.y2}>
|
|
67
|
+
<stop offset="0%" stopColor={gradient.from} />
|
|
68
|
+
<stop offset="100%" stopColor={gradient.to} />
|
|
69
|
+
</linearGradient>
|
|
70
|
+
),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return { fill: color, def: null };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Native SVG `<pattern>` def for a repeating shape fill — fully vector,
|
|
78
|
+
* identical in the player and headless frame capture.
|
|
79
|
+
*/
|
|
80
|
+
function patternDef(id: string, pattern: ShapePattern): JSX.Element {
|
|
81
|
+
const size = pattern.size ?? 24;
|
|
82
|
+
const opacity = pattern.opacity ?? 1;
|
|
83
|
+
const color = pattern.color;
|
|
84
|
+
return (
|
|
85
|
+
<pattern
|
|
86
|
+
id={id}
|
|
87
|
+
width={size}
|
|
88
|
+
height={size}
|
|
89
|
+
patternUnits="userSpaceOnUse"
|
|
90
|
+
// Diagonal tiles rotate the whole pattern; dots/grid stay axis-aligned.
|
|
91
|
+
patternTransform={pattern.kind === 'diagonal' ? 'rotate(45)' : undefined}
|
|
92
|
+
>
|
|
93
|
+
{pattern.kind === 'dots' && (
|
|
94
|
+
<circle
|
|
95
|
+
cx={size / 2}
|
|
96
|
+
cy={size / 2}
|
|
97
|
+
r={Math.max(1, size / 12)}
|
|
98
|
+
fill={color}
|
|
99
|
+
opacity={opacity}
|
|
100
|
+
/>
|
|
101
|
+
)}
|
|
102
|
+
{pattern.kind === 'grid' && (
|
|
103
|
+
<path
|
|
104
|
+
d={`M ${size} 0 L 0 0 0 ${size}`}
|
|
105
|
+
fill="none"
|
|
106
|
+
stroke={color}
|
|
107
|
+
strokeWidth={1}
|
|
108
|
+
opacity={opacity}
|
|
109
|
+
/>
|
|
110
|
+
)}
|
|
111
|
+
{pattern.kind === 'diagonal' && (
|
|
112
|
+
<line x1={0} y1={0} x2={0} y2={size} stroke={color} strokeWidth={1} opacity={opacity} />
|
|
113
|
+
)}
|
|
114
|
+
</pattern>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* SVG filter def for a shape's procedural filter (static film grain via
|
|
120
|
+
* feTurbulence). Returns the def plus the `filter` attribute value.
|
|
121
|
+
*/
|
|
122
|
+
export function resolveShapeFilter(
|
|
123
|
+
layerId: string,
|
|
124
|
+
filter: ShapeFilter | undefined,
|
|
125
|
+
): { filterAttr: string | undefined; def: JSX.Element | null } {
|
|
126
|
+
if (!filter || filter.type !== 'noise') return { filterAttr: undefined, def: null };
|
|
127
|
+
const id = `squisq-noise-${layerId}`;
|
|
128
|
+
const opacity = filter.opacity ?? 0.05;
|
|
129
|
+
return {
|
|
130
|
+
filterAttr: `url(#${id})`,
|
|
131
|
+
def: (
|
|
132
|
+
<filter id={id} x="0%" y="0%" width="100%" height="100%">
|
|
133
|
+
<feTurbulence
|
|
134
|
+
type="fractalNoise"
|
|
135
|
+
baseFrequency={filter.baseFrequency ?? 0.8}
|
|
136
|
+
numOctaves={2}
|
|
137
|
+
stitchTiles="stitch"
|
|
138
|
+
result="noise"
|
|
139
|
+
/>
|
|
140
|
+
<feColorMatrix in="noise" type="saturate" values="0" result="mono" />
|
|
141
|
+
<feComponentTransfer in="mono" result="faded">
|
|
142
|
+
<feFuncA type="linear" slope={opacity} intercept={0} />
|
|
143
|
+
</feComponentTransfer>
|
|
144
|
+
<feComposite in="faded" in2="SourceGraphic" operator="in" />
|
|
145
|
+
</filter>
|
|
146
|
+
),
|
|
147
|
+
};
|
|
148
|
+
}
|