前言
两点之间的连线是很常见的,但是都是直直的一条线段,为了使连线更加平滑,我们可以使用曲线进行连线,本功能考虑使用贝塞尔曲线进行连线绘制,最后将线段的两端节点连接,返回一个polygon。
贝塞尔简介
给定不同的点 P0 和 P1,线性贝塞尔曲线只是这两点之间的一条线。
相当于线性插值。
Turf.bezierSpline()简介
接受一条线,通过应用贝塞尔样条算法返回一个弯曲的版本
官方例子
var line = turf.lineString([
[-76.091308, 18.427501],
[-76.695556, 18.729501],
[-76.552734, 19.40443],
[-74.61914, 19.134789],
[-73.652343, 20.07657],
[-73.157958, 20.210656]
]);
var curved = turf.bezierSpline(line);
参数说明
line:input LineString
options:{
resolution:点之间的时间(毫秒)
sharpness:衡量样条路径应该有多弯曲的一个度量
}
具体可以查看官方的bezierSpline函数
UseBezierSpline.js完整代码
import { onBeforeUnmount, reactive, ref } from 'vue'
import * as turf from '@turf/turf'
/**
* @Description 接受一条线,通过应用贝塞尔样条算法返回一个弯曲的版本(贝塞尔曲线)
* @param mainMap 地图对象
* @param drawComplete 完成绘制的回调函数
* @Param bezierOptions
* @Param bezierOptions.resolution 点之间的时间(毫秒)
* @Param bezierOptions.sharpness 衡量样条路径应该有多弯曲的一个度量
* @param drawLayer 绘制图形的layer
* @Author ZhangJun
* @Date 2024-01-11 09:55:12
* @return void
**/
export default function useBezierSpline(mainMap, drawComplete = null, bezierOptions = {}, drawLayer = undefined) {
//默认的贝塞尔配置参数合并
let bezierOptions_config = reactive({
resolution: 1000,
sharpness: 0.85,
...bezierOptions,
})
//绘制事件状态
let status = ref('start')
// 记录当前状态是否可以拖动绘制
let isDragging = true
//拖动绘制的坐标
let drawPath = []
//拖动绘制的线
let drawLine = reactive(null)
//鼠标正在移动的点
let movePoint
//最终绘制的polygon,最终返回的就是它的轮廓坐标
let polygon_draw = reactive(null)
//鼠标提示
let mouseEventPopup = new L.popup({ className: 'customPopup', closeButton: false })
if (mainMap) {
//关闭的时候一定要销毁
onBeforeUnmount(() => {
closeDraw()
mainMap?.removeLayer(drawLayer)
})
if (!drawLayer) {
drawLayer = L.featureGroup([])
drawLayer.addTo(mainMap)
}
//初始化事件
let initEvents = () => {
isDragging = false
//按下鼠标确定需要添加的节点(暂停中)
mainMap.on('mousedown', (e) => {
isDragging = false
let { lat, lng } = e.latlng
drawPath = [...drawPath, [lng, lat]]
//如果才开始点击第一次,就创建一个polyline,后面需要动态需改它的path
if (drawPath?.length === 1) {
status.value = 'start'
//添加绘制line
drawLine = L.polyline(drawPath, { color: 'red' }).addTo(mainMap)
}
})
//松开鼠标开始拖动绘制(开始绘制)
mainMap.on('mouseup', (e) => {
isDragging = true
})
//鼠标移动绘制(绘制中)
mainMap.on('mousemove', (e) => {
if (isDragging) {
let { lat, lng } = e.latlng
movePoint = [lng, lat]
//动态生成贝塞尔曲线的feature
let splineFeature = generationBezierSpline(drawPath, movePoint)
if (splineFeature) {
let tempCoords = turf.getCoords(turf.flip(splineFeature))
//将生成的贝塞尔曲线的坐标传给polyline,在地图上刷新渲染
drawLine?.setLatLngs(tempCoords)
}
}
mouseEventPopup?.setLatLng(e.latlng)?.setContent('右键结束绘制')
//如果还没有添加就直接先添加一下
if (!mainMap.hasLayer(mouseEventPopup)) {
//打开方向的popup
mouseEventPopup?.openOn(mainMap)
}
})
//右键结束(结束绘制)
mainMap.on('contextmenu', (e) => {
let coords = turf.getCoords(turf.flip(drawLine.toGeoJSON()))
//生成polygon
polygon_draw = L.polygon(coords, { color: 'green' })
clearDrawLayer()
addLayersToDrawLayer([polygon_draw])
//移除曲线
mainMap?.removeLayer(drawLine)
status.value = 'end'
//绘制完成的回调
if (typeof drawComplete === 'function') {
let result = getResult(polygon_draw)
drawComplete(result)
}
})
}
//移除事件
let removeEvents = () => {
//按下鼠标
mainMap?.off('mousedown')
//抬起鼠标
mainMap?.off('mouseup')
//拖拽事件
mainMap?.off('mousemove')
//右键事件
mainMap?.off('contextmenu')
}
//开始绘制
let startDraw = () => {
//禁止拖动地图
mainMap?.dragging?.disable()
//初始化事件
initEvents()
}
//清除原来绘制的内容
let clearDrawLayer = () => {
drawPath = []
drawLayer?.clearLayers()
}
//添加要素到drawLayer
let addLayersToDrawLayer = (features = []) => {
features?.forEach(feature => {
drawLayer.addLayer(feature)
})
}
/**
* @Description 生成曲线
* @Param originalPath 已经确定的点坐标集合
* @Param lastPoint 最后一个坐标点,一般为移动的点坐标
* @Author ZhangJun
* @Date 2024-01-11 10:36:11
* @return void
**/
let generationBezierSpline = (originalPath = drawPath, lastPoint = movePoint) => {
if (originalPath?.length > 0) {
//加入最后一个点
let line = turf.lineString([...originalPath, lastPoint], bezierOptions_config)
return turf.bezierSpline(line)
}
return null
}
//关闭绘制功能
let closeDraw = () => {
//清空绘制的几何
clearDrawLayer()
//一定要移除事件,否则事件之间会有干扰
removeEvents()
//移除popup
mainMap?.closePopup(mouseEventPopup)
//激活拖拽功能
mainMap?.dragging?.enable()
}
//获取最终的polygon的轮廓坐标
let getResult = (feature = polygon_draw) => {
if (feature) {
//获取输入 feature 并将它们的所有坐标从 [x, y] 翻转为 [y, x]。
let featureCollection = turf.flip(feature.toGeoJSON())
return turf.getCoords(featureCollection)
}
return []
}
return { status, getResult, closeDraw, drawLayer, bezierOptions_config, startDraw }
}
return {}
}
UseBezierSpline.js使用
if (wizMap?.map) {
let { startDraw, closeDraw, status: temp, getResult } = useBezierSpline(wizMap?.map)
getCoords = getResult
//当前绘制状态(是否完成绘制)
status.value = temp
onMounted(() => {
//需要这种处理,不然会有异常
nextTick(() => {
startDraw()
})
})
}
效果
贝塞尔曲线
本文为学习笔记,仅供参考