Skip to content

Commit 5b0286e

Browse files
committed
Added centerOn function
1 parent 638c207 commit 5b0286e

2 files changed

Lines changed: 71 additions & 2 deletions

File tree

animation/projects/src/dummy/scenes/dummy.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { makeScene2D, Code, LezerHighlighter } from '@motion-canvas/2d';
33
import { all, createRef, waitFor } from '@motion-canvas/core';
44
import { MyStyle } from '../../styles';
55
import { parser as parser_cpp } from '@lezer/cpp';
6+
import { DEFAULT } from '@motion-canvas/core/lib/signals';
7+
import { centerOn } from '../../utils';
8+
import { lines } from '@motion-canvas/2d';
69

710
import dummyCode from '@lectures/dummy.md?snippet=dummy_snippet/main.cpp';
811

@@ -21,7 +24,10 @@ export default makeScene2D(function* (view) {
2124
/>
2225
);
2326

24-
yield* waitFor(0.5);
25-
yield* codeRef().code(dummyCode, 1);
27+
yield* codeRef().code(dummyCode, 0);
28+
yield* centerOn(codeRef(), DEFAULT, 1, 36);
2629
yield* waitFor(1);
30+
31+
yield* centerOn(codeRef(), lines(3, 3), 1, 40);
32+
yield* waitFor(3);
2733
});

animation/projects/src/utils.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Code } from '@motion-canvas/2d';
2+
import { BBox, all, DEFAULT, ThreadGenerator } from '@motion-canvas/core';
3+
4+
export function* centerOn(
5+
codeRef: Code,
6+
selectionInput: any,
7+
duration: number,
8+
targetFontSize?: number,
9+
): ThreadGenerator {
10+
// We only care about the bounding box of the new selection
11+
let bboxes: BBox[] = [];
12+
if (selectionInput !== DEFAULT) {
13+
// getSelectionBBox computes the bounds for the given selection using the CURRENT layout state.
14+
// We do NOT need to set the selection signal to measure it!
15+
bboxes = codeRef.getSelectionBBox(selectionInput);
16+
}
17+
18+
if (bboxes.length === 0) {
19+
const fallbackAnimations: ThreadGenerator[] = [
20+
codeRef.selection(selectionInput, duration),
21+
codeRef.y(0, duration)
22+
];
23+
if (targetFontSize !== undefined) {
24+
fallbackAnimations.push(codeRef.fontSize(targetFontSize, duration));
25+
}
26+
yield* all(...fallbackAnimations);
27+
return;
28+
}
29+
30+
let minY = Infinity;
31+
let maxY = -Infinity;
32+
33+
for (const bbox of bboxes) {
34+
if (bbox.top !== undefined && !isNaN(bbox.top)) minY = Math.min(minY, bbox.top);
35+
if (bbox.bottom !== undefined && !isNaN(bbox.bottom)) maxY = Math.max(maxY, bbox.bottom);
36+
}
37+
38+
let centerY = (minY + maxY) / 2;
39+
if (!isFinite(centerY) || isNaN(centerY)) {
40+
centerY = 0;
41+
}
42+
43+
// Scale the calculated target offset based on how the font size will change.
44+
// This perfectly centers it without needing to pollute the animation timeline
45+
// with synchronous layout mutations.
46+
if (targetFontSize !== undefined) {
47+
const currentFontSize = codeRef.fontSize();
48+
if (currentFontSize > 0) {
49+
centerY = centerY * (targetFontSize / currentFontSize);
50+
}
51+
}
52+
53+
const animations: ThreadGenerator[] = [
54+
codeRef.selection(selectionInput, duration),
55+
codeRef.y(-centerY, duration)
56+
];
57+
58+
if (targetFontSize !== undefined) {
59+
animations.push(codeRef.fontSize(targetFontSize, duration));
60+
}
61+
62+
yield* all(...animations);
63+
}

0 commit comments

Comments
 (0)