feat: shape for cards

This commit is contained in:
hyper
2026-03-14 15:48:55 +08:00
parent 984b8aa1c8
commit 107e6fd6a2
7 changed files with 142 additions and 31 deletions
+11 -1
View File
@@ -2,7 +2,7 @@
import { calculateDimensions } from './dimensions';
import { loadCSV, CSV } from '../../utils/csv-loader';
import { initLayerConfigs, formatLayers, initLayerConfigsForSide } from './layer-parser';
import type { CardData, LayerConfig, Dimensions, CardSide } from '../types';
import type { CardData, LayerConfig, Dimensions, CardSide, CardShape } from '../types';
/**
* 默认配置常量
@@ -24,6 +24,7 @@ export interface DeckState {
gridH: number;
bleed: number;
padding: number;
shape: CardShape;
fixed: boolean;
src: string;
rawSrc: string; // 原始 CSV 路径(用于生成代码时保持相对路径)
@@ -75,6 +76,7 @@ export interface DeckActions {
setGridH: (grid: number) => void;
setBleed: (bleed: number) => void;
setPadding: (padding: number) => void;
setShape: (shape: CardShape) => void;
// 数据设置
setCards: (cards: CSV<CardData>) => void;
@@ -144,6 +146,7 @@ export function createDeckStore(
gridH: DECK_DEFAULTS.GRID_H,
bleed: DECK_DEFAULTS.BLEED,
padding: DECK_DEFAULTS.PADDING,
shape: 'rectangle',
fixed: false,
src: initialSrc,
rawSrc: initialSrc,
@@ -206,6 +209,9 @@ export function createDeckStore(
setState({ padding });
updateDimensions();
};
const setShape = (shape: CardShape) => {
setState({ shape });
};
const setCards = (cards: CSV<CardData>) => setState({ cards, activeTab: 0 });
const setActiveTab = (index: number) => setState({ activeTab: index });
@@ -313,6 +319,9 @@ export function createDeckStore(
if (state.padding !== DECK_DEFAULTS.PADDING) {
parts.push(`padding="${state.padding}" `);
}
if (state.shape !== 'rectangle') {
parts.push(`shape="${state.shape}" `);
}
parts.push(`layers="${frontLayersStr}" `);
if (backLayersString) {
@@ -368,6 +377,7 @@ export function createDeckStore(
setGridH,
setBleed,
setPadding,
setShape,
setCards,
setActiveTab,
updateCardData,
@@ -0,0 +1,56 @@
import type { CardShape } from '../types';
/**
* 获取 CSS clip-path 值用于形状裁剪
*/
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';
}
}
/**
* 获取 SVG clipPath 定义
* @param id clipPath 的唯一 ID
* @param width 卡片宽度
* @param height 卡片高度
* @param shape 卡片形状
*/
export function getShapeSvgClipPath(
id: string,
width: number,
height: number,
shape: CardShape
): string {
const halfW = width / 2;
const halfH = height / 2;
switch (shape) {
case 'circle':
return `
<clipPath id="${id}">
<circle cx="${halfW}" cy="${halfH}" r="${halfW}" />
</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 '';
}
}