This commit is contained in:
@@ -263,7 +263,9 @@ export function initLanding() {
|
|||||||
const extension = timing.trace(progress(time, flow.grow, 1000));
|
const extension = timing.trace(progress(time, flow.grow, 1000));
|
||||||
const converge = timing.travel(progress(time, flow.converge, 560));
|
const converge = timing.travel(progress(time, flow.converge, 560));
|
||||||
const longLength = Math.max(0, innerHeight * .4 - root.offsetHeight * 7.2 / 2 - 8);
|
const longLength = Math.max(0, innerHeight * .4 - root.offsetHeight * 7.2 / 2 - 8);
|
||||||
const lineStartY = centreY + root.offsetHeight * scale / 2 + 8 * (1 - converge);
|
// Anchor to the root's local bottom edge at every scale. Only the tip
|
||||||
|
// extends/converges; the origin has no independent screen-space offset.
|
||||||
|
const lineStartY = centreY + root.offsetHeight * scale / 2;
|
||||||
const finalLength = lastRootBranch.offsetTop + lastRootBranch.offsetHeight / 2 - slot.offsetTop - slot.offsetHeight;
|
const finalLength = lastRootBranch.offsetTop + lastRootBranch.offsetHeight / 2 - slot.offsetTop - slot.offsetHeight;
|
||||||
const tipY = lineStartY + mix(longLength * extension, finalLength * scale, converge);
|
const tipY = lineStartY + mix(longLength * extension, finalLength * scale, converge);
|
||||||
const handoff = progress(time, flow.handoff, 280);
|
const handoff = progress(time, flow.handoff, 280);
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ test('hover streams are masked at the actual slash edges throughout the loop', a
|
|||||||
await expect(page.locator('.tap-stream-b')).toHaveCSS('opacity', '0');
|
await expect(page.locator('.tap-stream-b')).toHaveCSS('opacity', '0');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('trunk thickness keeps the same proportion to the slash throughout scaling', async ({ page }, testInfo) => {
|
test('trunk thickness and origin stay proportional to the slash throughout scaling', async ({ page }, testInfo) => {
|
||||||
await page.clock.install({ time: new Date('2026-01-01T00:00:00Z') });
|
await page.clock.install({ time: new Date('2026-01-01T00:00:00Z') });
|
||||||
await page.clock.pauseAt(new Date('2026-01-01T00:00:01Z'));
|
await page.clock.pauseAt(new Date('2026-01-01T00:00:01Z'));
|
||||||
await armed(page);
|
await armed(page);
|
||||||
@@ -102,12 +102,20 @@ test('trunk thickness keeps the same proportion to the slash throughout scaling'
|
|||||||
const path = document.querySelector<SVGPathElement>('.trunk-line')!;
|
const path = document.querySelector<SVGPathElement>('.trunk-line')!;
|
||||||
const matrix = path.getScreenCTM()!;
|
const matrix = path.getScreenCTM()!;
|
||||||
const stroke = parseFloat(getComputedStyle(path).strokeWidth) * Math.hypot(matrix.a, matrix.b);
|
const stroke = parseFloat(getComputedStyle(path).strokeWidth) * Math.hypot(matrix.a, matrix.b);
|
||||||
const slashHeight = document.querySelector('.root-shape')!.getBoundingClientRect().height;
|
const slash = document.querySelector('.root-shape')!.getBoundingClientRect();
|
||||||
return { stroke, slashHeight, opacity: Number(getComputedStyle(path).opacity) };
|
const start = path.getPointAtLength(0).matrixTransform(matrix);
|
||||||
|
return {
|
||||||
|
stroke, slashHeight: slash.height, opacity: Number(getComputedStyle(path).opacity),
|
||||||
|
anchorX: (start.x - slash.left) / slash.width,
|
||||||
|
gapRatio: (start.y - slash.bottom) / slash.height,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
expect(ink.opacity).toBeGreaterThan(0);
|
expect(ink.opacity).toBeGreaterThan(0);
|
||||||
// At rest: 1px trunk, 21px of actual slash ink (56/64 of its 24px box).
|
// At rest: 1px trunk, 21px of actual slash ink (56/64 of its 24px box).
|
||||||
expect(ink.stroke / ink.slashHeight).toBeCloseTo(1 / 21, 5);
|
expect(ink.stroke / ink.slashHeight).toBeCloseTo(1 / 21, 5);
|
||||||
|
// The same local anchor at every scale: centred, 1.5px below 21px of ink.
|
||||||
|
expect(ink.anchorX).toBeCloseTo(.5, 5);
|
||||||
|
expect(ink.gapRatio).toBeCloseTo(1 / 14, 5);
|
||||||
expect(ink.stroke).toBeLessThan(previousWidth);
|
expect(ink.stroke).toBeLessThan(previousWidth);
|
||||||
if ([1520, 2000, 2480].includes(time)) await page.screenshot({ path: testInfo.outputPath(`proportions-${time}.png`) });
|
if ([1520, 2000, 2480].includes(time)) await page.screenshot({ path: testInfo.outputPath(`proportions-${time}.png`) });
|
||||||
previousTime = time;
|
previousTime = time;
|
||||||
@@ -194,7 +202,7 @@ test('continuous motion overlaps growth, travel and reveal without intermediate
|
|||||||
expect(Math.abs(extended.dx)).toBeLessThan(.1);
|
expect(Math.abs(extended.dx)).toBeLessThan(.1);
|
||||||
expect(Math.abs(extended.axis)).toBeLessThan(.1);
|
expect(Math.abs(extended.axis)).toBeLessThan(.1);
|
||||||
expect(extended.dy).toBeGreaterThan(0);
|
expect(extended.dy).toBeGreaterThan(0);
|
||||||
expect(extended.gap).toBeCloseTo(8, 0);
|
expect(extended.gap).toBeCloseTo(0, 1);
|
||||||
expect(extended.bottom).toBeLessThan(page.viewportSize()!.height);
|
expect(extended.bottom).toBeLessThan(page.viewportSize()!.height);
|
||||||
await page.screenshot({ path: testInfo.outputPath('04-travelling-trunk.png') });
|
await page.screenshot({ path: testInfo.outputPath('04-travelling-trunk.png') });
|
||||||
await page.clock.runFor(920); // 2600ms: moving tree, SVG handoff AND panel onset.
|
await page.clock.runFor(920); // 2600ms: moving tree, SVG handoff AND panel onset.
|
||||||
|
|||||||
Reference in New Issue
Block a user