feat: draw point
@ -24,7 +24,7 @@ export default [
|
||||
files: ['**/*.vue'],
|
||||
languageOptions: { parserOptions: { parser: tseslint.parser } },
|
||||
rules: {
|
||||
'vue/multi-word-component-names': ['warn', { ignores: ['index'] }],
|
||||
'vue/multi-word-component-names': ['warn', { ignores: ['index', 'exception'] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
public/point/11-light.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
public/point/12-light.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
public/point/13-light.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
public/point/14-light.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
public/point/15-light.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
@ -44,8 +44,6 @@ export const EDITOR_CONFIG: Options = {
|
||||
keydown: KeydownType.None,
|
||||
strictScope: true,
|
||||
moveConnectedLine: false,
|
||||
textRotate: false,
|
||||
textFlip: false,
|
||||
disableInput: true,
|
||||
disableRotate: true,
|
||||
disableSize: true,
|
||||
@ -53,12 +51,11 @@ export const EDITOR_CONFIG: Options = {
|
||||
disableEmptyLine: true,
|
||||
disableRepeatLine: true,
|
||||
minScale: 0.19,
|
||||
maxScale: 2.01,
|
||||
maxScale: 4.01,
|
||||
scaleOff: 0.01,
|
||||
defaultAnchors: [],
|
||||
activeGlobalAlpha: 0,
|
||||
fontSize: 14,
|
||||
lineHeight: 1.5,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'top',
|
||||
fontFamily: 'system-ui',
|
||||
};
|
||||
|
9
src/assets/themes/antd-dark.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"token": {
|
||||
"colorInfo": "#0ea278",
|
||||
"colorPrimary": "#0dbb8a",
|
||||
"colorTextBase": "#fafafa",
|
||||
"colorBgBase": "#2a2c2c",
|
||||
"colorBorderSecondary": "#595e5e"
|
||||
}
|
||||
}
|
@ -6,12 +6,12 @@
|
||||
"strokeActive": "#FCC947",
|
||||
"fill-1": "#14D1A5",
|
||||
"fill-2": "#69C6F5",
|
||||
"fill-3": "#E48B1D"
|
||||
"fill-3": "#E48B1D",
|
||||
"fill-4": "#E48B1D"
|
||||
},
|
||||
"point-l": {
|
||||
"stroke": "#595959",
|
||||
"strokeActive": "#FCC947",
|
||||
"fill": "#1F1F1F"
|
||||
"strokeActive": "#FCC947"
|
||||
},
|
||||
"line": "#8C8C8C"
|
||||
}
|
@ -21,6 +21,7 @@ const mode = ref<Mode>(Mode.常规);
|
||||
watch(editor.value.mouseClick, (v) => {
|
||||
if (mode.value !== Mode.添加点位) return;
|
||||
if (isEmpty(v)) return;
|
||||
editor.value.addPoint(v, 1);
|
||||
editor.value.addPoint(v, 11);
|
||||
});
|
||||
watch(editor.value.mouseBrush, (v) => {
|
||||
@ -44,4 +45,4 @@ watch(editor.value.mouseBrush, (v) => {
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<!-- <style scoped lang="scss"></style> -->
|
||||
|
10
src/pages/exception.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
type Props = {
|
||||
code: string;
|
||||
};
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>{{ code }}</template>
|
||||
|
||||
<!-- <style scoped lang="scss"></style> -->
|
@ -1,6 +1,6 @@
|
||||
import { EDITOR_CONFIG, MapAreaType, type MapPen, MapPointType } from '@api/map';
|
||||
import sTheme from '@core/theme.service';
|
||||
import { EditType, LockState, Meta2d } from '@meta2d/core';
|
||||
import { CanvasLayer, EditType, LockState, Meta2d } from '@meta2d/core';
|
||||
import { useObservable } from '@vueuse/rxjs';
|
||||
import { cloneDeep, get, pick } from 'lodash-es';
|
||||
import { debounceTime, filter, map, Subject, switchMap } from 'rxjs';
|
||||
@ -44,13 +44,19 @@ export class EditorService extends Meta2d {
|
||||
),
|
||||
);
|
||||
|
||||
public override find(target: string): MapPen[] {
|
||||
return super.find(target);
|
||||
}
|
||||
|
||||
//#region 点位
|
||||
public async addPoint(p: Point, type = MapPointType.普通点): Promise<void> {
|
||||
const pen: MapPen = {
|
||||
...p,
|
||||
...this.#mapPoint(type),
|
||||
...this.#mapPointImage(type),
|
||||
name: 'point',
|
||||
text: 'POINT',
|
||||
tags: ['point', `point-${type}`],
|
||||
label: 'POINT',
|
||||
point: { type },
|
||||
};
|
||||
const { x, y, width, height } = this.getPenRect(pen);
|
||||
@ -60,13 +66,17 @@ export class EditorService extends Meta2d {
|
||||
this.pushHistory({ type: EditType.Add, pens: [cloneDeep(pen)] });
|
||||
}
|
||||
|
||||
#mapPoint(type: MapPointType): Required<Pick<MapPen, 'width' | 'height' | 'lineWidth' | 'image'>> {
|
||||
const theme = this.data().theme;
|
||||
#mapPoint(type: MapPointType): Required<Pick<MapPen, 'width' | 'height' | 'lineWidth' | 'iconSize'>> {
|
||||
const width = type < 10 ? 24 : 48;
|
||||
const height = type < 10 ? 24 : 60;
|
||||
const lineWidth = type < 10 ? 2 : 3;
|
||||
const iconSize = type < 10 ? 4 : 10;
|
||||
return { width, height, lineWidth, iconSize };
|
||||
}
|
||||
#mapPointImage(type: MapPointType): Required<Pick<MapPen, 'image' | 'canvasLayer'>> {
|
||||
const theme = this.data().theme;
|
||||
const image = type < 10 ? '' : `/point/${type}-${theme}.png`;
|
||||
return { width, height, lineWidth, image };
|
||||
return { image, canvasLayer: CanvasLayer.CanvasMain };
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@ -81,6 +91,7 @@ export class EditorService extends Meta2d {
|
||||
if (w * scale < 50 || h * scale < 60) return;
|
||||
const pen: MapPen = {
|
||||
name: 'area',
|
||||
tags: ['area', `area-${type}`],
|
||||
x: Math.min(p1.x, p2.x),
|
||||
y: Math.min(p1.y, p2.y),
|
||||
width: w,
|
||||
@ -103,14 +114,31 @@ export class EditorService extends Meta2d {
|
||||
|
||||
watch(
|
||||
() => sTheme.theme,
|
||||
(v) => this.setTheme(v),
|
||||
(v) => this.#load(v),
|
||||
{ immediate: true },
|
||||
);
|
||||
}
|
||||
|
||||
#load(theme?: string): void {
|
||||
if (theme) {
|
||||
this.setTheme(theme);
|
||||
}
|
||||
|
||||
this.find('point').forEach((pen) => {
|
||||
if (!pen.point?.type) return;
|
||||
if (pen.point.type < 10) return;
|
||||
this.canvas.updateValue(pen, this.#mapPointImage(pen.point.type));
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
#listen(e: unknown, v: any) {
|
||||
switch (e) {
|
||||
case 'opened':
|
||||
this.#load();
|
||||
break;
|
||||
|
||||
case 'click':
|
||||
case 'mousedown':
|
||||
case 'mouseup':
|
||||
@ -132,8 +160,8 @@ export class EditorService extends Meta2d {
|
||||
//#region 绘制函数
|
||||
function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const theme = sTheme.editor;
|
||||
const active = pen.calculative?.active;
|
||||
const { x = 0, y = 0, width = 0, height = 0 } = pen.calculative?.worldRect ?? {};
|
||||
const { active, iconSize: r = 0, fontSize = 14, lineHeight = 1.5, fontFamily } = pen.calculative ?? {};
|
||||
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
|
||||
const { type } = pen.point ?? {};
|
||||
const { label = '' } = pen ?? {};
|
||||
|
||||
@ -143,28 +171,55 @@ function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
case MapPointType.等待点:
|
||||
case MapPointType.避让点:
|
||||
case MapPointType.临时避让点:
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + w / 2 - r, y + r);
|
||||
ctx.arcTo(x + w / 2, y, x + w - r, y + h / 2 - r, r);
|
||||
ctx.arcTo(x + w, y + h / 2, x + w / 2 + r, y + h - r, r);
|
||||
ctx.arcTo(x + w / 2, y + h, x + r, y + h / 2 + r, r);
|
||||
ctx.arcTo(x, y + h / 2, x + r, y + h / 2 - r, r);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = get(theme, `point-s.fill-${type}`) ?? '';
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = get(theme, active ? 'point-s.strokeActive' : 'point-s.stroke') ?? '';
|
||||
if (type === MapPointType.临时避让点) {
|
||||
ctx.lineCap = 'round';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + 0.66 * r, y + h / 2 - 0.66 * r);
|
||||
ctx.lineTo(x + r, y + h / 2 - r);
|
||||
ctx.moveTo(x + w / 2 - 0.66 * r, y + 0.66 * r);
|
||||
ctx.lineTo(x + w / 2 - r, y + r);
|
||||
ctx.moveTo(x + w / 2 + 0.66 * r, y + 0.66 * r);
|
||||
ctx.lineTo(x + w / 2 + r, y + r);
|
||||
ctx.moveTo(x + w - 0.66 * r, y + h / 2 - 0.66 * r);
|
||||
ctx.lineTo(x + w - r, y + h / 2 - r);
|
||||
ctx.moveTo(x + w - 0.66 * r, y + h / 2 + 0.66 * r);
|
||||
ctx.lineTo(x + w - r, y + h / 2 + r);
|
||||
ctx.moveTo(x + w / 2 + 0.66 * r, y + h - 0.66 * r);
|
||||
ctx.lineTo(x + w / 2 + r, y + h - r);
|
||||
ctx.moveTo(x + w / 2 - 0.66 * r, y + h - 0.66 * r);
|
||||
ctx.lineTo(x + w / 2 - r, y + h - r);
|
||||
ctx.moveTo(x + 0.66 * r, y + h / 2 + 0.66 * r);
|
||||
ctx.lineTo(x + r, y + h / 2 + r);
|
||||
}
|
||||
ctx.stroke();
|
||||
break;
|
||||
case MapPointType.电梯点:
|
||||
case MapPointType.自动门点:
|
||||
case MapPointType.充电点:
|
||||
case MapPointType.停靠点:
|
||||
case MapPointType.动作点:
|
||||
ctx.rect(x, y, width, height);
|
||||
// ctx.fillStyle = get(theme, 'point-l.fill') ?? '';
|
||||
// ctx.fill();
|
||||
ctx.roundRect(x, y, w, h, r);
|
||||
ctx.strokeStyle = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke') ?? '';
|
||||
ctx.stroke();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ctx.fillStyle = get(theme, 'color') ?? '';
|
||||
ctx.font = '14px/20px system-ui';
|
||||
ctx.font = `${fontSize}px/${lineHeight} ${fontFamily}`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(label, x + width / 2, y - 10);
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(label, x + w / 2, y - fontSize * lineHeight);
|
||||
ctx.restore();
|
||||
}
|
||||
function anchorPoint(pen: MapPen): void {
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||
|
||||
export const ROUTES = Object.freeze<RouteRecordRaw[]>([
|
||||
{ path: '/:pathMatch(.*)*', redirect: '/' },
|
||||
{ path: '/:pathMatch(.*)*', redirect: '/exception/404' },
|
||||
{
|
||||
path: '/exception/:code',
|
||||
props: true,
|
||||
component: () => import('@/exception.vue'),
|
||||
},
|
||||
{
|
||||
path: '/scene-editor/:id',
|
||||
props: true,
|
||||
|
@ -29,7 +29,7 @@ class ThemeService {
|
||||
public get ant(): AntdTheme {
|
||||
switch (this.#theme.value) {
|
||||
case Theme.Dark:
|
||||
return { algorithm: theme.darkAlgorithm };
|
||||
return THEME_MAP[`antd-${this.#theme.value}`] ?? {};
|
||||
case Theme.Light:
|
||||
default:
|
||||
return { algorithm: theme.defaultAlgorithm };
|
||||
|