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(() => const selectionStyle = createMemo(() =>
getSelectionBoxStyle(store.state.selectStart, store.state.selectEnd, store.state.dimensions) 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); const selection = useCardSelection(store);

View File

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

View File

@ -1,20 +1,52 @@
import type { CardShape } from '../types'; 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 * CSS clip-path
*
* @param shape
* @param width
* @param height
* @param cornerRadius >0 使
* @param segmentsPerCorner
*/ */
export function getShapeClipPath(shape: CardShape): string { export function getShapeClipPath(
switch (shape) { shape: CardShape,
case 'circle': width: number,
return 'circle(50% at 50% 50%)'; height: number,
case 'triangle': cornerRadius: number = 0,
return 'polygon(50% 0%, 0% 100%, 100% 100%)'; segmentsPerCorner: number = 4
case 'hexagon': ): string {
return 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)'; // 无圆角的基本形状使用简化的 CSS clip-path
case 'rectangle': if (cornerRadius <= 0) {
default: switch (shape) {
return 'none'; 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 width
* @param height * @param height
* @param shape * @param shape
* @param cornerRadius
* @param segmentsPerCorner
*/ */
export function getShapeSvgClipPath( export function getShapeSvgClipPath(
id: string, id: string,
width: number, width: number,
height: number, height: number,
shape: CardShape shape: CardShape,
cornerRadius: number = 0,
segmentsPerCorner: number = 4
): string { ): string {
const halfW = width / 2; const points = getCardShapePoints(shape, width, height, cornerRadius, segmentsPerCorner);
const halfH = height / 2; const pathData = contourToSvgPath(points, true);
switch (shape) { return `
case 'circle':
return `
<clipPath id="${id}"> <clipPath id="${id}">
<circle cx="${halfW}" cy="${halfH}" r="${halfW}" /> <path d="${pathData}" />
</clipPath>`; </clipPath>`;
case 'triangle': }
return `
<clipPath id="${id}"> /**
<polygon points="${halfW},0 0,${height} ${width},${height}" /> * SVG path
</clipPath>`; * @param width
case 'hexagon': * @param height
return ` * @param shape
<clipPath id="${id}"> * @param cornerRadius
<polygon points="${halfW},0 ${width},${height * 0.25} ${width},${height * 0.75} ${halfW},${height} 0,${height * 0.75} 0,${height * 0.25}" /> * @param segmentsPerCorner
</clipPath>`; */
case 'rectangle': export function getCardShapePath(
default: width: number,
return ''; height: number,
} shape: CardShape,
cornerRadius: number = 0,
segmentsPerCorner: number = 4
): string {
const points = getCardShapePoints(shape, width, height, cornerRadius, segmentsPerCorner);
return contourToSvgPath(points, true);
} }