feat: 添加描述区功能,更新相关组件和样式以支持描述信息的显示
This commit is contained in:
parent
48295b20e6
commit
39044f3d65
@ -44,7 +44,7 @@ export default [
|
|||||||
quotes: ['error', 'single'],
|
quotes: ['error', 'single'],
|
||||||
semi: ['error', 'always'],
|
semi: ['error', 'always'],
|
||||||
'max-len': ['warn', { code: 120, ignoreComments: true, ignoreUrls: true, ignoreTemplateLiterals: true }],
|
'max-len': ['warn', { code: 120, ignoreComments: true, ignoreUrls: true, ignoreTemplateLiterals: true }],
|
||||||
'max-lines': ['warn', { max: 1000, skipBlankLines: true, skipComments: true }],
|
'max-lines': ['warn', { max: 2000, skipBlankLines: true, skipComments: true }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -118,6 +118,8 @@ export enum MapAreaType {
|
|||||||
非互斥区,
|
非互斥区,
|
||||||
/** 约束区 - 机器人运动受到特定约束的区域 */
|
/** 约束区 - 机器人运动受到特定约束的区域 */
|
||||||
约束区,
|
约束区,
|
||||||
|
/** 描述区 - 用于显示描述信息的特殊区域 */
|
||||||
|
描述区,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,6 +11,9 @@ $icons: (
|
|||||||
area13-active,
|
area13-active,
|
||||||
area13-detail,
|
area13-detail,
|
||||||
area13,
|
area13,
|
||||||
|
area14-active,
|
||||||
|
area14-detail,
|
||||||
|
area14,
|
||||||
battery_charge,
|
battery_charge,
|
||||||
battery,
|
battery,
|
||||||
connect_off,
|
connect_off,
|
||||||
|
BIN
src/assets/icons/dark/area14-active.png
Normal file
BIN
src/assets/icons/dark/area14-active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 240 B |
BIN
src/assets/icons/dark/area14-detail.png
Normal file
BIN
src/assets/icons/dark/area14-detail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 240 B |
BIN
src/assets/icons/dark/area14.png
Normal file
BIN
src/assets/icons/dark/area14.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 240 B |
BIN
src/assets/icons/light/area14-active.png
Normal file
BIN
src/assets/icons/light/area14-active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 240 B |
BIN
src/assets/icons/light/area14-detail.png
Normal file
BIN
src/assets/icons/light/area14-detail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 240 B |
BIN
src/assets/icons/light/area14.png
Normal file
BIN
src/assets/icons/light/area14.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 240 B |
@ -37,6 +37,8 @@
|
|||||||
"fill-12": "#0DBB8A33",
|
"fill-12": "#0DBB8A33",
|
||||||
"stroke-13": "#e61e4aad",
|
"stroke-13": "#e61e4aad",
|
||||||
"fill-13": "#e61e4a33",
|
"fill-13": "#e61e4a33",
|
||||||
|
"stroke-14": "#FFD70099",
|
||||||
|
"fill-14": "#FFD70033",
|
||||||
"strokeActive": "#FCC947"
|
"strokeActive": "#FCC947"
|
||||||
},
|
},
|
||||||
"robot": {
|
"robot": {
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
"fill-12": "#0DBB8A33",
|
"fill-12": "#0DBB8A33",
|
||||||
"stroke-13": "#e61e4aad",
|
"stroke-13": "#e61e4aad",
|
||||||
"fill-13": "#e61e4a33",
|
"fill-13": "#e61e4a33",
|
||||||
|
"stroke-14": "#FFD70099",
|
||||||
|
"fill-14": "#FFD70033",
|
||||||
"strokeActive": "#EBB214"
|
"strokeActive": "#EBB214"
|
||||||
},
|
},
|
||||||
"robot": {
|
"robot": {
|
||||||
|
@ -87,6 +87,7 @@ const routes = computed<string[]>(
|
|||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-select
|
<a-select
|
||||||
class="full"
|
class="full"
|
||||||
|
:placeholder="$t('请选择库区规则')"
|
||||||
:value="pen.area?.inoutflag"
|
:value="pen.area?.inoutflag"
|
||||||
@change="editor.updateArea(id, { inoutflag: $event as 1 | 2 })"
|
@change="editor.updateArea(id, { inoutflag: $event as 1 | 2 })"
|
||||||
>
|
>
|
||||||
@ -103,9 +104,12 @@ const routes = computed<string[]>(
|
|||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-textarea
|
<a-textarea
|
||||||
class="prop"
|
class="prop"
|
||||||
:placeholder="$t('请输入描述内容')"
|
:placeholder="area.type === MapAreaType.描述区 ? $t('请输入要显示的文字内容') : $t('请输入描述内容')"
|
||||||
:maxlength="100"
|
:maxlength="area.type === MapAreaType.描述区 ? 50 : 100"
|
||||||
:autoSize="{ minRows: 3, maxRows: 3 }"
|
:autoSize="{
|
||||||
|
minRows: area.type === MapAreaType.描述区 ? 2 : 3,
|
||||||
|
maxRows: area.type === MapAreaType.描述区 ? 2 : 3,
|
||||||
|
}"
|
||||||
:value="pen?.desc"
|
:value="pen?.desc"
|
||||||
@change="editor.updatePen(id, { desc: $event.target.value }, false)"
|
@change="editor.updatePen(id, { desc: $event.target.value }, false)"
|
||||||
/>
|
/>
|
||||||
@ -138,7 +142,10 @@ const routes = computed<string[]>(
|
|||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
|
|
||||||
<a-collapse-panel
|
<a-collapse-panel
|
||||||
v-if="[MapAreaType.互斥区, MapAreaType.非互斥区, MapAreaType.约束区].includes(area.type)"
|
v-if="
|
||||||
|
[MapAreaType.互斥区, MapAreaType.非互斥区, MapAreaType.约束区].includes(area.type) &&
|
||||||
|
area.type !== MapAreaType.描述区
|
||||||
|
"
|
||||||
:header="$t('绑定站点')"
|
:header="$t('绑定站点')"
|
||||||
>
|
>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
|
@ -79,6 +79,15 @@ const canDelete = computed<boolean>(() => editor.value.current.value?.name === '
|
|||||||
>
|
>
|
||||||
<i class="icon" :class="mode === MapAreaType.约束区 ? 'area13-active' : 'area13'" />
|
<i class="icon" :class="mode === MapAreaType.约束区 ? 'area13-active' : 'area13'" />
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
class="icon-btn tool-btn ml-12"
|
||||||
|
:class="{ active: mode === MapAreaType.描述区 }"
|
||||||
|
size="large"
|
||||||
|
:title="$t('添加描述区')"
|
||||||
|
@click="mode = MapAreaType.描述区"
|
||||||
|
>
|
||||||
|
<i class="icon" :class="mode === MapAreaType.描述区 ? 'area14-active' : 'area14'" />
|
||||||
|
</a-button>
|
||||||
<a-divider class="size-24 mh-8" type="vertical" />
|
<a-divider class="size-24 mh-8" type="vertical" />
|
||||||
|
|
||||||
<a-button class="icon-btn tool-btn" size="large" :title="$t('保存')" @click="updateScene">
|
<a-button class="icon-btn tool-btn" size="large" :title="$t('保存')" @click="updateScene">
|
||||||
|
@ -1122,6 +1122,9 @@ export class EditorService extends Meta2d {
|
|||||||
case MapAreaType.约束区:
|
case MapAreaType.约束区:
|
||||||
selected?.filter(({ point }) => point?.type).forEach(({ id }) => points.push(id!));
|
selected?.filter(({ point }) => point?.type).forEach(({ id }) => points.push(id!));
|
||||||
break;
|
break;
|
||||||
|
case MapAreaType.描述区:
|
||||||
|
// 描述区不需要绑定点位或路线
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -1451,7 +1454,7 @@ function drawArea(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
|||||||
const { active, fontSize = 14, lineHeight = 1.5, fontFamily } = pen.calculative ?? {};
|
const { active, fontSize = 14, lineHeight = 1.5, fontFamily } = pen.calculative ?? {};
|
||||||
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
|
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
|
||||||
const { type } = pen.area ?? {};
|
const { type } = pen.area ?? {};
|
||||||
const { label = '' } = pen ?? {};
|
const { label = '', desc = '' } = pen ?? {};
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.rect(x, y, w, h);
|
ctx.rect(x, y, w, h);
|
||||||
@ -1459,11 +1462,52 @@ function drawArea(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.strokeStyle = get(theme, active ? 'area.strokeActive' : `area.stroke-${type}`) ?? '';
|
ctx.strokeStyle = get(theme, active ? 'area.strokeActive' : `area.stroke-${type}`) ?? '';
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 如果是描述区且有描述内容,渲染描述文字
|
||||||
|
if (type === MapAreaType.描述区 && desc) {
|
||||||
|
ctx.fillStyle = get(theme, 'color') ?? '';
|
||||||
|
// 使用更大的字体显示描述文字
|
||||||
|
const descFontSize = Math.min(w / 6, h / 1.5, 128); // 根据区域大小自适应字体大小,最大128px
|
||||||
|
ctx.font = `${descFontSize}px ${fontFamily}`;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'top'; // 使用 top 基准,便于精确计算
|
||||||
|
|
||||||
|
// 文字换行处理
|
||||||
|
const words = desc.split('');
|
||||||
|
const maxCharsPerLine = Math.floor(w / (descFontSize * 0.6)); // 估算每行能容纳的字符数
|
||||||
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < words.length; i += maxCharsPerLine) {
|
||||||
|
lines.push(words.slice(i, i + maxCharsPerLine).join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 measureText 精确计算字体高度
|
||||||
|
const testText = '测试文字'; // 用于测量字体高度的测试文字
|
||||||
|
const textMetrics = ctx.measureText(testText);
|
||||||
|
const actualFontHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
|
||||||
|
const lineSpacing = actualFontHeight * 0.2; // 行间距为字体高度的20%
|
||||||
|
const descLineHeight = actualFontHeight + lineSpacing;
|
||||||
|
|
||||||
|
// 计算整个文本块的总高度
|
||||||
|
const totalHeight = lines.length * descLineHeight - lineSpacing; // 减去最后一行不需要的行间距
|
||||||
|
|
||||||
|
// 计算文本块的起始Y坐标,确保整个文本块在区域中垂直居中
|
||||||
|
const startY = y + h / 2 - totalHeight / 2;
|
||||||
|
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
const lineY = startY + index * descLineHeight;
|
||||||
|
// 水平居中:x + w / 2,垂直居中:lineY
|
||||||
|
ctx.fillText(line, x + w / 2, lineY);
|
||||||
|
});
|
||||||
|
} else if (type !== MapAreaType.描述区 && label) {
|
||||||
|
// 非描述区才显示标签
|
||||||
ctx.fillStyle = get(theme, 'color') ?? '';
|
ctx.fillStyle = get(theme, 'color') ?? '';
|
||||||
ctx.font = `${fontSize}px/${lineHeight} ${fontFamily}`;
|
ctx.font = `${fontSize}px/${lineHeight} ${fontFamily}`;
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
ctx.fillText(label, x + w / 2, y - fontSize * lineHeight);
|
ctx.fillText(label, x + w / 2, y - fontSize * lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user