refactor: preview contour correctly

This commit is contained in:
2026-03-15 14:55:50 +08:00
parent ee2fa057f6
commit 2b1dbd41e1
3 changed files with 77 additions and 34 deletions
+5 -1
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);
+1 -1
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">
+71 -32
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);
} }