Skip to content

Commit 25379c7

Browse files
committed
fix: preview slide effects with the render's distances
1 parent 493268c commit 25379c7

2 files changed

Lines changed: 59 additions & 27 deletions

File tree

‎src/core/animations/effect-preset-builder.ts‎

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,19 @@ export class EffectPresetBuilder {
5959
case "slideUp":
6060
case "slideDown": {
6161
const isHorizontal = effectName === "slideLeft" || effectName === "slideRight";
62-
const startsPositive = effectName === "slideLeft" || effectName === "slideUp";
62+
// Left and down start on the positive side of their axis (offset y points up).
63+
const startsPositive = effectName === "slideLeft" || effectName === "slideDown";
6364

64-
const fittedSize = this.getFittedSize(editSize, clipSize);
65+
// The render checks for overflow with the media fitted to the frame's other side, whatever the clip's fit.
6566
const editDimension = isHorizontal ? editSize.width : editSize.height;
66-
const fittedDimension = isHorizontal ? fittedSize.width : fittedSize.height;
67+
const fittedDimension = isHorizontal
68+
? (clipSize.width / clipSize.height) * editSize.height
69+
: (clipSize.height / clipSize.width) * editSize.width;
6770

6871
let targetOffset = this.getSlideStart();
69-
const minScale = editDimension + editDimension * targetOffset * 2;
72+
const scaleFactor = 1 + targetOffset * 2;
7073

71-
if (fittedDimension < minScale) {
72-
const scaleFactor = minScale / fittedDimension;
74+
if (fittedDimension < editDimension * scaleFactor) {
7375
scaleKeyframes.push({ from: scaleFactor, to: scaleFactor, start, length, interpolation: "linear" });
7476
} else {
7577
targetOffset = (fittedDimension - editDimension) / 2 / editDimension;
@@ -111,36 +113,18 @@ export class EffectPresetBuilder {
111113
private getSlideStart(): number {
112114
const { name, speed } = this.effectPreset;
113115

116+
// Fractions of the frame, matching the render.
114117
if (name.startsWith("slide")) {
115118
switch (speed) {
116119
case "Slow":
117120
return 0.03;
118121
case "Fast":
119-
return 0.2;
120-
default:
121122
return 0.12;
123+
default:
124+
return 0.08;
122125
}
123126
}
124127

125128
return 0;
126129
}
127-
128-
private getFittedSize(editSize: Size, clipSize: Size): Size {
129-
const fit = this.clipConfiguration.fit ?? "crop";
130-
131-
switch (fit) {
132-
case "cover":
133-
case "crop": {
134-
const scale = Math.max(editSize.width / clipSize.width, editSize.height / clipSize.height);
135-
return { width: clipSize.width * scale, height: clipSize.height * scale };
136-
}
137-
case "contain": {
138-
const scale = Math.min(editSize.width / clipSize.width, editSize.height / clipSize.height);
139-
return { width: clipSize.width * scale, height: clipSize.height * scale };
140-
}
141-
case "none":
142-
default:
143-
return clipSize;
144-
}
145-
}
146130
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { EffectPresetBuilder } from "@animations/effect-preset-builder";
2+
import { sec } from "@core/timing/types";
3+
import type { ResolvedClip } from "@schemas";
4+
5+
const EDIT = { width: 1920, height: 1080 };
6+
7+
const build = (effect: string, clipSize = EDIT, fit: ResolvedClip["fit"] = "crop") =>
8+
new EffectPresetBuilder({
9+
id: "clip-1",
10+
asset: { type: "image", src: "https://example.com/a.png" },
11+
start: sec(0),
12+
length: sec(5),
13+
fit,
14+
effect: effect as ResolvedClip["effect"]
15+
}).buildRelative(EDIT, clipSize);
16+
17+
describe("EffectPresetBuilder", () => {
18+
it.each([
19+
["slideLeft", 0.08],
20+
["slideLeftSlow", 0.03],
21+
["slideLeftFast", 0.12]
22+
])("pans %s by the render's %s of the frame, enlarged to cover it", (effect, distance) => {
23+
const { offsetXKeyframes, scaleKeyframes } = build(effect);
24+
25+
expect(offsetXKeyframes).toEqual([{ from: distance, to: -distance, start: 0, length: 5 }]);
26+
expect(scaleKeyframes[0]).toMatchObject({ from: 1 + 2 * distance, to: 1 + 2 * distance });
27+
});
28+
29+
it.each([
30+
["slideUp", -0.08, 0.08],
31+
["slideDown", 0.08, -0.08]
32+
])("pans %s the way its name says (offset y points up)", (effect, from, to) => {
33+
expect(build(effect).offsetYKeyframes).toEqual([{ from, to, start: 0, length: 5 }]);
34+
});
35+
36+
it("enlarges narrow media by the same amount whatever its fit", () => {
37+
const { scaleKeyframes } = build("slideLeft", { width: 1080, height: 1920 }, "contain");
38+
39+
expect(scaleKeyframes[0]).toMatchObject({ from: 1.16, to: 1.16 });
40+
});
41+
42+
it.each(["crop", "contain"] as const)("pans the overflow of media fitted to the frame's height (%s)", fit => {
43+
const { offsetXKeyframes, scaleKeyframes } = build("slideRight", { width: 4000, height: 1000 }, fit);
44+
45+
expect(offsetXKeyframes).toEqual([{ from: -0.625, to: 0.625, start: 0, length: 5 }]);
46+
expect(scaleKeyframes).toEqual([]);
47+
});
48+
});

0 commit comments

Comments
 (0)