@bendyline/squisq-react 2.0.0 → 2.0.1
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 +3 -1
- package/dist/index.js +87 -9
- 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 +46 -43
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +28 -0
- package/package.json +2 -2
- package/src/BlockRenderer.tsx +1 -0
- package/src/LinearDocView.tsx +3 -0
- package/src/__tests__/BlockRenderer.test.tsx +59 -0
- package/src/__tests__/LinearDocView.test.tsx +13 -0
- package/src/__tests__/MarkdownRenderer.test.tsx +3 -3
- package/src/__tests__/fillStyle.test.tsx +47 -0
- package/src/layers/ImageLayer.tsx +106 -1
- package/src/layers/ShapeLayer.tsx +29 -7
- package/src/styles/doc-animations.css +36 -0
package/dist/index.d.ts
CHANGED
|
@@ -691,8 +691,10 @@ interface ImageLayerProps {
|
|
|
691
691
|
};
|
|
692
692
|
/** Current time relative to block start (for animation timing) */
|
|
693
693
|
blockTime: number;
|
|
694
|
+
/** Whether authored and responsive image motion should run. */
|
|
695
|
+
animationsEnabled?: boolean;
|
|
694
696
|
}
|
|
695
|
-
declare function ImageLayer({ layer, basePath, viewport, blockTime }: ImageLayerProps): react_jsx_runtime.JSX.Element;
|
|
697
|
+
declare function ImageLayer({ layer, basePath, viewport, blockTime, animationsEnabled, }: ImageLayerProps): react_jsx_runtime.JSX.Element;
|
|
696
698
|
|
|
697
699
|
interface TextLayerProps {
|
|
698
700
|
layer: TextLayer$1;
|
package/dist/index.js
CHANGED
|
@@ -207,7 +207,13 @@ function getAnchorOffset(anchor, width, height) {
|
|
|
207
207
|
|
|
208
208
|
// src/layers/ImageLayer.tsx
|
|
209
209
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
210
|
-
function ImageLayer({
|
|
210
|
+
function ImageLayer({
|
|
211
|
+
layer,
|
|
212
|
+
basePath,
|
|
213
|
+
viewport,
|
|
214
|
+
blockTime,
|
|
215
|
+
animationsEnabled = true
|
|
216
|
+
}) {
|
|
211
217
|
const { content, position, animation } = layer;
|
|
212
218
|
const x = resolveValue(position.x, viewport.width);
|
|
213
219
|
const y = resolveValue(position.y, viewport.height);
|
|
@@ -222,6 +228,50 @@ function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
222
228
|
const preserveAspectRatio = getPreserveAspectRatio(content.fit);
|
|
223
229
|
const isCover = content.fit === "cover";
|
|
224
230
|
const isSpatialAnim = animation && SPATIAL_ANIMATION_TYPES.has(animation.type);
|
|
231
|
+
const usesPortraitPan = isCover && shouldUsePortraitPan(viewport, width, height, animationsEnabled, animation);
|
|
232
|
+
if (usesPortraitPan) {
|
|
233
|
+
const panClass = getPortraitPanClass(animation);
|
|
234
|
+
const panStyle = getPortraitPanStyle(isSpatialAnim ? animation : void 0);
|
|
235
|
+
const containerAnim = isSpatialAnim ? { className: "", style: {} } : animStyle;
|
|
236
|
+
return /* @__PURE__ */ jsx2(
|
|
237
|
+
"g",
|
|
238
|
+
{
|
|
239
|
+
className: `block-layer block-layer--image ${containerAnim.className}`,
|
|
240
|
+
style: containerAnim.style,
|
|
241
|
+
"data-layer-id": layer.id,
|
|
242
|
+
"data-image-framing": "portrait-pan",
|
|
243
|
+
children: /* @__PURE__ */ jsx2("foreignObject", { x: finalX, y: finalY, width, height, children: /* @__PURE__ */ jsx2(
|
|
244
|
+
"div",
|
|
245
|
+
{
|
|
246
|
+
style: {
|
|
247
|
+
width: `${width}px`,
|
|
248
|
+
height: `${height}px`,
|
|
249
|
+
overflow: "hidden"
|
|
250
|
+
},
|
|
251
|
+
children: /* @__PURE__ */ jsx2(
|
|
252
|
+
"img",
|
|
253
|
+
{
|
|
254
|
+
src,
|
|
255
|
+
alt: content.alt || "",
|
|
256
|
+
className: panClass,
|
|
257
|
+
style: {
|
|
258
|
+
width: `${width}px`,
|
|
259
|
+
height: `${height}px`,
|
|
260
|
+
objectFit: "cover",
|
|
261
|
+
objectPosition: "center",
|
|
262
|
+
display: "block",
|
|
263
|
+
pointerEvents: "none",
|
|
264
|
+
...filter ? { filter } : {},
|
|
265
|
+
...content.blur && content.blur > 0 ? { transform: "scale(1.06)" } : {},
|
|
266
|
+
...panStyle
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
) })
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
}
|
|
225
275
|
if (isCover && isSpatialAnim && animation) {
|
|
226
276
|
const kbAnim = remapToKenBurns(animation);
|
|
227
277
|
const kbStyle = getAnimationStyle(kbAnim, blockTime);
|
|
@@ -317,6 +367,23 @@ function getPreserveAspectRatio(fit) {
|
|
|
317
367
|
}
|
|
318
368
|
}
|
|
319
369
|
var SPATIAL_ANIMATION_TYPES = /* @__PURE__ */ new Set(["panLeft", "panRight", "slowZoom", "zoomIn", "zoomOut"]);
|
|
370
|
+
var PORTRAIT_ASPECT_CUTOFF = 0.83;
|
|
371
|
+
function shouldUsePortraitPan(viewport, layerWidth, layerHeight, animationsEnabled, animation) {
|
|
372
|
+
if (!animationsEnabled || animation?.type === "none") return false;
|
|
373
|
+
if (viewport.width / viewport.height >= PORTRAIT_ASPECT_CUTOFF) return false;
|
|
374
|
+
return layerWidth >= viewport.width * 0.7 && layerHeight >= viewport.height * 0.7;
|
|
375
|
+
}
|
|
376
|
+
function getPortraitPanClass(animation) {
|
|
377
|
+
const pansBack = animation?.type === "panRight" || animation?.type === "slowZoom" && animation.panDirection === "right";
|
|
378
|
+
return pansBack ? "squisq-image--portrait-pan-left" : "squisq-image--portrait-pan-right";
|
|
379
|
+
}
|
|
380
|
+
function getPortraitPanStyle(animation) {
|
|
381
|
+
return {
|
|
382
|
+
"--portrait-pan-duration": `${animation?.duration ?? 12}s`,
|
|
383
|
+
"--portrait-pan-delay": `${animation?.delay ?? 0}s`,
|
|
384
|
+
"--portrait-pan-easing": animation?.easing ?? "ease-in-out"
|
|
385
|
+
};
|
|
386
|
+
}
|
|
320
387
|
function remapToKenBurns(anim) {
|
|
321
388
|
switch (anim.type) {
|
|
322
389
|
case "panLeft":
|
|
@@ -766,6 +833,7 @@ function wrapText(text, fontSize, maxWidth) {
|
|
|
766
833
|
// src/layers/ShapeLayer.tsx
|
|
767
834
|
import { useId as useId2 } from "react";
|
|
768
835
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
836
|
+
var FULL_BLEED_OVERSCAN = 1;
|
|
769
837
|
function ShapeLayer({ layer, viewport, blockTime }) {
|
|
770
838
|
const { content, position, animation } = layer;
|
|
771
839
|
const defsId = `${useId2().replace(/:/g, "")}-${layer.id}`;
|
|
@@ -776,6 +844,12 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
776
844
|
const anchorOffset = getAnchorOffset(position.anchor, width, height);
|
|
777
845
|
const x = rawX + anchorOffset.x;
|
|
778
846
|
const y = rawY + anchorOffset.y;
|
|
847
|
+
const isUnborderedFullBleedRect = content.shape === "rect" && x === 0 && y === 0 && width === viewport.width && height === viewport.height && !content.stroke && !content.borderRadius;
|
|
848
|
+
const overscan = isUnborderedFullBleedRect ? FULL_BLEED_OVERSCAN : 0;
|
|
849
|
+
const paintX = x - overscan;
|
|
850
|
+
const paintY = y - overscan;
|
|
851
|
+
const paintWidth = width + overscan * 2;
|
|
852
|
+
const paintHeight = height + overscan * 2;
|
|
779
853
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
780
854
|
const fill = content.fill || "none";
|
|
781
855
|
const isCSSGradient = typeof fill === "string" && fill.includes("gradient(");
|
|
@@ -786,12 +860,12 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
786
860
|
className: `block-layer block-layer--shape ${animStyle.className}`,
|
|
787
861
|
style: animStyle.style,
|
|
788
862
|
"data-layer-id": layer.id,
|
|
789
|
-
children: /* @__PURE__ */ jsx5("foreignObject", { x, y, width, height, children: /* @__PURE__ */ jsx5(
|
|
863
|
+
children: /* @__PURE__ */ jsx5("foreignObject", { x: paintX, y: paintY, width: paintWidth, height: paintHeight, children: /* @__PURE__ */ jsx5(
|
|
790
864
|
"div",
|
|
791
865
|
{
|
|
792
866
|
style: {
|
|
793
|
-
width: `${
|
|
794
|
-
height: `${
|
|
867
|
+
width: `${paintWidth}px`,
|
|
868
|
+
height: `${paintHeight}px`,
|
|
795
869
|
background: fill,
|
|
796
870
|
borderRadius: content.borderRadius ? `${content.borderRadius}px` : void 0,
|
|
797
871
|
pointerEvents: "none"
|
|
@@ -831,10 +905,10 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
831
905
|
content.shape === "rect" && /* @__PURE__ */ jsx5(
|
|
832
906
|
"rect",
|
|
833
907
|
{
|
|
834
|
-
x,
|
|
835
|
-
y,
|
|
836
|
-
width,
|
|
837
|
-
height,
|
|
908
|
+
x: paintX,
|
|
909
|
+
y: paintY,
|
|
910
|
+
width: paintWidth,
|
|
911
|
+
height: paintHeight,
|
|
838
912
|
rx: content.borderRadius,
|
|
839
913
|
ry: content.borderRadius,
|
|
840
914
|
...shapeProps
|
|
@@ -1661,7 +1735,8 @@ function LayerRenderer({
|
|
|
1661
1735
|
layer: renderedLayer,
|
|
1662
1736
|
basePath,
|
|
1663
1737
|
viewport,
|
|
1664
|
-
blockTime
|
|
1738
|
+
blockTime,
|
|
1739
|
+
animationsEnabled
|
|
1665
1740
|
}
|
|
1666
1741
|
);
|
|
1667
1742
|
case "text":
|
|
@@ -3946,6 +4021,9 @@ function LinearDocView({
|
|
|
3946
4021
|
.squisq-linear-content p {
|
|
3947
4022
|
margin-bottom: 0.75em;
|
|
3948
4023
|
}
|
|
4024
|
+
.squisq-linear-content p + p {
|
|
4025
|
+
margin-top: 1.25em;
|
|
4026
|
+
}
|
|
3949
4027
|
.squisq-linear-content ul,
|
|
3950
4028
|
.squisq-linear-content ol {
|
|
3951
4029
|
padding-left: 2em;
|