fix: scale detail overlay with canvas zoom
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-22 03:12:15 +08:00
parent 409770d386
commit ac02644d3f

View File

@ -66,9 +66,12 @@ function resolveOverlayText(
function renderOverlayComponentContent(
snapshot: DetailUiScreenOverlaySnapshot,
component: RenderableOverlayComponent,
scale: number,
) {
const content = resolveOverlayText(snapshot, component);
const lines = content.split("\n").filter(Boolean);
const iconSize = Math.max(12, Math.round(16 * scale));
const smallIconSize = Math.max(10, Math.round(14 * scale));
if (component.type === "detail_feature_card") {
const headline = lines[0] || content;
@ -77,7 +80,7 @@ function renderOverlayComponentContent(
<div className="flex h-full w-full flex-col justify-between">
<div className="flex items-start gap-3">
<div className="mt-0.5 rounded-2xl bg-white/70 p-2 text-blue-600">
<Sparkles className="h-4 w-4" />
<Sparkles style={{ width: iconSize, height: iconSize }} />
</div>
<div className="min-w-0 flex-1">
<div className="text-[0.95em] font-extrabold leading-[1.2]">{headline}</div>
@ -95,7 +98,7 @@ function renderOverlayComponentContent(
if (component.type === "detail_feature_tag") {
return (
<div className="flex h-full w-full items-center justify-center gap-1 whitespace-nowrap">
<Sparkles className="h-3.5 w-3.5" />
<Sparkles style={{ width: smallIconSize, height: smallIconSize }} />
<span>{content}</span>
</div>
);
@ -120,7 +123,7 @@ function renderOverlayComponentContent(
if (component.type === "detail_proof_bar") {
return (
<div className="flex h-full w-full items-center gap-3">
<ShieldCheck className="h-4.5 w-4.5 shrink-0" />
<ShieldCheck style={{ width: iconSize, height: iconSize }} className="shrink-0" />
<div className="min-w-0 text-[0.86em] font-semibold leading-[1.2]">{content}</div>
</div>
);
@ -130,7 +133,7 @@ function renderOverlayComponentContent(
return (
<div className="flex h-full w-full items-center justify-center gap-2 whitespace-nowrap">
<span>{content}</span>
<ArrowRight className="h-4.5 w-4.5" />
<ArrowRight style={{ width: iconSize, height: iconSize }} />
</div>
);
}
@ -139,7 +142,9 @@ function renderOverlayComponentContent(
return (
<div className="flex h-full w-full items-center justify-between gap-3">
<div className="min-w-0 text-[0.82em] font-semibold leading-[1.25]">{content}</div>
{component.type === "detail_cta_banner" ? <CheckCircle2 className="h-4.5 w-4.5 shrink-0" /> : null}
{component.type === "detail_cta_banner" ? (
<CheckCircle2 style={{ width: iconSize, height: iconSize }} className="shrink-0" />
) : null}
</div>
);
}
@ -151,6 +156,7 @@ function getOverlayComponentStyle(params: {
component: RenderableOverlayComponent;
snapshot: DetailUiScreenOverlaySnapshot;
content: string;
scale: number;
frameOverride?: {
x: number;
y: number;
@ -167,6 +173,7 @@ function getOverlayComponentStyle(params: {
const style = (params.component.style || {}) as Record<string, unknown>;
const widthScale = 100 / Math.max(1, params.snapshot.screenSize.width);
const heightScale = 100 / Math.max(1, params.snapshot.screenSize.height);
const scale = Math.max(0.55, Math.min(2.4, params.scale));
const overlayStyle: CSSProperties = {
position: "absolute",
@ -175,12 +182,15 @@ function getOverlayComponentStyle(params: {
width: `${Number(frame.width || 0) * widthScale}%`,
height: `${Number(frame.height || 0) * heightScale}%`,
zIndex: Number(params.component.layout?.zIndex || 1),
fontSize: typeof style.fontSize === "number" ? `${style.fontSize}px` : undefined,
fontSize:
typeof style.fontSize === "number" ? `${style.fontSize * scale}px` : undefined,
fontFamily: typeof style.fontFamily === "string" ? style.fontFamily : undefined,
fontWeight: typeof style.fontWeight === "string" ? style.fontWeight : undefined,
lineHeight: typeof style.lineHeight === "number" ? style.lineHeight : undefined,
letterSpacing:
typeof style.letterSpacing === "number" ? `${style.letterSpacing}px` : undefined,
typeof style.letterSpacing === "number"
? `${style.letterSpacing * scale}px`
: undefined,
color: typeof style.color === "string" ? style.color : undefined,
textAlign:
style.textAlign === "center" || style.textAlign === "right"
@ -195,17 +205,26 @@ function getOverlayComponentStyle(params: {
background: typeof style.backgroundColor === "string" ? style.backgroundColor : undefined,
borderColor: typeof style.borderColor === "string" ? style.borderColor : undefined,
borderWidth:
typeof style.borderWidth === "number" ? `${style.borderWidth}px` : undefined,
typeof style.borderWidth === "number"
? `${Math.max(1, style.borderWidth * scale)}px`
: undefined,
borderStyle:
typeof style.borderWidth === "number" && style.borderWidth > 0 ? "solid" : undefined,
borderRadius:
typeof style.borderRadius === "number" ? `${style.borderRadius}px` : undefined,
padding: typeof style.padding === "number" ? `${style.padding}px` : undefined,
typeof style.borderRadius === "number"
? `${style.borderRadius * scale}px`
: undefined,
padding:
typeof style.padding === "number" ? `${style.padding * scale}px` : undefined,
boxShadow: typeof style.boxShadow === "string" ? style.boxShadow : undefined,
backdropFilter:
typeof style.backdropBlur === "number" ? `blur(${style.backdropBlur}px)` : undefined,
typeof style.backdropBlur === "number"
? `blur(${style.backdropBlur * scale}px)`
: undefined,
WebkitBackdropFilter:
typeof style.backdropBlur === "number" ? `blur(${style.backdropBlur}px)` : undefined,
typeof style.backdropBlur === "number"
? `blur(${style.backdropBlur * scale}px)`
: undefined,
overflow: "hidden",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
@ -467,6 +486,10 @@ export function DetailSurfaceOverlayRenderer(params: {
params.imageMetrics && params.imageMetrics.renderedHeight > 0
? params.imageMetrics.renderedHeight
: params.overlay.screenSize.height;
const overlayScale = Math.min(
overlayWidth / Math.max(1, params.overlay.screenSize.width),
overlayHeight / Math.max(1, params.overlay.screenSize.height),
);
const frameOverrides = buildFrameOverrides({
overlay: params.overlay,
components,
@ -508,6 +531,7 @@ export function DetailSurfaceOverlayRenderer(params: {
component,
snapshot: params.overlay!,
content,
scale: overlayScale,
frameOverride: (() => {
const frame = frameOverrides.get(component.id);
if (!frame) return undefined;
@ -520,7 +544,7 @@ export function DetailSurfaceOverlayRenderer(params: {
})(),
})}
>
{renderOverlayComponentContent(params.overlay!, component)}
{renderOverlayComponentContent(params.overlay!, component, overlayScale)}
</div>
);
})}