地图路线拖动绘制
This commit is contained in:
parent
64d93c27df
commit
59a7eea5fe
@ -2,7 +2,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :style="wrapperStyle" class="vue-ruler-wrapper" onselectstart="return false;" ref="el">
|
<div :style="wrapperStyle" class="vue-ruler-wrapper" onselectstart="return false;" ref="el">
|
||||||
<section>
|
<section>
|
||||||
<div ref="horizontalRuler" class="vue-ruler-h" @mousedown.stop="horizontalDragRuler">
|
<div
|
||||||
|
ref="horizontalRuler"
|
||||||
|
class="vue-ruler-h"
|
||||||
|
@mousedown.stop="horizontalDragRuler"
|
||||||
|
:style="{ width: width + 'px' }"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-for="(item, index) in xScale"
|
v-for="(item, index) in xScale"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -11,7 +16,12 @@
|
|||||||
>{{ item.id }}</span
|
>{{ item.id }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div ref="verticalRuler" class="vue-ruler-v" @mousedown.stop="verticalDragRuler">
|
<div
|
||||||
|
ref="verticalRuler"
|
||||||
|
class="vue-ruler-v"
|
||||||
|
@mousedown.stop="verticalDragRuler"
|
||||||
|
:style="{ height: height + 'px' }"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-for="(item, index) in yScale"
|
v-for="(item, index) in yScale"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -82,6 +92,12 @@ const props = defineProps({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 100,
|
default: 100,
|
||||||
validator: (val) => val % 10 === 0
|
validator: (val) => val % 10 === 0
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -166,12 +182,10 @@ onBeforeUnmount(() => {
|
|||||||
off(document, 'mouseup', dottedLineUp)
|
off(document, 'mouseup', dottedLineUp)
|
||||||
off(window, 'resize', windowResize)
|
off(window, 'resize', windowResize)
|
||||||
})
|
})
|
||||||
//function
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
box()
|
box()
|
||||||
scaleCalc()
|
scaleCalc()
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowResize = () => {
|
const windowResize = () => {
|
||||||
xScale.value = [{ id: 0 }]
|
xScale.value = [{ id: 0 }]
|
||||||
yScale.value = [{ id: 0 }]
|
yScale.value = [{ id: 0 }]
|
||||||
@ -193,8 +207,10 @@ const box = () => {
|
|||||||
getCalcRevise(xScale.value, contentLeft)
|
getCalcRevise(xScale.value, contentLeft)
|
||||||
getCalcRevise(yScale.value, contentTop)
|
getCalcRevise(yScale.value, contentTop)
|
||||||
}
|
}
|
||||||
windowWidth.value = document.documentElement.clientWidth - leftSpacing
|
// windowWidth.value = document.documentElement.clientWidth - leftSpacing
|
||||||
windowHeight.value = document.documentElement.clientHeight - topSpacing - 80
|
// windowHeight.value = document.documentElement.clientHeight - topSpacing - 80
|
||||||
|
windowWidth.value = props.width + 18
|
||||||
|
windowHeight.value = props.height + 18
|
||||||
rulerWidth = verticalRuler.value.clientWidth
|
rulerWidth = verticalRuler.value.clientWidth
|
||||||
rulerHeight = horizontalRuler.value.clientHeight
|
rulerHeight = horizontalRuler.value.clientHeight
|
||||||
}
|
}
|
||||||
@ -204,8 +220,8 @@ const setSpacing = () => {
|
|||||||
}
|
}
|
||||||
// 计算刻度
|
// 计算刻度
|
||||||
const scaleCalc = () => {
|
const scaleCalc = () => {
|
||||||
getCalc(xScale.value, windowWidth.value)
|
getCalc(xScale.value, props.width)
|
||||||
getCalc(yScale.value, windowHeight.value)
|
getCalc(yScale.value, props.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取刻度
|
//获取刻度
|
||||||
@ -360,7 +376,7 @@ const dragVerticalLine = (id) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vue-ruler-h {
|
.vue-ruler-h {
|
||||||
width: calc(100% - 18px);
|
// width: calc(100% - 18px);
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
left: 18px;
|
left: 18px;
|
||||||
@ -371,7 +387,7 @@ const dragVerticalLine = (id) => {
|
|||||||
|
|
||||||
.vue-ruler-v {
|
.vue-ruler-v {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 85vh;
|
// height: 85vh;
|
||||||
top: 18px;
|
top: 18px;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAyCAMAAABmvHtTAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRFMzMzAAAA////BqjYlAAAACBJREFUeNpiYGBEBwwMTGiAakI0NX7U9aOuHyGuBwgwAH6bBkAR6jkzAAAAAElFTkSuQmCC)
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAyCAMAAABmvHtTAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRFMzMzAAAA////BqjYlAAAACBJREFUeNpiYGBEBwwMTGiAakI0NX7U9aOuHyGuBwgwAH6bBkAR6jkzAAAAAElFTkSuQmCC)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,127 +1,190 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="box">
|
<div>
|
||||||
<svg :width="boxWidth" :height="boxHeight">
|
<svg
|
||||||
<path
|
ref="svgElement"
|
||||||
v-for="(curve, index) in curves"
|
:width="width"
|
||||||
|
:height="height"
|
||||||
|
@mousedown="startDrawing"
|
||||||
|
@mousemove="drawLine"
|
||||||
|
@mouseup="stopDrawing"
|
||||||
|
@mouseleave="stopDrawing"
|
||||||
|
>
|
||||||
|
<!-- 渲染所有点 -->
|
||||||
|
<circle
|
||||||
|
v-for="(point, index) in points"
|
||||||
:key="index"
|
:key="index"
|
||||||
:d="getCurvePath(curve)"
|
:cx="point.x"
|
||||||
:stroke="curve.isSelected ? 'red' : 'blue'"
|
:cy="point.y"
|
||||||
|
r="5"
|
||||||
|
fill="red"
|
||||||
|
@mousedown="startFromPoint(index, $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 渲染所有直线 -->
|
||||||
|
<path
|
||||||
|
v-for="(line, index) in lines"
|
||||||
|
:key="'line-' + index"
|
||||||
|
:d="`M ${line.startPointX} ${line.startPointY} L ${line.endPointX} ${line.endPointY}`"
|
||||||
|
stroke="black"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
fill="none"
|
fill="none"
|
||||||
@mousedown="selectCurve(index)"
|
|
||||||
/>
|
/>
|
||||||
<circle
|
|
||||||
v-for="(curve, index) in curves"
|
<!-- 实时绘制当前直线 -->
|
||||||
:key="'start-' + index"
|
<path
|
||||||
:cx="curve.startControlX"
|
v-if="isDrawing"
|
||||||
:cy="curve.startControlY"
|
:d="`M ${startX} ${startY} L ${currentX} ${currentY}`"
|
||||||
r="5"
|
stroke="blue"
|
||||||
fill="green"
|
stroke-width="2"
|
||||||
@mousedown="startDrag(index, 'start')"
|
fill="none"
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
v-for="(curve, index) in curves"
|
|
||||||
:key="'end-' + index"
|
|
||||||
:cx="curve.endControlX"
|
|
||||||
:cy="curve.endControlY"
|
|
||||||
r="5"
|
|
||||||
fill="green"
|
|
||||||
@mousedown="startDrag(index, 'end')"
|
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<!-- 显示直线数据 -->
|
||||||
|
<div>
|
||||||
|
<h3>直线数据:</h3>
|
||||||
|
<pre>{{ lines }}</pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
// 盒子的宽度和高度
|
export default {
|
||||||
const boxWidth = ref(800)
|
setup() {
|
||||||
const boxHeight = ref(600)
|
const svgElement = ref(null) // SVG 元素的引用
|
||||||
|
const width = 800 // SVG 宽度
|
||||||
|
const height = 600 // SVG 高度
|
||||||
|
|
||||||
// 初始点位数据
|
// 点位数据
|
||||||
const curves = ref([
|
const points = ref([
|
||||||
{
|
{ x: 10, y: 10 },
|
||||||
startX: 100,
|
{ x: 30, y: 100 },
|
||||||
startY: 100,
|
{ x: 40, y: 40 },
|
||||||
endX: 300,
|
{ x: 230, y: 400 },
|
||||||
endY: 100,
|
{ x: 750, y: 640 }
|
||||||
startControlX: 100,
|
])
|
||||||
startControlY: 100,
|
|
||||||
endControlX: 300,
|
|
||||||
endControlY: 100,
|
|
||||||
isSelected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
startX: 200,
|
|
||||||
startY: 200,
|
|
||||||
endX: 400,
|
|
||||||
endY: 200,
|
|
||||||
startControlX: 200,
|
|
||||||
startControlY: 200,
|
|
||||||
endControlX: 400,
|
|
||||||
endControlY: 400,
|
|
||||||
isSelected: false
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// 当前拖动的曲线索引和控制点类型
|
// 直线数据
|
||||||
const dragging = ref({
|
const lines = ref([])
|
||||||
index: null,
|
|
||||||
type: null
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取曲线的路径字符串
|
// 当前绘制的直线状态
|
||||||
const getCurvePath = (curve) => {
|
const isDrawing = ref(false)
|
||||||
return `M${curve.startX},${curve.startY} C${curve.startControlX},${curve.startControlY} ${curve.endControlX},${curve.endControlY} ${curve.endX},${curve.endY}`
|
const startX = ref(0)
|
||||||
}
|
const startY = ref(0)
|
||||||
|
const currentX = ref(0)
|
||||||
|
const currentY = ref(0)
|
||||||
|
const startPointIndex = ref(null) // 起始点的索引
|
||||||
|
|
||||||
// 选中曲线
|
// 从点开始绘制
|
||||||
const selectCurve = (index) => {
|
const startFromPoint = (index, event) => {
|
||||||
curves.value.forEach((curve, i) => {
|
const point = points.value[index]
|
||||||
curve.isSelected = i === index
|
startX.value = point.x
|
||||||
})
|
startY.value = point.y
|
||||||
console.log('Selected curve:', curves.value[index])
|
startPointIndex.value = index
|
||||||
}
|
isDrawing.value = true
|
||||||
|
event.preventDefault() // 防止默认行为
|
||||||
|
}
|
||||||
|
|
||||||
// 开始拖动控制点
|
// 实时绘制
|
||||||
const startDrag = (index, type) => {
|
const drawLine = (event) => {
|
||||||
dragging.value = { index, type }
|
if (isDrawing.value) {
|
||||||
window.addEventListener('mousemove', handleDrag)
|
const rect = svgElement.value.getBoundingClientRect()
|
||||||
window.addEventListener('mouseup', endDrag)
|
currentX.value = event.clientX - rect.left
|
||||||
}
|
currentY.value = event.clientY - rect.top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理拖动事件
|
// 结束绘制
|
||||||
const handleDrag = (event) => {
|
const stopDrawing = () => {
|
||||||
if (dragging.value.index !== null) {
|
if (isDrawing.value) {
|
||||||
const curve = curves.value[dragging.value.index]
|
// 找到最近的终点
|
||||||
let newX = event.offsetX
|
const endPointIndex = findClosestPoint(currentX.value, currentY.value)
|
||||||
let newY = event.offsetY
|
|
||||||
|
|
||||||
// 确保控制点不超出盒子范围
|
if (endPointIndex !== null && endPointIndex !== startPointIndex.value) {
|
||||||
newX = Math.max(0, Math.min(newX, boxWidth.value))
|
const endPoint = points.value[endPointIndex]
|
||||||
newY = Math.max(0, Math.min(newY, boxHeight.value))
|
const newLine = {
|
||||||
|
startPointX: startX.value,
|
||||||
|
startPointY: startY.value,
|
||||||
|
endPointX: endPoint.x,
|
||||||
|
endPointY: endPoint.y
|
||||||
|
}
|
||||||
|
|
||||||
if (dragging.value.type === 'start') {
|
// 检查是否已存在相同的直线
|
||||||
curve.startControlX = newX
|
const isDuplicate = lines.value.some(
|
||||||
curve.startControlY = newY
|
(line) =>
|
||||||
} else {
|
(line.startPointX === newLine.startPointX &&
|
||||||
curve.endControlX = newX
|
line.startPointY === newLine.startPointY &&
|
||||||
curve.endControlY = newY
|
line.endPointX === newLine.endPointX &&
|
||||||
|
line.endPointY === newLine.endPointY) ||
|
||||||
|
(line.startPointX === newLine.endPointX &&
|
||||||
|
line.startPointY === newLine.endPointY &&
|
||||||
|
line.endPointX === newLine.startPointX &&
|
||||||
|
line.endPointY === newLine.startPointY)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!isDuplicate) {
|
||||||
|
// 保存当前直线
|
||||||
|
lines.value.push(newLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
isDrawing.value = false
|
||||||
|
startX.value = 0
|
||||||
|
startY.value = 0
|
||||||
|
currentX.value = 0
|
||||||
|
currentY.value = 0
|
||||||
|
startPointIndex.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到最近的点
|
||||||
|
const findClosestPoint = (x, y) => {
|
||||||
|
let minDistance = Infinity
|
||||||
|
let closestIndex = null
|
||||||
|
|
||||||
|
points.value.forEach((point, index) => {
|
||||||
|
const distance = Math.sqrt((point.x - x) ** 2 + (point.y - y) ** 2)
|
||||||
|
if (distance < minDistance && distance < 10) {
|
||||||
|
// 10 是点的捕捉范围
|
||||||
|
minDistance = distance
|
||||||
|
closestIndex = index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return closestIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
svgElement,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
points,
|
||||||
|
lines,
|
||||||
|
isDrawing,
|
||||||
|
startX,
|
||||||
|
startY,
|
||||||
|
currentX,
|
||||||
|
currentY,
|
||||||
|
startFromPoint,
|
||||||
|
drawLine,
|
||||||
|
stopDrawing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 结束拖动
|
|
||||||
const endDrag = () => {
|
|
||||||
dragging.value = { index: null, type: null }
|
|
||||||
window.removeEventListener('mousemove', handleDrag)
|
|
||||||
window.removeEventListener('mouseup', endDrag)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.box {
|
svg {
|
||||||
border: 1px solid black;
|
border: 1px solid #ccc;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user