ai-chatbot/lib/agents/psd-image-placement.ts
Your Name dc04c1d91b
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: support dragging and scaling psd image slots
2026-03-31 20:40:06 +08:00

102 lines
2.7 KiB
TypeScript

import type { AgentPsdTemplateReplaceMode } from "@/lib/agents/psd-template-config";
export interface AgentPsdImagePlacement {
scale: number;
offsetX: number;
offsetY: number;
}
export interface AgentPsdImageSize {
width: number;
height: number;
}
export function getDefaultAgentPsdImagePlacement(): AgentPsdImagePlacement {
return {
scale: 1,
offsetX: 0,
offsetY: 0,
};
}
export function normalizeAgentPsdImagePlacement(
value: Partial<AgentPsdImagePlacement> | null | undefined
): AgentPsdImagePlacement {
const scale = Number(value?.scale);
const offsetX = Number(value?.offsetX);
const offsetY = Number(value?.offsetY);
return {
scale: Number.isFinite(scale) && scale > 0 ? Math.max(0.1, Math.min(8, scale)) : 1,
offsetX: Number.isFinite(offsetX) ? offsetX : 0,
offsetY: Number.isFinite(offsetY) ? offsetY : 0,
};
}
function resolveBaseFittedSize(params: {
containerWidth: number;
containerHeight: number;
imageWidth: number;
imageHeight: number;
replaceMode: AgentPsdTemplateReplaceMode;
}) {
const { containerWidth, containerHeight, imageWidth, imageHeight, replaceMode } = params;
const safeImageWidth = Math.max(1, imageWidth);
const safeImageHeight = Math.max(1, imageHeight);
if (replaceMode === "stretch") {
return {
width: containerWidth,
height: containerHeight,
left: 0,
top: 0,
};
}
const fitRatio =
replaceMode === "contain"
? Math.min(containerWidth / safeImageWidth, containerHeight / safeImageHeight)
: Math.max(containerWidth / safeImageWidth, containerHeight / safeImageHeight);
const width = safeImageWidth * fitRatio;
const height = safeImageHeight * fitRatio;
return {
width,
height,
left: (containerWidth - width) / 2,
top: (containerHeight - height) / 2,
};
}
export function resolveAgentPsdImagePlacementFrame(params: {
containerWidth: number;
containerHeight: number;
imageWidth: number;
imageHeight: number;
replaceMode: AgentPsdTemplateReplaceMode;
placement?: Partial<AgentPsdImagePlacement> | null;
}) {
const { containerWidth, containerHeight, imageWidth, imageHeight, replaceMode } = params;
const placement = normalizeAgentPsdImagePlacement(params.placement);
const baseFrame = resolveBaseFittedSize({
containerWidth,
containerHeight,
imageWidth,
imageHeight,
replaceMode,
});
const width = baseFrame.width * placement.scale;
const height = baseFrame.height * placement.scale;
const left = baseFrame.left - (width - baseFrame.width) / 2 + placement.offsetX;
const top = baseFrame.top - (height - baseFrame.height) / 2 + placement.offsetY;
return {
width,
height,
left,
top,
};
}