Files
ttrpg-tools/src/components/md-deck/hooks/dimensions.ts
T

134 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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`
};
}