feat(map-toolbar): 新增连续测距功能,支持多段测量并显示距离
This commit is contained in:
parent
d5e226c3cf
commit
c4a098d137
@ -76,6 +76,8 @@ const toggleMeasure = () => {
|
|||||||
start.value = null;
|
start.value = null;
|
||||||
end.value = null;
|
end.value = null;
|
||||||
current.value = null;
|
current.value = null;
|
||||||
|
// 清理连续测距状态
|
||||||
|
try { mPts.value = []; mCur.value = null; } catch {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,6 +135,77 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('keydown', onKeydown);
|
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({
|
defineOptions({
|
||||||
name: 'MapToolbar',
|
name: 'MapToolbar',
|
||||||
@ -199,8 +272,8 @@ defineOptions({
|
|||||||
<div
|
<div
|
||||||
class="measure-overlay"
|
class="measure-overlay"
|
||||||
:class="{ active: measuring }"
|
:class="{ active: measuring }"
|
||||||
@mousedown="onMouseDownOverlay"
|
@mousedown="onMeasureDown"
|
||||||
@mousemove="onMouseMoveOverlay"
|
@dblclick="onMeasureDblClick"
|
||||||
>
|
>
|
||||||
<svg class="measure-svg" xmlns="http://www.w3.org/2000/svg">
|
<svg class="measure-svg" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g v-if="measuring && start && (end || current)">
|
<g v-if="measuring && start && (end || current)">
|
||||||
@ -230,6 +303,31 @@ defineOptions({
|
|||||||
<g v-else-if="measuring">
|
<g v-else-if="measuring">
|
||||||
<text x="20" y="40" fill="#fff" font-size="13">提示:依次点击两点进行测量,按 Esc 退出</text>
|
<text x="20" y="40" fill="#fff" font-size="13">提示:依次点击两点进行测量,按 Esc 退出</text>
|
||||||
</g>
|
</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>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user