fix: align the static fallback with the nightdrive scene
This commit is contained in:
@@ -54,6 +54,15 @@ if (posterFailed.value) recordTestFaultEvent('poster-failure');
|
|||||||
:data-poster-failed="posterFailed"
|
:data-poster-failed="posterFailed"
|
||||||
>
|
>
|
||||||
<img v-if="!posterFailed" class="journey-poster" alt="" width="1600" height="900" @error="posterFailed = true">
|
<img v-if="!posterFailed" class="journey-poster" alt="" width="1600" height="900" @error="posterFailed = true">
|
||||||
|
<div v-if="!posterFailed" class="journey-poster-composition">
|
||||||
|
<span class="journey-poster__stars" />
|
||||||
|
<span class="journey-poster__sun" />
|
||||||
|
<span class="journey-poster__terrain" />
|
||||||
|
<span class="journey-poster__road" />
|
||||||
|
<span class="journey-poster__rail journey-poster__rail--left" />
|
||||||
|
<span class="journey-poster__rail journey-poster__rail--right" />
|
||||||
|
<span class="journey-poster__car" />
|
||||||
|
</div>
|
||||||
<canvas v-if="showCanvas" ref="canvas" class="journey-canvas" aria-hidden="true" />
|
<canvas v-if="showCanvas" ref="canvas" class="journey-canvas" aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -28,86 +28,107 @@
|
|||||||
.journey-poster {
|
.journey-poster {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
background:
|
background:
|
||||||
radial-gradient(
|
radial-gradient(ellipse at 50% 48%, rgb(135 28 105 / 58%) 0, transparent 44%),
|
||||||
ellipse at 42% 89%,
|
linear-gradient(180deg, #02040b 0 30%, #08071a 49%, #1b0b27 67%, #02060b 100%);
|
||||||
rgb(255 45 85 / 72%) 0 0.65%,
|
|
||||||
rgb(255 45 85 / 20%) 0.9% 2.6%,
|
|
||||||
transparent 4.8%
|
|
||||||
),
|
|
||||||
radial-gradient(
|
|
||||||
ellipse at 58% 89%,
|
|
||||||
rgb(255 45 85 / 72%) 0 0.65%,
|
|
||||||
rgb(255 45 85 / 20%) 0.9% 2.6%,
|
|
||||||
transparent 4.8%
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
166deg,
|
|
||||||
transparent 0 67%,
|
|
||||||
rgb(12 21 31 / 98%) 67.2% 76%,
|
|
||||||
transparent 76.2%
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
194deg,
|
|
||||||
transparent 0 67%,
|
|
||||||
rgb(12 21 31 / 98%) 67.2% 76%,
|
|
||||||
transparent 76.2%
|
|
||||||
),
|
|
||||||
radial-gradient(
|
|
||||||
ellipse at 50% 91%,
|
|
||||||
rgb(5 9 15 / 100%) 0 13%,
|
|
||||||
transparent 13.4%
|
|
||||||
),
|
|
||||||
radial-gradient(
|
|
||||||
ellipse at 50% 55.8%,
|
|
||||||
transparent 0 4.8%,
|
|
||||||
rgb(4 8 20 / 88%) 5% 5.35%,
|
|
||||||
transparent 5.55%
|
|
||||||
),
|
|
||||||
radial-gradient(
|
|
||||||
ellipse at 50% 54.6%,
|
|
||||||
rgb(244 173 103 / 92%) 0 6.7%,
|
|
||||||
rgb(199 75 145 / 65%) 7.1% 9.8%,
|
|
||||||
transparent 14%
|
|
||||||
),
|
|
||||||
repeating-linear-gradient(
|
|
||||||
0deg,
|
|
||||||
transparent 0 5.2%,
|
|
||||||
rgb(4 8 20 / 88%) 5.3% 5.65%,
|
|
||||||
transparent 5.75% 7.2%
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
165deg,
|
|
||||||
transparent 0 55%,
|
|
||||||
rgb(120 216 229 / 54%) 55.12% 55.32%,
|
|
||||||
transparent 55.5%
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
195deg,
|
|
||||||
transparent 0 55%,
|
|
||||||
rgb(120 216 229 / 54%) 55.12% 55.32%,
|
|
||||||
transparent 55.5%
|
|
||||||
),
|
|
||||||
conic-gradient(
|
|
||||||
from 167deg at 50% 55%,
|
|
||||||
rgb(5 9 17 / 96%) 0 12deg,
|
|
||||||
transparent 12.4deg 335.6deg,
|
|
||||||
rgb(5 9 17 / 96%) 336deg 360deg
|
|
||||||
),
|
|
||||||
repeating-linear-gradient(
|
|
||||||
0deg,
|
|
||||||
transparent 0 6.5%,
|
|
||||||
rgb(73 198 215 / 14%) 6.65% 6.8%,
|
|
||||||
transparent 6.95% 9.5%
|
|
||||||
),
|
|
||||||
radial-gradient(
|
|
||||||
ellipse at 50% 62%,
|
|
||||||
rgb(131 41 109 / 42%) 0,
|
|
||||||
transparent 48%
|
|
||||||
),
|
|
||||||
linear-gradient(180deg, #030713 0 38%, #0c1025 54%, #26162e 69%, #050a14 100%);
|
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.journey-poster-composition,
|
||||||
|
.journey-poster-composition > span {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster-composition {
|
||||||
|
inset: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__stars {
|
||||||
|
inset: 0;
|
||||||
|
opacity: 0.72;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(circle, rgb(225 252 255 / 90%) 0 1px, transparent 1.3px),
|
||||||
|
radial-gradient(circle, rgb(120 216 229 / 72%) 0 0.8px, transparent 1.1px);
|
||||||
|
background-position: 17px 29px, 83px 97px;
|
||||||
|
background-size: 173px 149px, 227px 181px;
|
||||||
|
mask-image: linear-gradient(180deg, #000 0 54%, transparent 72%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__sun {
|
||||||
|
inset: 33% auto auto 50%;
|
||||||
|
inline-size: clamp(7rem, 14vw, 15rem);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
translate: -50% 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
180deg,
|
||||||
|
transparent 0 8%,
|
||||||
|
rgb(51 5 31 / 72%) 8.5% 11%,
|
||||||
|
transparent 11.5% 15%
|
||||||
|
),
|
||||||
|
linear-gradient(180deg, #ff4d9b, #d52378 58%, #8e155c);
|
||||||
|
box-shadow: 0 0 3rem rgb(255 44 151 / 38%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__terrain {
|
||||||
|
inset: 48% -8% -10%;
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(0deg, transparent 0 9%, rgb(76 201 220 / 16%) 9.2% 9.5%, transparent 9.7% 13%),
|
||||||
|
repeating-linear-gradient(78deg, transparent 0 7.5%, rgb(76 201 220 / 14%) 7.7% 7.9%, transparent 8.1% 12%),
|
||||||
|
linear-gradient(180deg, rgb(79 18 76 / 70%), rgb(2 8 15 / 92%));
|
||||||
|
clip-path: polygon(0 17%, 13% 4%, 27% 15%, 39% 2%, 50% 14%, 64% 4%, 78% 16%, 90% 5%, 100% 18%, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__road {
|
||||||
|
inset: 48% -14% -12%;
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(180deg, transparent 0 7%, rgb(229 158 65 / 78%) 7.4% 9%, transparent 9.4% 15%) 50% 0 / clamp(0.22rem, 0.55vw, 0.7rem) 100% no-repeat,
|
||||||
|
linear-gradient(180deg, #050811, #000207);
|
||||||
|
clip-path: polygon(49.3% 0, 50.7% 0, 84% 100%, 16% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__rail {
|
||||||
|
inset: 0;
|
||||||
|
background: #67d4e4;
|
||||||
|
filter: drop-shadow(0 0 0.55rem rgb(76 207 225 / 72%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__rail--left {
|
||||||
|
clip-path: polygon(49.15% 48%, 49.35% 48%, 1.2% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__rail--right {
|
||||||
|
clip-path: polygon(50.65% 48%, 50.85% 48%, 100% 100%, 98.8% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__car {
|
||||||
|
inset: auto auto 1.2% 50%;
|
||||||
|
inline-size: clamp(7.5rem, 18vw, 19rem);
|
||||||
|
block-size: clamp(3.2rem, 7vw, 7.4rem);
|
||||||
|
translate: -50% 0;
|
||||||
|
background: #051526;
|
||||||
|
clip-path: polygon(22% 0, 78% 0, 100% 70%, 94% 100%, 6% 100%, 0 70%);
|
||||||
|
filter: drop-shadow(0 0.8rem 1.2rem rgb(0 0 0 / 76%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__car::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: 68% 10% auto;
|
||||||
|
block-size: clamp(0.24rem, 0.48vw, 0.5rem);
|
||||||
|
background: #ff315f;
|
||||||
|
box-shadow: 0 0 0.8rem rgb(255 49 95 / 48%);
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.journey-poster__car::after {
|
||||||
|
position: absolute;
|
||||||
|
inset: 82% 14% auto;
|
||||||
|
block-size: 0.22rem;
|
||||||
|
border-inline: clamp(0.7rem, 1.5vw, 1.5rem) solid #67d4e4;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
.journey-canvas {
|
.journey-canvas {
|
||||||
max-inline-size: none;
|
max-inline-size: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,10 +111,12 @@ const captureWorldSequence = async (
|
|||||||
intervalMs: number,
|
intervalMs: number,
|
||||||
): Promise<SampledFrame[]> => {
|
): Promise<SampledFrame[]> => {
|
||||||
const frames: SampledFrame[] = [];
|
const frames: SampledFrame[] = [];
|
||||||
|
const clip = await world.boundingBox();
|
||||||
|
if (clip === null) throw new Error("Journey world is not visible.");
|
||||||
for (let index = 0; index < frameCount; index += 1) {
|
for (let index = 0; index < frameCount; index += 1) {
|
||||||
frames.push(
|
frames.push(
|
||||||
await sampleFrame(
|
await sampleFrame(
|
||||||
await world.screenshot({ animations: "allow", scale: "css" }),
|
await world.page().screenshot({ animations: "allow", clip, scale: "css" }),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (index + 1 < frameCount) await world.page().waitForTimeout(intervalMs);
|
if (index + 1 < frameCount) await world.page().waitForTimeout(intervalMs);
|
||||||
@@ -264,9 +266,11 @@ test.describe("production visual composition", () => {
|
|||||||
boxesFitHorizontally: boxes.every(
|
boxesFitHorizontally: boxes.every(
|
||||||
(rect) => rect.left >= -1 && rect.right <= root.clientWidth + 1,
|
(rect) => rect.left >= -1 && rect.right <= root.clientWidth + 1,
|
||||||
),
|
),
|
||||||
panelWidthRatios: panels.map(
|
panelGeometry: panels.map((panel) => ({
|
||||||
(panel) => panel.getBoundingClientRect().width / root.clientWidth,
|
layout: panel.dataset.layout ?? "",
|
||||||
),
|
width: panel.getBoundingClientRect().width,
|
||||||
|
widthRatio: panel.getBoundingClientRect().width / root.clientWidth,
|
||||||
|
})),
|
||||||
panelBackgroundImage: firstPanelStyle.backgroundImage,
|
panelBackgroundImage: firstPanelStyle.backgroundImage,
|
||||||
panelFilter: firstPanelStyle.filter,
|
panelFilter: firstPanelStyle.filter,
|
||||||
textShadows: textStyles.map((style) => style.textShadow),
|
textShadows: textStyles.map((style) => style.textShadow),
|
||||||
@@ -302,15 +306,28 @@ test.describe("production visual composition", () => {
|
|||||||
expect(palette.restrainedAccent).toBeLessThan(0.28);
|
expect(palette.restrainedAccent).toBeLessThan(0.28);
|
||||||
|
|
||||||
if (viewport.width >= 1280) {
|
if (viewport.width >= 1280) {
|
||||||
expect(
|
for (const panel of composition.panelGeometry) {
|
||||||
composition.panelWidthRatios.every(
|
if (["right-rail", "compact-rail"].includes(panel.layout)) {
|
||||||
(ratio) => ratio >= 0.4 && ratio <= 0.45,
|
expect(panel.widthRatio).toBeGreaterThanOrEqual(0.4);
|
||||||
),
|
expect(panel.widthRatio).toBeLessThanOrEqual(0.45);
|
||||||
).toBe(true);
|
} else if (panel.layout === "left-timeline") {
|
||||||
|
expect(panel.widthRatio).toBeGreaterThanOrEqual(0.44);
|
||||||
|
expect(panel.widthRatio).toBeLessThanOrEqual(0.48);
|
||||||
|
} else if (["full-diagram", "toolkit-field"].includes(panel.layout)) {
|
||||||
|
expect(panel.widthRatio).toBeGreaterThanOrEqual(0.75);
|
||||||
|
expect(panel.widthRatio).toBeLessThanOrEqual(0.8);
|
||||||
|
} else if (panel.layout === "project-stage") {
|
||||||
|
expect(panel.widthRatio).toBeGreaterThanOrEqual(0.9);
|
||||||
|
expect(panel.widthRatio).toBeLessThanOrEqual(0.94);
|
||||||
|
} else if (panel.layout === "destination") {
|
||||||
|
expect(panel.width).toBeGreaterThanOrEqual(760);
|
||||||
|
expect(panel.width).toBeLessThanOrEqual(866);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (viewport.width < 768) {
|
} else if (viewport.width < 768) {
|
||||||
expect(
|
expect(
|
||||||
composition.panelWidthRatios.every(
|
composition.panelGeometry.every(
|
||||||
(ratio) => ratio >= 0.85 && ratio <= 1,
|
(panel) => panel.widthRatio >= 0.85 && panel.widthRatio <= 1,
|
||||||
),
|
),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
}
|
}
|
||||||
@@ -326,6 +343,7 @@ test.describe("production visual composition", () => {
|
|||||||
}, testInfo) => {
|
}, testInfo) => {
|
||||||
await page.setViewportSize({ width: 1440, height: 900 });
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
await waitForRenderedPortfolio(page, distUrl);
|
await waitForRenderedPortfolio(page, distUrl);
|
||||||
|
await page.getByRole("button", { name: "Drive settings" }).click();
|
||||||
await page.getByLabel("Quality control").selectOption("static");
|
await page.getByLabel("Quality control").selectOption("static");
|
||||||
await expect(page.locator(".journey-world")).toHaveAttribute(
|
await expect(page.locator(".journey-world")).toHaveAttribute(
|
||||||
"data-scene-state",
|
"data-scene-state",
|
||||||
@@ -333,6 +351,9 @@ test.describe("production visual composition", () => {
|
|||||||
);
|
);
|
||||||
await expect(page.locator(".journey-canvas")).toHaveCount(0);
|
await expect(page.locator(".journey-canvas")).toHaveCount(0);
|
||||||
await expect(page.locator(".journey-poster")).toHaveCount(1);
|
await expect(page.locator(".journey-poster")).toHaveCount(1);
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur());
|
||||||
|
await page.mouse.move(1, 1);
|
||||||
|
|
||||||
const first = await captureViewport(page, testInfo, "visual-static-a");
|
const first = await captureViewport(page, testInfo, "visual-static-a");
|
||||||
await page.waitForTimeout(200);
|
await page.waitForTimeout(200);
|
||||||
@@ -352,9 +373,13 @@ test.describe("production visual composition", () => {
|
|||||||
await page.evaluate(() => document.fonts.ready);
|
await page.evaluate(() => document.fonts.ready);
|
||||||
await expect(page.locator(".journey-canvas")).toHaveCount(0);
|
await expect(page.locator(".journey-canvas")).toHaveCount(0);
|
||||||
await expect(page.locator(".journey-poster")).toHaveCount(1);
|
await expect(page.locator(".journey-poster")).toHaveCount(1);
|
||||||
|
await page.getByRole("button", { name: "Drive settings" }).click();
|
||||||
await expect(page.getByRole("status")).toContainText(
|
await expect(page.getByRole("status")).toContainText(
|
||||||
"reduced motion requested",
|
"reduced motion requested",
|
||||||
);
|
);
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur());
|
||||||
|
await page.mouse.move(1, 1);
|
||||||
await captureViewport(page, testInfo, "visual-reduced-motion");
|
await captureViewport(page, testInfo, "visual-reduced-motion");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -368,7 +393,8 @@ test.describe("production visual composition", () => {
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByRole("button", { name: "Mute sound" }),
|
page.getByRole("button", { name: "Mute sound" }),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(page.getByLabel("Sound volume")).toHaveValue("0.4");
|
await page.getByRole("button", { name: "Sound volume" }).click();
|
||||||
|
await expect(page.getByLabel("Sound volume level")).toHaveValue("0.4");
|
||||||
await captureViewport(page, testInfo, "visual-audio-running");
|
await captureViewport(page, testInfo, "visual-audio-running");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -480,11 +506,15 @@ test.describe("three-flash safety", () => {
|
|||||||
}, testInfo) => {
|
}, testInfo) => {
|
||||||
await page.setViewportSize({ width: 1440, height: 900 });
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
await waitForRenderedPortfolio(page, distUrl);
|
await waitForRenderedPortfolio(page, distUrl);
|
||||||
|
await page.getByRole("button", { name: "Drive settings" }).click();
|
||||||
await page.getByLabel("Quality control").selectOption("static");
|
await page.getByLabel("Quality control").selectOption("static");
|
||||||
await expect(page.locator(".journey-world")).toHaveAttribute(
|
await expect(page.locator(".journey-world")).toHaveAttribute(
|
||||||
"data-scene-state",
|
"data-scene-state",
|
||||||
"static",
|
"static",
|
||||||
);
|
);
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur());
|
||||||
|
await page.mouse.move(1, 1);
|
||||||
const staticResult = analyzeFlashSequence(
|
const staticResult = analyzeFlashSequence(
|
||||||
await captureWorldSequence(page.locator(".journey-world"), 8, 100),
|
await captureWorldSequence(page.locator(".journey-world"), 8, 100),
|
||||||
10,
|
10,
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ describe('SceneCanvas', () => {
|
|||||||
expect(sceneMocks.build).not.toHaveBeenCalled();
|
expect(sceneMocks.build).not.toHaveBeenCalled();
|
||||||
expect(wrapper.findAll('canvas')).toHaveLength(0);
|
expect(wrapper.findAll('canvas')).toHaveLength(0);
|
||||||
expect(wrapper.get('img').attributes()).toMatchObject({ alt: '', width: '1600', height: '900' });
|
expect(wrapper.get('img').attributes()).toMatchObject({ alt: '', width: '1600', height: '900' });
|
||||||
|
expect(wrapper.findAll('.journey-poster-composition > span')).toHaveLength(7);
|
||||||
|
expect(wrapper.find('.journey-poster__sun').exists()).toBe(true);
|
||||||
|
expect(wrapper.find('.journey-poster__road').exists()).toBe(true);
|
||||||
|
expect(wrapper.find('.journey-poster__car').exists()).toBe(true);
|
||||||
expect(wrapper.text()).toContain('A decorative night road behind the portfolio content.');
|
expect(wrapper.text()).toContain('A decorative night road behind the portfolio content.');
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user