|
@@ -200,46 +200,83 @@ export default {
|
|
|
children.push({ type: 'rect', shape: rectShape, style: { fill: fillStyle, stroke: 'none' } });
|
|
children.push({ type: 'rect', shape: rectShape, style: { fill: fillStyle, stroke: 'none' } });
|
|
|
|
|
|
|
|
// C. 绘制内部图标与文本
|
|
// C. 绘制内部图标与文本
|
|
|
- const fs = Math.max(0.8, s * 0.9);
|
|
|
|
|
- if (type === 'green' && blockWidth > 15) {
|
|
|
|
|
- const darkWidth = Math.round(50 * fs);
|
|
|
|
|
|
|
+ // 提取基础缩放率
|
|
|
|
|
+ const baseFs = Math.max(0.8, s * 0.9);
|
|
|
|
|
+ // 只要宽度大于 5 像素就尝试去渲染(原版限制是 > 15,改小以支持极限压缩)
|
|
|
|
|
+ if (type === 'green' && blockWidth > 5) {
|
|
|
|
|
+
|
|
|
|
|
+ // --- 1. 将 iconValue 统一解析为数组,提前判断需要多宽的背景 ---
|
|
|
|
|
+ let iconList = [];
|
|
|
|
|
+ if (Array.isArray(iconValue)) {
|
|
|
|
|
+ iconList = iconValue;
|
|
|
|
|
+ } else if (typeof iconValue === 'string' && iconValue.trim() !== '') {
|
|
|
|
|
+ iconList = iconValue.split(',');
|
|
|
|
|
+ } else if (iconValue) {
|
|
|
|
|
+ iconList = [iconValue];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否有靠左(LT/LB)和靠右(RT/RB)的图标
|
|
|
|
|
+ let hasL = false, hasR = false;
|
|
|
|
|
+ iconList.forEach(icon => {
|
|
|
|
|
+ const valStr = String(icon).trim().toUpperCase();
|
|
|
|
|
+ const posConfig = POS_MAP[valStr] || { pos: 'RB' };
|
|
|
|
|
+ const pos = typeof posConfig === 'string' ? posConfig : (posConfig.pos || 'RB');
|
|
|
|
|
+ if (pos.includes('L')) hasL = true;
|
|
|
|
|
+ if (pos.includes('R')) hasR = true;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 核心:动态赋予深绿色区域的基础宽度!
|
|
|
|
|
+ // 如果左右都有图标,给46宽度;如果只有一侧有,收缩到28;啥都没给8
|
|
|
|
|
+ let idealDarkWidthBase = (hasL && hasR) ? 46 : (iconList.length > 0 ? 28 : 0);
|
|
|
|
|
+ if (idealDarkWidthBase === 0 && phaseName) idealDarkWidthBase = 8;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算当前缩放下,理想状态需要的总像素宽度
|
|
|
|
|
+ let idealDarkWidth = idealDarkWidthBase * baseFs;
|
|
|
|
|
+ let idealTextWidth = 26 * baseFs; // 预留给 "P1\n24" 这类文本的宽度
|
|
|
|
|
+ let totalNeededWidth = idealDarkWidth + 6 * baseFs + idealTextWidth;
|
|
|
|
|
+
|
|
|
|
|
+ // --- 2. 计算动态弹性缩放率 (如果外部方块太小,内部按比例整体缩小) ---
|
|
|
|
|
+ let innerScale = 1;
|
|
|
|
|
+ if (blockWidth < totalNeededWidth) {
|
|
|
|
|
+ // 最极限缩小到 15%,防止变成一个点引发渲染错误
|
|
|
|
|
+ innerScale = Math.max(0.15, blockWidth / totalNeededWidth);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 应用弹性缩放
|
|
|
|
|
+ const dynamicFs = baseFs * innerScale;
|
|
|
|
|
+ const darkWidth = idealDarkWidthBase * dynamicFs;
|
|
|
const midY = yPos + blockHeight / 2;
|
|
const midY = yPos + blockHeight / 2;
|
|
|
|
|
+ const pointerW = 4 * dynamicFs; // 中间那个小三角指针的大小也跟着缩放
|
|
|
|
|
|
|
|
const innerGroup = {
|
|
const innerGroup = {
|
|
|
type: 'group',
|
|
type: 'group',
|
|
|
|
|
+ // 用 clipPath 限制死边界,防止文字或图标因为四舍五入溢出色块
|
|
|
clipPath: { type: 'rect', shape: { x: start[0], y: yPos, width: blockWidth, height: blockHeight } },
|
|
clipPath: { type: 'rect', shape: { x: start[0], y: yPos, width: blockWidth, height: blockHeight } },
|
|
|
children: [
|
|
children: [
|
|
|
{ type: 'rect', shape: { x: start[0], y: yPos, width: darkWidth, height: blockHeight }, style: { fill: COLORS.GREEN_DARK } },
|
|
{ type: 'rect', shape: { x: start[0], y: yPos, width: darkWidth, height: blockHeight }, style: { fill: COLORS.GREEN_DARK } },
|
|
|
{
|
|
{
|
|
|
type: 'polygon',
|
|
type: 'polygon',
|
|
|
- shape: { points: [ [start[0] + darkWidth, midY - 4 * fs], [start[0] + darkWidth, midY + 4 * fs], [start[0] + darkWidth + 4 * fs, midY] ] },
|
|
|
|
|
|
|
+ shape: { points: [
|
|
|
|
|
+ [start[0] + darkWidth, midY - pointerW],
|
|
|
|
|
+ [start[0] + darkWidth, midY + pointerW],
|
|
|
|
|
+ [start[0] + darkWidth + pointerW, midY]
|
|
|
|
|
+ ] },
|
|
|
style: { fill: COLORS.GREEN_DARK }
|
|
style: { fill: COLORS.GREEN_DARK }
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // --- 核心修改:支持将传入的 iconValue 作为多个图标渲染 ---
|
|
|
|
|
- // 将 iconValue 统一解析为数组,支持数组格式 ['A', 'B'] 或 字符串逗号分隔格式 'A,B'
|
|
|
|
|
- let iconList = [];
|
|
|
|
|
- if (Array.isArray(iconValue)) {
|
|
|
|
|
- iconList = iconValue;
|
|
|
|
|
- } else if (typeof iconValue === 'string' && iconValue.trim() !== '') {
|
|
|
|
|
- iconList = iconValue.split(',');
|
|
|
|
|
- } else if (iconValue) {
|
|
|
|
|
- iconList = [iconValue];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 遍历所有图标,按它们各自配置的 pos (LT/RB/RT/LB) 计算坐标并绘制
|
|
|
|
|
|
|
+ // --- 3. 绘制内部图标 ---
|
|
|
iconList.forEach(icon => {
|
|
iconList.forEach(icon => {
|
|
|
const valStr = String(icon).trim().toUpperCase();
|
|
const valStr = String(icon).trim().toUpperCase();
|
|
|
const posConfig = POS_MAP[valStr] || { pos: 'RB', padX: 0, padY: 0, baseW: 20, baseH: 20 };
|
|
const posConfig = POS_MAP[valStr] || { pos: 'RB', padX: 0, padY: 0, baseW: 20, baseH: 20 };
|
|
|
const pos = typeof posConfig === 'string' ? posConfig : (posConfig.pos || 'RB');
|
|
const pos = typeof posConfig === 'string' ? posConfig : (posConfig.pos || 'RB');
|
|
|
|
|
|
|
|
- const drawW = Math.round((posConfig.baseW || 20) * fs);
|
|
|
|
|
- const drawH = Math.round((posConfig.baseH || 20) * fs);
|
|
|
|
|
-
|
|
|
|
|
- const padX = Math.round((posConfig.padX || 0) * fs);
|
|
|
|
|
- const padY = Math.round((posConfig.padY || 0) * fs);
|
|
|
|
|
|
|
+ // 图标尺寸和边距也应用了 dynamicFs 动态缩放
|
|
|
|
|
+ const drawW = Math.round((posConfig.baseW || 20) * dynamicFs);
|
|
|
|
|
+ const drawH = Math.round((posConfig.baseH || 20) * dynamicFs);
|
|
|
|
|
+ const padX = Math.round((posConfig.padX || 0) * dynamicFs);
|
|
|
|
|
+ const padY = Math.round((posConfig.padY || 0) * dynamicFs);
|
|
|
|
|
|
|
|
let iconX, iconY;
|
|
let iconX, iconY;
|
|
|
if (pos === 'LT') {
|
|
if (pos === 'LT') {
|
|
@@ -256,7 +293,6 @@ export default {
|
|
|
iconY = yPos + blockHeight - drawH - padY;
|
|
iconY = yPos + blockHeight - drawH - padY;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 绘制单个图标并推入容器
|
|
|
|
|
if (IMAGE_MAP[valStr]) {
|
|
if (IMAGE_MAP[valStr]) {
|
|
|
innerGroup.children.push({
|
|
innerGroup.children.push({
|
|
|
type: 'image',
|
|
type: 'image',
|
|
@@ -272,20 +308,31 @@ export default {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // 渲染文本 (相位号与时长)
|
|
|
|
|
- innerGroup.children.push({
|
|
|
|
|
- type: 'text',
|
|
|
|
|
- style: {
|
|
|
|
|
- text: `${phaseName}\n${duration}`,
|
|
|
|
|
- x: start[0] + darkWidth + Math.round(6 * fs),
|
|
|
|
|
- y: midY,
|
|
|
|
|
- fill: COLORS.TEXT_DARK,
|
|
|
|
|
- fontSize: Math.max(10, Math.round(12 * fs)),
|
|
|
|
|
- fontWeight: 'bold',
|
|
|
|
|
- align: 'left',
|
|
|
|
|
- verticalAlign: 'middle'
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ // --- 4. 渲染右侧文字 (相位号与时长) ---
|
|
|
|
|
+ // 彻底移除 8px 的硬性下限兜底,让字体完全跟随 dynamicFs 比例等比缩小
|
|
|
|
|
+ const fontSize = Math.max(1, 12 * dynamicFs);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算文本起点的X坐标
|
|
|
|
|
+ const textStartX = start[0] + darkWidth + pointerW + (2 * dynamicFs);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果剩余空间大于 0(有哪怕一丁点空间),才进行文字渲染
|
|
|
|
|
+ if (blockWidth > (darkWidth + pointerW + 2)) {
|
|
|
|
|
+ innerGroup.children.push({
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ style: {
|
|
|
|
|
+ text: `${phaseName}\n${duration}`,
|
|
|
|
|
+ x: textStartX,
|
|
|
|
|
+ y: midY,
|
|
|
|
|
+ fill: COLORS.TEXT_DARK,
|
|
|
|
|
+ fontSize: fontSize,
|
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
|
+ align: 'left',
|
|
|
|
|
+ verticalAlign: 'middle',
|
|
|
|
|
+ // 行高也严格跟随动态字号
|
|
|
|
|
+ lineHeight: fontSize * 1.2
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
children.push(innerGroup);
|
|
children.push(innerGroup);
|
|
|
}
|
|
}
|