|
|
@@ -45,9 +45,6 @@ export default {
|
|
|
scanLineTimer: null,
|
|
|
currentViewMinX: 0,
|
|
|
currentScanX: 0,
|
|
|
- // 每条波带的速度 42~48 km/h 随机(组件初始化时生成一次)
|
|
|
- greenSpeed: Math.round((42 + Math.random() * 6) * 10) / 10,
|
|
|
- blueSpeed: Math.round((42 + Math.random() * 6) * 10) / 10,
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -137,33 +134,32 @@ export default {
|
|
|
},
|
|
|
|
|
|
getWaveData() {
|
|
|
+ const speedMs = this.speedKmh / 3.6; // 设计车速换算为 m/s
|
|
|
const startX = this.intersections[0].x;
|
|
|
const endX = this.intersections[this.intersections.length - 1].x;
|
|
|
- const greenTravelTime = (endX - startX) / (this.greenSpeed / 3.6);
|
|
|
- const blueTravelTime = (endX - startX) / (this.blueSpeed / 3.6);
|
|
|
+ const travelTime = (endX - startX) / speedMs;
|
|
|
|
|
|
- // 【终极微调】:发车时间精确控制
|
|
|
- // 正向绿波 (A -> D):10秒起步,刚好完美贴合所有绿灯边缘
|
|
|
- const gStartY = 10;
|
|
|
+ // 正向绿波 (A -> D):gStartY 秒起步
|
|
|
+ const gStartY = 0;
|
|
|
const greenBottomLine = [
|
|
|
[startX, gStartY],
|
|
|
- [endX, gStartY + greenTravelTime]
|
|
|
+ [endX, gStartY + travelTime]
|
|
|
];
|
|
|
const greenTopLine = [
|
|
|
[startX, gStartY + this.bandwidth],
|
|
|
- [endX, gStartY + greenTravelTime + this.bandwidth]
|
|
|
+ [endX, gStartY + travelTime + this.bandwidth]
|
|
|
];
|
|
|
const greenCoords = [...greenBottomLine, ...[...greenTopLine].reverse()];
|
|
|
|
|
|
- // 反向蓝波 (D -> A):100秒起步,完美避开 B 路口的红灯区域
|
|
|
+ // 反向蓝波 (D -> A):bStartYAtD 秒起步
|
|
|
const bStartYAtD = 100;
|
|
|
const blueBottomLine = [
|
|
|
[endX, bStartYAtD],
|
|
|
- [startX, bStartYAtD + blueTravelTime]
|
|
|
+ [startX, bStartYAtD + travelTime]
|
|
|
];
|
|
|
const blueTopLine = [
|
|
|
[endX, bStartYAtD + this.bandwidth],
|
|
|
- [startX, bStartYAtD + blueTravelTime + this.bandwidth]
|
|
|
+ [startX, bStartYAtD + travelTime + this.bandwidth]
|
|
|
];
|
|
|
const blueCoords = [...blueBottomLine, ...[...blueTopLine].reverse()];
|
|
|
|
|
|
@@ -174,8 +170,7 @@ export default {
|
|
|
topLine: greenTopLine,
|
|
|
color: this.upWaveColor,
|
|
|
lineCol: '#2ecc71',
|
|
|
- isBlue: false,
|
|
|
- speed: this.greenSpeed
|
|
|
+ isBlue: false
|
|
|
},
|
|
|
{
|
|
|
coords: blueCoords,
|
|
|
@@ -183,8 +178,7 @@ export default {
|
|
|
topLine: blueTopLine,
|
|
|
color: this.downWaveColor,
|
|
|
lineCol: '#3498db',
|
|
|
- isBlue: true,
|
|
|
- speed: this.blueSpeed
|
|
|
+ isBlue: true
|
|
|
}
|
|
|
];
|
|
|
},
|
|
|
@@ -192,10 +186,13 @@ export default {
|
|
|
updateChart() {
|
|
|
if (!this.$_chart) return;
|
|
|
|
|
|
- // x 轴刻度仅在红块首尾(每周期的 greenDuration 与 cycle 结束点)显示
|
|
|
- const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
|
|
|
- const redLen = this.cycle - this.greenDuration;
|
|
|
- const tickInterval = redLen > 0 ? gcd(this.greenDuration, redLen) : this.greenDuration;
|
|
|
+ // x 轴刻度仅对齐首路口(A)的红块首尾
|
|
|
+ const gcd = (a, b) => (b === 0 ? Math.abs(a) : gcd(b, a % b));
|
|
|
+ const firstOffset = this.intersections[0] ? this.intersections[0].offset : 0;
|
|
|
+ const redStartMod = ((firstOffset + this.greenDuration) % this.cycle + this.cycle) % this.cycle; // 红块起点
|
|
|
+ const redEndMod = ((firstOffset) % this.cycle + this.cycle) % this.cycle; // 红块终点 = 下周期起点
|
|
|
+ // interval 需同时整除 redStartMod 与 redEndMod 的间距,保证 label 落点
|
|
|
+ const tickInterval = gcd(gcd(this.greenDuration, this.cycle - this.greenDuration), firstOffset || this.cycle);
|
|
|
|
|
|
const option = {
|
|
|
backgroundColor: 'transparent',
|
|
|
@@ -207,14 +204,18 @@ export default {
|
|
|
interval: tickInterval, name: '时间 (秒)',
|
|
|
nameLocation: 'end',
|
|
|
nameTextStyle: { color: '#a0aabf', padding: [0, 0, 0, 0], fontSize: this.fs(12) },
|
|
|
- axisLine: { show: true, onZero: false, lineStyle: { color: 'rgba(255,255,255,0.15)' } },
|
|
|
- axisTick: { show: false },
|
|
|
+ axisLine: { show: true, onZero: false, lineStyle: { color: 'rgba(255,255,255,0.5)' } },
|
|
|
+ axisTick: {
|
|
|
+ show: true,
|
|
|
+ lineStyle: { color: 'rgba(255,255,255,0.5)' },
|
|
|
+ length: this.fs(5)
|
|
|
+ },
|
|
|
axisLabel: {
|
|
|
color: '#a0aabf',
|
|
|
fontSize: this.fs(10),
|
|
|
formatter: (value) => {
|
|
|
const mod = ((value % this.cycle) + this.cycle) % this.cycle;
|
|
|
- return (mod === 0 || mod === this.greenDuration) ? value : '';
|
|
|
+ return (mod === redStartMod || mod === redEndMod) ? value : '';
|
|
|
}
|
|
|
},
|
|
|
splitLine: { show: this.showYSplitLine, lineStyle: { type: 'dashed', color: 'rgba(255, 255, 255, 0.08)' } }
|
|
|
@@ -315,7 +316,7 @@ export default {
|
|
|
{ type: 'polyline', shape: { points }, style: { stroke: item.lineCol, lineDash: [4, 4], lineWidth: 1 } }
|
|
|
];
|
|
|
})(),
|
|
|
- { type: 'text', x: speedX, y: speedY, rotation: speedAngle, style: { text: `${item.speed}km/h`, fill: '#fff', fontSize: this.fs(11), textAlign: 'center' } },
|
|
|
+ { type: 'text', x: speedX, y: speedY, rotation: speedAngle, style: { text: `${this.speedKmh}km/h`, fill: '#fff', fontSize: this.fs(11), textAlign: 'center' } },
|
|
|
...(() => {
|
|
|
const cxBw = (midBottomX + midTopX) / 2;
|
|
|
const cyBw = (midBottomY + midTopY) / 2;
|