refactor: preview contour correctly

This commit is contained in:
hypercross 2026-03-15 14:55:50 +08:00
parent ee2fa057f6
commit 2b1dbd41e1
3 changed files with 77 additions and 34 deletions

View File

@ -19,7 +19,11 @@ export function CardPreview(props: CardPreviewProps) {
const selectionStyle = createMemo(() =>
getSelectionBoxStyle(store.state.selectStart, store.state.selectEnd, store.state.dimensions)
);
const shapeClipPath = createMemo(() => getShapeClipPath(store.state.shape));
const shapeClipPath = createMemo(() => {
const dims = store.state.dimensions;
if (!dims) return 'none';
return getShapeClipPath(store.state.shape, dims.cardWidth, dims.cardHeight, store.state.cornerRadius);
});
const selection = useCardSelection(store);

View File

@ -152,7 +152,7 @@ export function PrintPreview(props: PrintPreviewProps) {
const cardWidth = store.state.dimensions?.cardWidth || 56;
const cardHeight = store.state.dimensions?.cardHeight || 88;
const clipPathId = `clip-${page.pageIndex}-${card.data.id || card.x}-${card.y}`;
const shapeClipPath = getShapeSvgClipPath(clipPathId, cardWidth, cardHeight, store.state.shape);
const shapeClipPath = getShapeSvgClipPath(clipPathId, cardWidth, cardHeight, store.state.shape, store.state.cornerRadius);
return (
<g class="card-group">

View File

@ -1,20 +1,52 @@
import type { CardShape } from '../types';
import { getCardShapePoints, contourToSvgPath } from '../../../plotcutter/contour';
/**
* CSS polygon()
* @param points mm
* @returns CSS polygon() 使
*/
function pointsToCssPolygon(points: [number, number][], width: number, height: number): string {
if (points.length === 0) return 'none';
const coords = points.map(([x, y]) => {
const percentX = (x / width) * 100;
const percentY = (y / height) * 100;
return `${percentX.toFixed(4)}% ${percentY.toFixed(4)}%`;
});
return `polygon(${coords.join(', ')})`;
}
/**
* CSS clip-path
*
* @param shape
* @param width
* @param height
* @param cornerRadius >0 使
* @param segmentsPerCorner
*/
export function getShapeClipPath(shape: CardShape): string {
switch (shape) {
case 'circle':
return 'circle(50% at 50% 50%)';
case 'triangle':
return 'polygon(50% 0%, 0% 100%, 100% 100%)';
case 'hexagon':
return 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)';
case 'rectangle':
default:
return 'none';
export function getShapeClipPath(
shape: CardShape,
width: number,
height: number,
cornerRadius: number = 0,
segmentsPerCorner: number = 4
): string {
// 无圆角的基本形状使用简化的 CSS clip-path
if (cornerRadius <= 0) {
switch (shape) {
case 'circle':
return 'circle(50% at 50% 50%)';
case 'rectangle':
return 'none';
}
}
// 其他情况使用多边形近似(包括圆角形状、三角形、六边形)
const points = getCardShapePoints(shape, width, height, cornerRadius, segmentsPerCorner);
return pointsToCssPolygon(points, width, height);
}
/**
@ -23,34 +55,41 @@ export function getShapeClipPath(shape: CardShape): string {
* @param width
* @param height
* @param shape
* @param cornerRadius
* @param segmentsPerCorner
*/
export function getShapeSvgClipPath(
id: string,
width: number,
height: number,
shape: CardShape
shape: CardShape,
cornerRadius: number = 0,
segmentsPerCorner: number = 4
): string {
const halfW = width / 2;
const halfH = height / 2;
const points = getCardShapePoints(shape, width, height, cornerRadius, segmentsPerCorner);
const pathData = contourToSvgPath(points, true);
switch (shape) {
case 'circle':
return `
return `
<clipPath id="${id}">
<circle cx="${halfW}" cy="${halfH}" r="${halfW}" />
<path d="${pathData}" />
</clipPath>`;
case 'triangle':
return `
<clipPath id="${id}">
<polygon points="${halfW},0 0,${height} ${width},${height}" />
</clipPath>`;
case 'hexagon':
return `
<clipPath id="${id}">
<polygon points="${halfW},0 ${width},${height * 0.25} ${width},${height * 0.75} ${halfW},${height} 0,${height * 0.75} 0,${height * 0.25}" />
</clipPath>`;
case 'rectangle':
default:
return '';
}
}
/**
* SVG path
* @param width
* @param height
* @param shape
* @param cornerRadius
* @param segmentsPerCorner
*/
export function getCardShapePath(
width: number,
height: number,
shape: CardShape,
cornerRadius: number = 0,
segmentsPerCorner: number = 4
): string {
const points = getCardShapePoints(shape, width, height, cornerRadius, segmentsPerCorner);
return contourToSvgPath(points, true);
}