134 lines
3.8 KiB
TypeScript
134 lines
3.8 KiB
TypeScript
import type { Dimensions } from '../types';
|
||
|
||
export interface DimensionOptions {
|
||
sizeW: number;
|
||
sizeH: number;
|
||
bleed: number;
|
||
padding: number;
|
||
gridW: number;
|
||
gridH: number;
|
||
}
|
||
|
||
/**
|
||
* 解析卡牌尺寸和网格配置
|
||
*/
|
||
export function calculateDimensions(options: DimensionOptions): Dimensions {
|
||
// 实际卡牌尺寸(含出血)
|
||
const cardWidth = options.sizeW + options.bleed * 2;
|
||
const cardHeight = options.sizeH + options.bleed * 2;
|
||
|
||
// 网格区域尺寸(减去 padding)
|
||
const gridAreaWidth = options.sizeW - options.padding * 2;
|
||
const gridAreaHeight = options.sizeH - options.padding * 2;
|
||
|
||
// 每个网格单元的尺寸(mm)
|
||
const cellWidth = gridAreaWidth / options.gridW;
|
||
const cellHeight = gridAreaHeight / options.gridH;
|
||
|
||
// 网格区域起点(相对于卡牌左上角,含 bleed 和 padding)
|
||
const gridOriginX = options.bleed + options.padding;
|
||
const gridOriginY = options.bleed + options.padding;
|
||
|
||
return {
|
||
cardWidth,
|
||
cardHeight,
|
||
gridAreaWidth,
|
||
gridAreaHeight,
|
||
cellWidth,
|
||
cellHeight,
|
||
gridW: options.gridW,
|
||
gridH: options.gridH,
|
||
gridOriginX,
|
||
gridOriginY,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 计算 layer 位置样式(单位:mm)
|
||
* 根据 orientation 旋转元素,同时保持覆盖相同的区域
|
||
*
|
||
* orientation 含义:
|
||
* - n (0°): 正常方向,内容从上到下阅读
|
||
* - e (90°): 顺时针旋转 90°,内容从右到左阅读(右侧为顶部)
|
||
* - s (180°): 旋转 180°,内容从下到上阅读
|
||
* - w (270°): 顺时针旋转 270°,内容从左到右阅读(左侧为顶部)
|
||
*/
|
||
export function getLayerStyle(
|
||
layer: { x1: number; y1: number; x2: number; y2: number; orientation?: 'n' | 's' | 'e' | 'w' },
|
||
dims: Dimensions
|
||
): {
|
||
left: string;
|
||
top: string;
|
||
width: string;
|
||
height: string;
|
||
transform?: string;
|
||
'transform-origin'?: string;
|
||
} {
|
||
const cellWidth = dims.cellWidth;
|
||
const cellHeight = dims.cellHeight;
|
||
|
||
// 计算原始矩形的边界(相对于网格区域起点)
|
||
const baseLeft = (layer.x1 - 1) * cellWidth;
|
||
const baseTop = (layer.y1 - 1) * cellHeight;
|
||
const baseWidth = (layer.x2 - layer.x1 + 1) * cellWidth;
|
||
const baseHeight = (layer.y2 - layer.y1 + 1) * cellHeight;
|
||
|
||
const orientation = layer.orientation ?? 'n';
|
||
|
||
// 根据方向决定旋转角度
|
||
// n: 0°, e: 90°, s: 180°, w: 270°
|
||
const rotationMap: Record<'n' | 's' | 'e' | 'w', number> = {
|
||
n: 0,
|
||
e: 90,
|
||
s: 180,
|
||
w: 270
|
||
};
|
||
|
||
const rotation = rotationMap[orientation];
|
||
|
||
// 对于 e/w 方向,需要交换宽高
|
||
if (orientation === 'e' || orientation === 'w') {
|
||
// 计算旋转后的位置,使得元素覆盖相同的区域
|
||
// 旋转 90°/270°后,原来的宽度变成高度,高度变成宽度
|
||
// 需要调整 left/top 使得旋转后的元素中心与原始矩形中心重合
|
||
|
||
// 元素尺寸交换
|
||
const width = baseHeight;
|
||
const height = baseWidth;
|
||
|
||
// 计算新起点,使得旋转后覆盖相同区域
|
||
// 旋转中心设为元素中心,旋转后元素覆盖的区域与原始区域相同
|
||
const left = baseLeft + (baseWidth - width) / 2;
|
||
const top = baseTop + (baseHeight - height) / 2;
|
||
|
||
return {
|
||
left: `${left}mm`,
|
||
top: `${top}mm`,
|
||
width: `${width}mm`,
|
||
height: `${height}mm`,
|
||
transform: `rotate(${rotation}deg)`,
|
||
'transform-origin': 'center center'
|
||
};
|
||
}
|
||
|
||
// n 或 s 方向,宽高不变
|
||
if (orientation === 's') {
|
||
return {
|
||
left: `${baseLeft}mm`,
|
||
top: `${baseTop}mm`,
|
||
width: `${baseWidth}mm`,
|
||
height: `${baseHeight}mm`,
|
||
transform: `rotate(180deg)`,
|
||
'transform-origin': 'center center'
|
||
};
|
||
}
|
||
|
||
// n 方向(默认)
|
||
return {
|
||
left: `${baseLeft}mm`,
|
||
top: `${baseTop}mm`,
|
||
width: `${baseWidth}mm`,
|
||
height: `${baseHeight}mm`
|
||
};
|
||
}
|