无图不欢,先上图
使用方法(以vue3为例)
<template><canvas class="lane" ref="laneCanvas"></canvas>
</template><script setup>
import { ref, onMounted } from 'vue'
import Lane from '@/utils/lane.js'let laneCanvas = ref(null)
/*** 车道方向,进口方向* 1 - 北,2 - 东北,3 - 东,4 - 东南,* 5 - 南,6 - 西南,7 - 西,8 - 西北* * 直行放行 nThrough 0不放行 1放行* 左转放行 nTurnLeft 0不放行 1放行* 右转放行 nTurnRight 0不放行 1放行* 调头 nTurnAround 0不放行 1放行* * 通道相位 nChannelNumberPhase 1-红灯 2绿灯 3黄灯*/
const randData = () => {let data = []let cdireCtions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北']cdireCtions.forEach((item, index) => {if (rand(0, 1)) {let lanes = []let lanesLength = rand(1, 6)if (lanesLength === 1) {lanes = [{nThrough: 1,nTurnLeft: rand(0, 1),nTurnRight: rand(0, 1),nTurnAround: rand(0, 1),nChannelNumberPhase: rand(1, 3)}]} else if (lanesLength === 2) {lanes = [{nThrough: 1,nTurnLeft: rand(0, 1),nTurnRight: 0,nTurnAround: rand(0, 1),nChannelNumberPhase: rand(1, 3)}, {nThrough: 1,nTurnLeft: 0,nTurnRight: rand(0, 1),nTurnAround: 0,nChannelNumberPhase: rand(1, 3)}]} else {for (let i = 0; i < lanesLength; i++) {let nThrough = 0let nTurnLeft = 0let nTurnRight = 0let nTurnAround = 0if (i === 0) {nThrough = rand(0, 1)nTurnLeft = 1nTurnAround = rand(0, 1)}if (i === lanesLength - 1) {nThrough = rand(0, 1)nTurnRight = 1nTurnAround = 0}if (i > 0 && i < lanesLength - 1) {nThrough = 1if (lanes[i - 1].nTurnLeft) {nTurnLeft = rand(0, 1)nTurnRight = 0} else if (lanes[i - 1].nTurnRight) {nTurnLeft = 0nTurnRight = 1} else {nTurnLeft = 0nTurnRight = rand(0, 1)}nTurnAround = 0}lanes.push({nThrough,nTurnLeft,nTurnRight,nTurnAround,nChannelNumberPhase: rand(1, 3)})}}data.push({nApproachDirection: index + 1,cdireCtion: cdireCtions[index],lanes})}})if (data.length < 2) {data = randData()}return data
}
const dataTest = [{nApproachDirection: 1,cdireCtion: '北',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 3}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 2}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 2,cdireCtion: '东北',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 3,cdireCtion: '东',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 4,cdireCtion: '东南',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 5,cdireCtion: '南',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 6,cdireCtion: '西南',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 7,cdireCtion: '西',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 8,cdireCtion: '西北',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}]
const dataTest2 = [{nApproachDirection: 1,cdireCtion: '东',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}, {nApproachDirection: 2,cdireCtion: '南',lanes: [{nThrough: 0,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 1,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 1,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 1,nTurnLeft: 0,nTurnRight: 0,nTurnAround: 0,nChannelNumberPhase: 1}, {nThrough: 0,nTurnLeft: 0,nTurnRight: 1,nTurnAround: 0,nChannelNumberPhase: 1}]
}]
const rand = (n, m) => {var c = m - n + 1return Math.floor(Math.random() * c + n)
}
onMounted(() => {let data = randData()console.log(data)let laneC = new Lane({canvas: laneCanvas.value,data: [...data]})setInterval(() => {data.forEach(dataItem => {dataItem.lanes.forEach(lane => {lane.nChannelNumberPhase = rand(1, 3)})})laneC.setData(data)}, 5000)
})</script><style scoped lang="scss">
.lane {width: 100%;height: 100%;background-color: #325e76;
}
</style>
lane.js源码
class Lane {constructor(opt) {this.dpr = window.devicePixelRatio || 1this.canvas = opt.canvasthis.w = nullthis.h = nullthis.ctx = nullthis.data = opt.data// 车道范围坐标this.region = []// 车道线坐标this.dataXY = []// 路中心空白区域占canvas宽高最小值的比,用来计算车道宽度。占比越大,中心空白区域越大,车道越宽,线路越短。取值范围0-1,不允许取0,1this.laneCenterProportion = 0.6 || opt.laneCenterProportion// 车道样式this.laneStyle = opt.laneStylethis.init()}init() {if (!this.canvas) {return}if (this.canvas.width !== Math.floor(this.canvas.offsetWidth * this.dpr) || this.canvas.height !== Math.floor(this.canvas.offsetHeight * this.dpr)) {this.w = this.canvas.width = Math.floor(this.canvas.offsetWidth * this.dpr)this.h = this.canvas.height = Math.floor(this.canvas.offsetHeight * this.dpr)}this.ctx = this.canvas.getContext('2d')this.getLaneStyle()this.formatDataXY()this.getRegion()this.draw()}// 获取车道样式getLaneStyle() {let laneStyle = {// 车道范围region: {width: 2 * this.dpr,color: '#fff',type: 'solid',CurveType: 'quadratic', // normal: 插值曲线, quadratic: 二次贝塞尔, arc: 圆弧线。arc有问题,请勿使用background: '#1f2748'},// 车道左侧车道线innerLeft: {width: 1 * this.dpr,color: '#999',type: [10 * this.dpr, 10 * this.dpr],},// 车道右侧车道线innerRight: {width: 1 * this.dpr,color: '#eee',type: [10 * this.dpr, 10 * this.dpr],},// 车道分割线innerDivider: {width: 2 * this.dpr,color: '#f0bf0a',type: 'solid'},// 车道标识direction: {widthProportion: 0.1, // 占车道比例,建议小于0.2HeightWidthProportion: 10, // 高宽比,建议大于5maxWidth: 20 * this.dpr,arrowWidth: 2, // 箭头/方向线的比例, 建议大于1小于2background: '#ddd'},// 斑马线zebraCrossing: {widthProportion: 0.05, // 单个斑马线宽占车道比例,建议小于0.2widthHeightProportion: 0.2, // 单个斑马线宽高比,建议小于0.5color: '#ddd'},// 红绿灯trafficLight: {rProportion: 0.3, // 单个红绿灯半径占车道比例,建议小于0.5,colors: ['#FF0033', '#33CC00', '#FFFF33'],}}if (this.laneStyle) {this.laneStyle = Object.assign(laneStyle, this.laneStyle)} else {this.laneStyle = laneStyle}let laneMaxNum = this.getLaneMaxNum()let sideLength = this.getSideLength()// 车道宽度 / 2 表示双向this.laneStyle.width = sideLength / 2 / laneMaxNum// 方向表示线宽高this.laneStyle.direction.width = this.laneStyle.width * this.laneStyle.direction.widthProportionif (this.laneStyle.direction.width > this.laneStyle.direction.maxWidth) {this.laneStyle.direction.width = this.laneStyle.direction.maxWidth}this.laneStyle.direction.height = this.laneStyle.direction.width * this.laneStyle.direction.HeightWidthProportion// 斑马线宽高this.laneStyle.zebraCrossing.width = this.laneStyle.width * this.laneStyle.zebraCrossing.widthProportionthis.laneStyle.zebraCrossing.height = this.laneStyle.zebraCrossing.width / this.laneStyle.zebraCrossing.widthHeightProportionthis.laneStyle.zebraCrossing.type = [this.laneStyle.zebraCrossing.width, this.laneStyle.zebraCrossing.width * 4]// 红绿灯半径this.laneStyle.trafficLight.r = this.laneStyle.width * this.laneStyle.trafficLight.rProportion}// 获取最大车道数getLaneMaxNum() {let laneMaxNum = 0this.data.forEach(item => {if (item.lanes.length > laneMaxNum) {laneMaxNum = item.lanes.length}})if(laneMaxNum === 1){laneMaxNum = 2 }return laneMaxNum}// 获取中心路口八边形边长getSideLength() {let minW = this.w > this.h ? this.h : this.wlet sideLength = minW * this.laneCenterProportion / (Math.sqrt(2) + 1)return sideLength}// 计算车道坐标formatDataXY() {let dataXY = []// this.laneStyle// 车道起始中心位置let centerX = this.w / 2let centerY = this.h - this.h * (1 - this.laneCenterProportion) / 2// 车道长度let laneLength = Math.sqrt(Math.pow(this.w, 2) * Math.pow(this.h, 2))this.data.forEach(dataItem => {let dataXYItem = {nApproachDirection: dataItem.nApproachDirection,}// 起始xlet startX = centerX - this.laneStyle.width * dataItem.lanes.length// 起始ylet startY = centerY + this.laneStyle.zebraCrossing.height * 2// 结束Ylet endY = startY + laneLength// 线let lines = []// 单向车道分割线数量let innerLines = dataItem.lanes.length - 1// 车道左边线lines.push({x0: startX,y0: startY - this.laneStyle.zebraCrossing.height * 2,x1: startX,y1: endY,type: 'outer'})// 车道左侧分割线for (let i = 0; i < innerLines; i++) {let x = startX + (i + 1) * this.laneStyle.widthlines.push({x0: x,y0: startY,x1: x,y1: endY,style: { ...this.laneStyle.innerLeft }})}// 左右车道分割线let dividerX = startX + (innerLines + 1) * this.laneStyle.widthlines.push({x0: dividerX,y0: startY,x1: dividerX,y1: endY,style: { ...this.laneStyle.innerDivider }})// 车道右侧分割线for (let i = 0; i < innerLines; i++) {let x = startX + (innerLines + i + 2) * this.laneStyle.widthlines.push({x0: x,y0: startY,x1: x,y1: endY,style: { ...this.laneStyle.innerRight }})}// 车道右边线let outerRightx = startX + (innerLines + 1) * 2 * this.laneStyle.widthlines.push({x0: outerRightx,y0: startY - this.laneStyle.zebraCrossing.height * 2,x1: outerRightx,y1: endY,type: 'outer'})dataXYItem.lines = lines// 方向标识let directionIdentifyings = []for (let i = 0; i < dataItem.lanes.length; i++) {let laneItem = dataItem.lanes[i]let key = [laneItem.nThrough, laneItem.nTurnLeft, laneItem.nTurnRight, laneItem.nTurnAround].join('')let line = lines[innerLines + i + 1]directionIdentifyings.push(this.getDirectionIdentifyings(key, this.laneStyle.direction.width, this.laneStyle.direction.height, {x: line.x0 + this.laneStyle.width / 2,y: line.y0 + this.laneStyle.direction.height / 2 + this.laneStyle.trafficLight.r * 4}))}dataXYItem.directionIdentifyings = directionIdentifyings// 斑马线dataXYItem.zebraCrossing = [{x: lines[0].x0,y: startY - this.laneStyle.zebraCrossing.height}, {x: lines[lines.length - 1].x0,y: startY - this.laneStyle.zebraCrossing.height}]// 红绿灯let trafficLights = []for (let i = 0; i < dataItem.lanes.length; i++) {let laneItem = dataItem.lanes[i]let line = lines[innerLines + i + 1]trafficLights.push({x: line.x0 + this.laneStyle.width / 2,y: line.y0 + this.laneStyle.trafficLight.r * 2,r: this.laneStyle.trafficLight.r,color: this.laneStyle.trafficLight.colors[laneItem.nChannelNumberPhase - 1]})}dataXYItem.trafficLights = trafficLightsdataXY.push(dataXYItem)})this.dataXYByRotate(dataXY)this.dataXY = dataXY}// 获取方向标识坐标getDirectionIdentifyings(key, w, h, centerXY) {// 标识边界let topY = centerXY.y - h / 2let bottomY = centerXY.y + h / 2let leftX = centerXY.x - w / 2 * 3let rightX = centerXY.x + w / 2 * 3// 直行线中心位置let cX = centerXY.x + wlet cY = centerXY.y// 箭头宽高let arrowW = w * this.laneStyle.direction.arrowWidthlet arrowH = arrowW * Math.sin(Math.PI / 3)// 线坐标let points = []// 三角形坐标let arrowPoints = []switch (key) {case '0001':// 调头arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: topY + w / 2 },{ x: leftX + w / 2 * 5, y: topY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])break;case '0100':// 左转arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '1000':// 直行leftX = centerXY.x - w / 2rightX = centerXY.x + w / 2cX = centerXY.xarrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])break;case '0010':// 右转cX = centerXY.x - warrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '0101':// 调头左转arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '1001':// 调头直行arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])break;case '0011':// 调头右转leftX = centerXY.x - w / 2 * 5rightX = centerXY.x + w / 2 * 5arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])cX = centerXY.xarrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '1100':// 左转直行arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])break;case '0110':// 左转右转leftX = centerXY.x - w / 2 * 5rightX = centerXY.x + w / 2 * 5cX = centerXY.xarrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '1010':// 直行右转cX = centerXY.x - warrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '1101':// 调头左转直行arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])break;case '1011':// 调头直行右转leftX = centerXY.x - w / 2 * 5rightX = centerXY.x + w / 2 * 5cX = centerXY.xarrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[2][0].x + arrowPoints[2][2].x) / 2, y: (arrowPoints[2][0].y + arrowPoints[2][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '0111':// 调头左转右转leftX = centerXY.x - w / 2 * 5rightX = centerXY.x + w / 2 * 5cX = centerXY.xarrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[2][0].x + arrowPoints[2][2].x) / 2, y: (arrowPoints[2][0].y + arrowPoints[2][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '1110':// 左转直行右转leftX = centerXY.x - w / 2 * 5rightX = centerXY.x + w / 2 * 5cX = centerXY.xarrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[2][0].x + arrowPoints[2][2].x) / 2, y: (arrowPoints[2][0].y + arrowPoints[2][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;case '1111':// 调头左转直行右转leftX = centerXY.x - w / 2 * 5rightX = centerXY.x + w / 2 * 5cX = centerXY.xarrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: leftX + w / 2, y: bottomY - arrowH },{ x: leftX + w / 2, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: cY + w / 2 },{ x: leftX + w / 2 * 5, y: bottomY },])arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: cX, y: topY + arrowH },{ x: cX, y: bottomY }])arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))points.push([{ x: (arrowPoints[3][0].x + arrowPoints[3][2].x) / 2, y: (arrowPoints[3][0].y + arrowPoints[3][2].y) / 2 },{ x: cX, y: cY },{ x: cX, y: bottomY }])break;}return { arrowPoints, points }}getArrow(key, w, h, w2, h2, topY, bottomY, leftX, rightX, cX, cY) {let point = []let wd = (w - w2) / 2 // 三角形边长与线宽的差值的一半let rotateDeg = 30// 左转右转旋转角度let hv = h2 / Math.cos(Math.PI / 180 * rotateDeg) // 计算左转右转虚拟线长let topYv = topY - (hv - h2) / 2 // 虚拟起始高度switch (key) {case 1:// 调头point = [{ x: leftX - wd, y: bottomY - h },{ x: leftX + w2 / 2, y: bottomY },{ x: leftX + w2 + wd, y: bottomY - h }]break;case 2:// 左转point = [{ x: cX + w / 2, y: topYv + h },{ x: cX, y: topYv },{ x: cX - w / 2, y: topYv + h }]point.forEach(item => {let newXY = this.computePosition(item.x, item.y, -rotateDeg, cX, cY)item.x = newXY.xitem.y = newXY.y})break;case 3:// 直行point = [{ x: cX + w / 2, y: topY + h },{ x: cX, y: topY },{ x: cX - w / 2, y: topY + h }]break;case 4:// 右转point = [{ x: cX + w / 2, y: topYv + h },{ x: cX, y: topYv },{ x: cX - w / 2, y: topYv + h }]point.forEach(item => {let newXY = this.computePosition(item.x, item.y, rotateDeg, cX, cY)item.x = newXY.xitem.y = newXY.y})break;}return point}// 计算旋转坐标dataXYByRotate(dataXY) {let centerX = this.w / 2let centerY = this.h / 2dataXY.forEach(dataXYItem => {// 八边形,一个边占45度let rotateReg = -180 + (dataXYItem.nApproachDirection - 1) * 45dataXYItem.lines.forEach(line => {let xy0 = this.computePosition(line.x0, line.y0, rotateReg, centerX, centerY)line.x0 = xy0.xline.y0 = xy0.ylet xy1 = this.computePosition(line.x1, line.y1, rotateReg, centerX, centerY)line.x1 = xy1.xline.y1 = xy1.y})dataXYItem.directionIdentifyings.forEach(directionIdentifying => {directionIdentifying.points.forEach(point => {point.forEach(item => {let { x, y } = this.computePosition(item.x, item.y, rotateReg, centerX, centerY)item.x = xitem.y = y})})directionIdentifying.arrowPoints.forEach(arrowPoint => {arrowPoint.forEach(item => {let { x, y } = this.computePosition(item.x, item.y, rotateReg, centerX, centerY)item.x = xitem.y = y})})})dataXYItem.zebraCrossing.forEach(zebraCrossing => {let { x, y } = this.computePosition(zebraCrossing.x, zebraCrossing.y, rotateReg, centerX, centerY)zebraCrossing.x = xzebraCrossing.y = y})dataXYItem.trafficLights.forEach(trafficLight => {let { x, y } = this.computePosition(trafficLight.x, trafficLight.y, rotateReg, centerX, centerY)trafficLight.x = xtrafficLight.y = y})})}// 旋转计算computePosition(x, y, angle, centerX, centerY) {// 圆心let a = centerX;let b = centerY;// 计算let c = Math.PI / 180 * angle;let rx = (x - a) * Math.cos(c) - (y - b) * Math.sin(c) + a;let ry = (y - b) * Math.cos(c) + (x - a) * Math.sin(c) + b;return { x: rx, y: ry };}// 获取车道范围getRegion() {let region = []for (let i = 0; i < this.dataXY.length; i++) {let dataXYItem = this.dataXY[i]let linesLength = dataXYItem.lines.lengthif (i !== 0) {// 衔接上一车道let prevDataXYItem = this.dataXY[i - 1]let data = {prevNApproachDirection: prevDataXYItem.nApproachDirection,nApproachDirection: dataXYItem.nApproachDirection,type: 'connect'}let diffNApproachDirection = dataXYItem.nApproachDirection - prevDataXYItem.nApproachDirectionif(diffNApproachDirection > 4){diffNApproachDirection = (prevDataXYItem.nApproachDirection + 8) - dataXYItem.nApproachDirection}if (diffNApproachDirection === 4) {// 车道正对,直线即可data.point = [{ x: prevDataXYItem.lines[0].x0, y: prevDataXYItem.lines[0].y0 },{ x: dataXYItem.lines[linesLength - 1].x0, y: dataXYItem.lines[linesLength - 1].y0 },]region.push(data)} else {if (this.laneStyle.region.CurveType === 'arc') {let angle = 45 * diffNApproachDirectionlet startAngle = 45 * (prevDataXYItem.nApproachDirection - 5)data.OR = this.findCircleCenter(prevDataXYItem.lines[0].x0, prevDataXYItem.lines[0].y0,dataXYItem.lines[linesLength - 1].x0, dataXYItem.lines[linesLength - 1].y0,angle,true)data.OR.startAngle = startAngledata.OR.endAngle = startAngle - angledata.OR.anticlockwise = true} else {// 曲线let laneXY0 = this.calculateIntersection([[prevDataXYItem.lines[0].x0, prevDataXYItem.lines[0].y0],[prevDataXYItem.lines[0].x1, prevDataXYItem.lines[0].y1],], [[dataXYItem.lines[linesLength - 1].x0, dataXYItem.lines[linesLength - 1].y0],[dataXYItem.lines[linesLength - 1].x1, dataXYItem.lines[linesLength - 1].y1],])let laneXY1 = [(prevDataXYItem.lines[0].x0 + dataXYItem.lines[linesLength - 1].x0) / 2, (prevDataXYItem.lines[0].y0 + dataXYItem.lines[linesLength - 1].y0) / 2]let originPoints = [{ x: prevDataXYItem.lines[0].x0, y: prevDataXYItem.lines[0].y0 },{ x: laneXY1[0] - (laneXY1[0] - laneXY0[0]) * diffNApproachDirection / 4, y: laneXY1[1] - (laneXY1[1] - laneXY0[1]) * diffNApproachDirection / 4 },{ x: dataXYItem.lines[linesLength - 1].x0, y: dataXYItem.lines[linesLength - 1].y0 },]if (this.laneStyle.region.CurveType === 'normal') {let point = this.getCurveVertex(originPoints)data.point = point} else {data.point = originPoints}}region.push(data)}}// 车道范围region.push({nApproachDirection: dataXYItem.nApproachDirection,x0: dataXYItem.lines[linesLength - 1].x0,y0: dataXYItem.lines[linesLength - 1].y0,x1: dataXYItem.lines[linesLength - 1].x1,y1: dataXYItem.lines[linesLength - 1].y1,x2: dataXYItem.lines[0].x1,y2: dataXYItem.lines[0].y1,x3: dataXYItem.lines[0].x0,y3: dataXYItem.lines[0].y0,type: 'lane'})if (i === this.dataXY.length - 1) {// 衔接起始车道let startDataXYItem = this.dataXY[0]let startLinesLength = startDataXYItem.lines.lengthlet data = {startNApproachDirection: startDataXYItem.nApproachDirection,nApproachDirection: dataXYItem.nApproachDirection,type: 'connect'}let diffNApproachDirection = startDataXYItem.nApproachDirection + 8 - dataXYItem.nApproachDirectionif(diffNApproachDirection > 4){diffNApproachDirection = dataXYItem.nApproachDirection - startDataXYItem.nApproachDirection}if (diffNApproachDirection === 4) {// 车道正对,直线即可data.point = [{ x: dataXYItem.lines[0].x0, y: dataXYItem.lines[0].y0 },{ x: startDataXYItem.lines[startLinesLength - 1].x0, y: startDataXYItem.lines[startLinesLength - 1].y0 },]region.push(data)} else {if (this.laneStyle.region.CurveType === 'arc') {let angle = 45 * diffNApproachDirectionlet startAngle = 45 * (dataXYItem.nApproachDirection - 1)data.OR = this.findCircleCenter(dataXYItem.lines[0].x0, dataXYItem.lines[0].y0,startDataXYItem.lines[linesLength - 1].x0, startDataXYItem.lines[linesLength - 1].y0,angle,true)data.OR.endAngle = startAngle + angledata.OR.startAngle = startAngledata.OR.anticlockwise = false} else {// 曲线let laneXY0 = this.calculateIntersection([[dataXYItem.lines[0].x0, dataXYItem.lines[0].y0],[dataXYItem.lines[0].x1, dataXYItem.lines[0].y1],], [[startDataXYItem.lines[startLinesLength - 1].x0, startDataXYItem.lines[startLinesLength - 1].y0],[startDataXYItem.lines[startLinesLength - 1].x1, startDataXYItem.lines[startLinesLength - 1].y1],])let laneXY1 = [(startDataXYItem.lines[startLinesLength - 1].x0 + dataXYItem.lines[0].x0) / 2, (startDataXYItem.lines[startLinesLength - 1].y0 + dataXYItem.lines[0].y0) / 2]let originPoints = [{ x: dataXYItem.lines[0].x0, y: dataXYItem.lines[0].y0 },{ x: laneXY1[0] - (laneXY1[0] - laneXY0[0]) * diffNApproachDirection / 4, y: laneXY1[1] - (laneXY1[1] - laneXY0[1]) * diffNApproachDirection / 4 },{ x: startDataXYItem.lines[startLinesLength - 1].x0, y: startDataXYItem.lines[startLinesLength - 1].y0 },]if (this.laneStyle.region.CurveType === 'normal') {let point = this.getCurveVertex(originPoints)data.point = point} else {data.point = originPoints}}region.push(data)}}}this.region = region}// 获取两条直线的交点calculateIntersection(line1, line2) {// 解方程组const x1 = line1[0][0];const y1 = line1[0][1];const x2 = line1[1][0];const y2 = line1[1][1];const x3 = line2[0][0];const y3 = line2[0][1];const x4 = line2[1][0];const y4 = line2[1][1];const denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);if (denominator === 0) {// 直线平行,没有交点return null;}const intersectionX = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denominator;const intersectionY = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denominator;return [intersectionX, intersectionY];}// 以下四个方法获取曲线getCurveVertex(vertex, pointsPow = 0.4) {let length = 0for (let i = 0; i < vertex.length - 1; i++) {length += Math.sqrt(Math.pow(vertex[i].x - vertex[i + 1].x, 2) + Math.pow(vertex[i].y - vertex[i + 1].y, 2))}length = Math.ceil(length)return this.getNewData(vertex, length * pointsPow)}// 曲线 插值getNewData(pointsOrigin, pointsPow) {const points = []const divisions = (pointsOrigin.length - 1) * pointsPowfor (let i = 0; i < divisions; i++) {points.push(this.getPoint(i, divisions, pointsOrigin, pointsPow))}return points}getPoint(i, divisions, pointsOrigin, pointsPow) {const isRealI = (i * divisions) % pointsPowconst p = ((pointsOrigin.length - 1) * i) / divisionsconst intPoint = Math.floor(p)const weight = p - intPointconst p0 = pointsOrigin[intPoint === 0 ? intPoint : intPoint - 1]const p1 = pointsOrigin[intPoint]const p2 = pointsOrigin[intPoint > pointsOrigin.length - 2 ? pointsOrigin.length - 1 : intPoint + 1]const p3 = pointsOrigin[intPoint > pointsOrigin.length - 3 ? pointsOrigin.length - 1 : intPoint + 2]return {isReal: isRealI === 0,x: this.catmullRom(weight, p0.x, p1.x, p2.x, p3.x),y: this.catmullRom(weight, p0.y, p1.y, p2.y, p3.y)}}catmullRom(t, p0, p1, p2, p3) {const v0 = (p2 - p0) * 0.5const v1 = (p3 - p1) * 0.5const t2 = t * tconst t3 = t * t2return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1}// 根据圆上两点以及夹角角度 求 圆心findCircleCenter(x1, y1, x2, y2, theta, isNeg) {let cx = 0;let cy = 0;let dDistance = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));let dRadius = dDistance * 0.5 / Math.sin(Math.PI / 180 * theta * 0.5);if (dDistance == 0.0) {// cout << "\n输入了相同的点!\n";return false;}if ((2 * dRadius) < dDistance) {// cout << "\n两点间距离大于直径!\n";return false;}let k_verticle = 0.0;let mid_x = 0.0let mid_y = 0.0;let a = 1.0;let b = 1.0;let c = 1.0;let k = (y2 - y1) / (x2 - x1);let cx1, cy1, cx2, cy2;if (k == 0) {cx1 = (x1 + x2) / 2.0;cx2 = (x1 + x2) / 2.0;cy1 = y1 + Math.sqrt(dRadius * dRadius - (x1 - x2) * (x1 - x2) / 4.0);cy2 = y2 - Math.sqrt(dRadius * dRadius - (x1 - x2) * (x1 - x2) / 4.0);}else {k_verticle = -1.0 / k;mid_x = (x1 + x2) / 2.0;mid_y = (y1 + y2) / 2.0;a = 1.0 + k_verticle * k_verticle;b = -2 * mid_x - k_verticle * k_verticle * (x1 + x2);c = mid_x * mid_x + k_verticle * k_verticle * (x1 + x2) * (x1 + x2) / 4.0 -(dRadius * dRadius - ((mid_x - x1) * (mid_x - x1) + (mid_y - y1) * (mid_y - y1)));cx1 = (-1.0 * b + Math.sqrt(b * b - 4 * a * c)) / (2 * a);cx2 = (-1.0 * b - Math.sqrt(b * b - 4 * a * c)) / (2 * a);cy1 = this.y_Coordinates(mid_x, mid_y, k_verticle, cx1);cy2 = this.y_Coordinates(mid_x, mid_y, k_verticle, cx2);}//cx2,cy2为顺时针圆心坐标,cx1,cy1为逆时针圆心坐标if (isNeg) {cx = cx1;cy = cy1;}else {cx = cx2;cy = cy2;}return {x: cx, y: cy, r: Math.sqrt(Math.pow(cx - x1, 2) + Math.pow(cy - y1, 2))};}y_Coordinates(x, y, k, x0) {return k * x0 - k * x + y;}// 设置新的红绿灯数据setData(data){this.dataXY.forEach((dataXYItem, dataXYIndex) => {dataXYItem.trafficLights.forEach((trafficLight, trafficLightIndex) => {trafficLight.color = this.laneStyle.trafficLight.colors[data[dataXYIndex].lanes[trafficLightIndex].nChannelNumberPhase - 1]})})this.ctx.clearRect(0,0,this.w,this.h)this.draw()}// 绘制draw() {this.drawRegion()this.drawLines()this.drawDirectionIdentifyings()this.drawZebraCrossing()this.drawTrafficLight()// this.drawHelper()}drawRegion() {this.ctx.save()// 缩放// this.ctx.translate(this.w / 2, this.h / 2)// this.ctx.scale(this.scaleC, this.scaleC)// this.ctx.translate(-this.w / 2, -this.h / 2)this.ctx.beginPath()this.ctx.fillStyle = this.laneStyle.region.backgroundthis.ctx.lineWidth = this.laneStyle.region.widththis.ctx.strokeStyle = this.laneStyle.region.colorthis.ctx.lineJoin = 'round';for (let i = 0; i < this.region.length; i++) {let regionItem = this.region[i]if (regionItem.type === 'connect') {if (regionItem?.point?.length === 2 && this.laneStyle.region.CurveType !== 'arc') {// 直线regionItem.point.forEach(item => {this.ctx.lineTo(item.x, item.y)})} else if (this.laneStyle.region.CurveType === 'arc') {// 圆if(regionItem.OR)this.ctx.arc(regionItem.OR.x, regionItem.OR.y, regionItem.OR.r, regionItem.OR.startAngle * Math.PI / 180, regionItem.OR.endAngle * Math.PI / 180, regionItem.OR.anticlockwise)} else if (this.laneStyle.region.CurveType === 'normal') {// 插值regionItem.point.forEach(item => {this.ctx.lineTo(item.x, item.y)})} else {// 二次贝塞尔this.ctx.lineTo(regionItem.point[0].x, regionItem.point[0].y)this.ctx.quadraticCurveTo(regionItem.point[1].x, regionItem.point[1].y, regionItem.point[2].x, regionItem.point[2].y)}} else {this.ctx.lineTo(regionItem.x0, regionItem.y0)this.ctx.lineTo(regionItem.x1, regionItem.y1)this.ctx.lineTo(regionItem.x2, regionItem.y2)this.ctx.lineTo(regionItem.x3, regionItem.y3)}}this.ctx.fill()this.ctx.stroke()this.ctx.closePath()this.ctx.restore()}drawLines() {this.dataXY.forEach((dataXYItem, dataXYIndex) => {dataXYItem.lines.forEach(lineItem => {if (lineItem.type !== 'outer') {this.ctx.save()// 缩放// this.ctx.translate(this.w / 2, this.h / 2)// this.ctx.scale(this.scaleC, this.scaleC)// this.ctx.translate(-this.w / 2, -this.h / 2)this.ctx.beginPath()this.ctx.lineWidth = lineItem.style.widththis.ctx.strokeStyle = lineItem.style.colorif (lineItem.style.type !== 'solid') {this.ctx.setLineDash(lineItem.style.type);}this.ctx.lineTo(lineItem.x0, lineItem.y0)this.ctx.lineTo(lineItem.x1, lineItem.y1)this.ctx.stroke()this.ctx.closePath()this.ctx.restore()}})})}drawDirectionIdentifyings() {this.dataXY.forEach((dataXYItem, dataXYIndex) => {dataXYItem.directionIdentifyings.forEach(directionIdentifying => {this.ctx.save()// 缩放// this.ctx.translate(this.w / 2, this.h / 2)// this.ctx.scale(this.scaleC, this.scaleC)// this.ctx.translate(-this.w / 2, -this.h / 2)directionIdentifying.points.forEach(pointItem => {this.ctx.beginPath()this.ctx.lineWidth = directionIdentifying.wthis.ctx.strokeStyle = this.laneStyle.direction.backgroundpointItem.forEach(item => {this.ctx.lineTo(item.x, item.y)})this.ctx.stroke()this.ctx.closePath()})directionIdentifying.arrowPoints.forEach(arrowPoint => {this.ctx.beginPath()this.ctx.fillStyle = this.laneStyle.direction.backgroundarrowPoint.forEach(item => {this.ctx.lineTo(item.x, item.y)})this.ctx.fill()this.ctx.closePath()})this.ctx.restore()})})}drawTrafficLight() {this.dataXY.forEach((dataXYItem, dataXYIndex) => {dataXYItem.trafficLights.forEach(trafficLight => {this.ctx.save()// 缩放// this.ctx.translate(this.w / 2, this.h / 2)// this.ctx.scale(this.scaleC, this.scaleC)// this.ctx.translate(-this.w / 2, -this.h / 2)this.ctx.beginPath()this.ctx.fillStyle = trafficLight.colorthis.ctx.arc(trafficLight.x, trafficLight.y, trafficLight.r, 0, Math.PI * 2)this.ctx.fill()this.ctx.closePath()this.ctx.restore()})})}drawZebraCrossing() {this.dataXY.forEach((dataXYItem, dataXYIndex) => {this.ctx.save()// 缩放// this.ctx.translate(this.w / 2, this.h / 2)// this.ctx.scale(this.scaleC, this.scaleC)// this.ctx.translate(-this.w / 2, -this.h / 2)this.ctx.beginPath()this.ctx.lineWidth = this.laneStyle.zebraCrossing.heightthis.ctx.strokeStyle = this.laneStyle.zebraCrossing.colorthis.ctx.setLineDash(this.laneStyle.zebraCrossing.type);dataXYItem.zebraCrossing.forEach(zebraCrossing => {this.ctx.lineTo(zebraCrossing.x, zebraCrossing.y)})this.ctx.stroke()this.ctx.closePath()this.ctx.restore()})}drawHelper() {// 绘制车道方向数字,用来查看车道是否正确this.ctx.beginPath()this.ctx.fillStyle = '#fff'this.ctx.font = 20 * this.dpr + 'px Arial'for (let i = 0; i < this.region.length; i++) {let regionItem = this.region[i]this.ctx.fillText(regionItem.nApproachDirection, regionItem.x0, regionItem.y0)}this.ctx.closePath()// 绘制坐标线this.ctx.save()this.ctx.lineWidth = 2 * this.dprthis.ctx.strokeStyle = 'red'this.ctx.beginPath()this.ctx.lineTo(this.w / 2, 0)this.ctx.lineTo(this.w / 2, this.h)this.ctx.stroke()this.ctx.closePath()this.ctx.beginPath()this.ctx.lineTo(0, this.h / 2)this.ctx.lineTo(this.w, this.h / 2)this.ctx.stroke()this.ctx.closePath()this.ctx.restore()// 绘制方向标识let testKeys = ['0001', '0100', '1000', '0010', '0101', '1001', '0011', '1100', '0110', '1010', '1101', '1011', '0111', '1110', '1111']let width = this.w / testKeys.lengthlet w = width * this.laneStyle.direction.widthProportionlet h = w * this.laneStyle.direction.HeightWidthProportiontestKeys.forEach((key, index) => {let { arrowPoints, points } = this.getDirectionIdentifyings(key, w, h, { x: width * index + width / 2, y: this.h / 2 })this.ctx.save()// 缩放// this.ctx.translate(this.w / 2, this.h / 2)// this.ctx.scale(this.scaleC, this.scaleC)// this.ctx.translate(-this.w / 2, -this.h / 2)points.forEach(pointItem => {this.ctx.beginPath()this.ctx.lineWidth = wthis.ctx.strokeStyle = '#fff'pointItem.forEach(item => {this.ctx.lineTo(item.x, item.y)})this.ctx.stroke()this.ctx.closePath()})arrowPoints.forEach(arrowPoint => {this.ctx.beginPath()this.ctx.fillStyle = '#fff'arrowPoint.forEach(item => {this.ctx.lineTo(item.x, item.y)})this.ctx.fill()this.ctx.closePath()})this.ctx.restore()})}
}export default Lane