feat(map-toolbar): 新增连续测距功能,支持多段测量并显示距离
This commit is contained in:
parent
d5e226c3cf
commit
c4a098d137
@ -76,6 +76,8 @@ const toggleMeasure = () => {
|
||||
start.value = null;
|
||||
end.value = null;
|
||||
current.value = null;
|
||||
// 清理连续测距状态
|
||||
try { mPts.value = []; mCur.value = null; } catch {}
|
||||
}
|
||||
};
|
||||
|
||||
@ -133,6 +135,77 @@ onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', onKeydown);
|
||||
});
|
||||
|
||||
// =============== 连续测距(以米显示)增强 ===============
|
||||
// 独立的测距状态,连续点击可累加多段,双击结束
|
||||
const mPts = ref<Array<{ x: number; y: number }>>([]);
|
||||
const mCur = ref<{ x: number; y: number } | null>(null);
|
||||
|
||||
const getScaleInfo = () => {
|
||||
const editor = editorRef.value;
|
||||
const canvasScale = editor?.store?.data?.scale ?? 1;
|
||||
let mapScale = 1;
|
||||
try {
|
||||
const sceneJson = editor?.save?.();
|
||||
if (sceneJson) {
|
||||
const sceneData = JSON.parse(sceneJson as string);
|
||||
if (typeof sceneData?.scale === 'number' && sceneData.scale > 0) {
|
||||
mapScale = sceneData.scale;
|
||||
}
|
||||
// 如果场景scale与画布缩放一致且存在ratio,则以ratio作为地图自身缩放的回退
|
||||
if (
|
||||
typeof (sceneData as any)?.scale === 'number' &&
|
||||
Math.abs((sceneData as any).scale - (canvasScale || 1)) < 1e-6 &&
|
||||
typeof (sceneData as any)?.ratio === 'number' && (sceneData as any).ratio > 0
|
||||
) {
|
||||
mapScale = (sceneData as any).ratio;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
return { canvasScale, mapScale };
|
||||
};
|
||||
|
||||
const totalPx2 = computed(() => {
|
||||
// 仅在点击时(确认的点列)计算,不随鼠标移动而变化
|
||||
if (!measuring.value) return 0;
|
||||
let t = 0;
|
||||
for (let i = 1; i < mPts.value.length; i++) {
|
||||
const dx = mPts.value[i].x - mPts.value[i - 1].x;
|
||||
const dy = mPts.value[i].y - mPts.value[i - 1].y;
|
||||
t += Math.hypot(dx, dy);
|
||||
}
|
||||
return t;
|
||||
});
|
||||
|
||||
const totalMeters2 = computed(() => {
|
||||
const { canvasScale, mapScale } = getScaleInfo();
|
||||
const denom = (canvasScale || 1) * (mapScale || 1);
|
||||
if (denom <= 0) return 0;
|
||||
return totalPx2.value / denom;
|
||||
});
|
||||
|
||||
const distanceLabel2 = computed(() => {
|
||||
const d = totalMeters2.value;
|
||||
if (d >= 1000) return (d / 1000).toFixed(2) + ' km';
|
||||
if (d >= 1) return d.toFixed(2) + ' m';
|
||||
return (d * 100).toFixed(1) + ' cm';
|
||||
});
|
||||
|
||||
const lastAnchor2 = computed(() => {
|
||||
// 标签位置固定在最后一个确认点,避免鼠标移动触发变更
|
||||
if (mPts.value.length) return mPts.value[mPts.value.length - 1];
|
||||
return { x: 0, y: 0 };
|
||||
});
|
||||
|
||||
const onMeasureDown = (e: MouseEvent) => {
|
||||
if (!measuring.value) return;
|
||||
mPts.value = [...mPts.value, { x: e.clientX, y: e.clientY }];
|
||||
};
|
||||
const onMeasureDblClick = () => {
|
||||
measuring.value = false;
|
||||
mPts.value = [];
|
||||
mCur.value = null;
|
||||
};
|
||||
|
||||
// 定义组件名称
|
||||
defineOptions({
|
||||
name: 'MapToolbar',
|
||||
@ -199,8 +272,8 @@ defineOptions({
|
||||
<div
|
||||
class="measure-overlay"
|
||||
:class="{ active: measuring }"
|
||||
@mousedown="onMouseDownOverlay"
|
||||
@mousemove="onMouseMoveOverlay"
|
||||
@mousedown="onMeasureDown"
|
||||
@dblclick="onMeasureDblClick"
|
||||
>
|
||||
<svg class="measure-svg" xmlns="http://www.w3.org/2000/svg">
|
||||
<g v-if="measuring && start && (end || current)">
|
||||
@ -230,6 +303,31 @@ defineOptions({
|
||||
<g v-else-if="measuring">
|
||||
<text x="20" y="40" fill="#fff" font-size="13">提示:依次点击两点进行测量,按 Esc 退出</text>
|
||||
</g>
|
||||
<!-- 连续测距附加绘制(以米):与上方原像素测距并存,仅在 measuring=true 时显示 -->
|
||||
<g v-if="measuring">
|
||||
<defs>
|
||||
<marker id="arrow" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L6,3 z" fill="#1e90ff" />
|
||||
</marker>
|
||||
</defs>
|
||||
<template v-if="mPts.length > 1">
|
||||
<template v-for="(pt, idx) in mPts" :key="'seg2-'+idx">
|
||||
<line v-if="idx > 0"
|
||||
:x1="mPts[idx-1].x" :y1="mPts[idx-1].y"
|
||||
:x2="pt.x" :y2="pt.y"
|
||||
stroke="#1e90ff" stroke-width="2" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-for="(pt, idx) in mPts" :key="'dot2-'+idx">
|
||||
<circle :cx="pt.x" :cy="pt.y" r="4" fill="#1e90ff" />
|
||||
</template>
|
||||
|
||||
<g :transform="`translate(${lastAnchor2.x}, ${lastAnchor2.y - 16})`">
|
||||
<rect x="-60" y="-18" width="120" height="20" rx="4" ry="4" fill="rgba(0,0,0,0.6)" />
|
||||
<text x="0" y="-4" fill="#fff" font-size="12" text-anchor="middle">{{ distanceLabel2 }}</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
Loading…
x
Reference in New Issue
Block a user