|
|
@@ -4,12 +4,13 @@
|
|
|
|
|
|
<script>
|
|
|
import * as echarts from 'echarts';
|
|
|
-
|
|
|
-// 定义大屏设计稿基准宽度 (假设为 1920)
|
|
|
-const DESIGN_WIDTH = 1920;
|
|
|
+// 1. 引入我们的防变形神器和混入
|
|
|
+import echartsResize, { px2echarts } from '@/mixins/echartsResize.js';
|
|
|
|
|
|
export default {
|
|
|
name: 'TrafficTimeSpace',
|
|
|
+ // 2. 注册混入,自动接管组件的图表缩放、数据重绘和销毁生命周期
|
|
|
+ mixins: [echartsResize],
|
|
|
props: {
|
|
|
intersections: { type: Array, required: true },
|
|
|
distances: { type: Array, required: true },
|
|
|
@@ -24,9 +25,9 @@ export default {
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
- chart: null,
|
|
|
scrollTimer: null,
|
|
|
currentViewTime: 0
|
|
|
+ // 删除了 chart 实例,交由 mixin 的 $_chart 管理
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -52,15 +53,7 @@ export default {
|
|
|
},
|
|
|
beforeDestroy() {
|
|
|
this.stopScroll();
|
|
|
- if (this._resizeObserver) {
|
|
|
- this._resizeObserver.disconnect();
|
|
|
- this._resizeObserver = null;
|
|
|
- }
|
|
|
- if (this.chart) {
|
|
|
- this.chart.dispose();
|
|
|
- this.chart = null;
|
|
|
- }
|
|
|
- window.removeEventListener('resize', this._resizeHandler);
|
|
|
+ // 所有的 resize 和 chart.dispose 统统删掉,mixin 会帮你兜底清理!
|
|
|
},
|
|
|
watch: {
|
|
|
waveData() { this.updateChart(); },
|
|
|
@@ -68,65 +61,31 @@ export default {
|
|
|
autoScroll(val) { val ? this.startScroll() : this.stopScroll(); }
|
|
|
},
|
|
|
methods: {
|
|
|
- // 【新增】:获取真实缩放比例
|
|
|
- getRealScale() {
|
|
|
- return window.innerWidth / DESIGN_WIDTH;
|
|
|
- },
|
|
|
-
|
|
|
initChart() {
|
|
|
- this.chart = echarts.init(this.$refs.chartContainer);
|
|
|
-
|
|
|
- // 窗口变化时的防抖处理
|
|
|
- this._resizeHandler = () => {
|
|
|
- if (this.chart) {
|
|
|
- this.chart.resize();
|
|
|
- this.updateChart(); // 【关键】:尺寸变了,需要重新生成 option 刷新字体和线条粗细!
|
|
|
- }
|
|
|
- };
|
|
|
- window.addEventListener('resize', this._resizeHandler);
|
|
|
-
|
|
|
- // 容器变化时的处理 (拖拽拉伸弹窗)
|
|
|
- if (typeof ResizeObserver !== 'undefined') {
|
|
|
- this._roaPending = false;
|
|
|
- this._resizeObserver = new ResizeObserver(() => {
|
|
|
- if (!this._roaPending) {
|
|
|
- this._roaPending = true;
|
|
|
- requestAnimationFrame(() => {
|
|
|
- this._roaPending = false;
|
|
|
- if (this.chart) {
|
|
|
- this.chart.resize();
|
|
|
- this.updateChart(); // 【关键】:同样需要重新渲染内部配置
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- this._resizeObserver.observe(this.$refs.chartContainer);
|
|
|
- }
|
|
|
-
|
|
|
+ // 3. 将实例赋值给 $_chart (mixin 规定的变量)
|
|
|
+ this.$_chart = echarts.init(this.$refs.chartContainer);
|
|
|
this.updateChart();
|
|
|
},
|
|
|
|
|
|
+ // mixin 中的 resize 会在窗口变化时自动调用这个方法
|
|
|
updateChart() {
|
|
|
- if (!this.chart) return;
|
|
|
+ if (!this.$_chart) return;
|
|
|
|
|
|
const self = this;
|
|
|
const distances = this.distances;
|
|
|
const intersections = this.reversedIntersections;
|
|
|
const maxDist = this.maxDistance;
|
|
|
-
|
|
|
- // 拿到当前缩放比例
|
|
|
- const scale = this.getRealScale();
|
|
|
|
|
|
- this.chart.setOption({
|
|
|
+ this.$_chart.setOption({
|
|
|
backgroundColor: 'transparent',
|
|
|
animation: false,
|
|
|
tooltip: { show: false },
|
|
|
- // 网格的边距也乘一下,防止屏幕缩小时文字被切掉
|
|
|
grid: {
|
|
|
- left: Math.round(90 * scale),
|
|
|
- right: Math.round(15 * scale),
|
|
|
- top: Math.round(10 * scale),
|
|
|
- bottom: Math.round(30 * scale)
|
|
|
+ // 4. 网格全部使用 px2echarts
|
|
|
+ left: px2echarts(80),
|
|
|
+ right: px2echarts(15),
|
|
|
+ top: px2echarts(10),
|
|
|
+ bottom: px2echarts(25)
|
|
|
},
|
|
|
xAxis: {
|
|
|
type: 'value',
|
|
|
@@ -135,7 +94,7 @@ export default {
|
|
|
axisLabel: {
|
|
|
color: '#7b95b9',
|
|
|
formatter: '{value}s',
|
|
|
- fontSize: Math.round(10 * scale) // 【修复】坐标轴字体随比例缩放
|
|
|
+ fontSize: px2echarts(10) // 5. 坐标轴字体
|
|
|
},
|
|
|
splitLine: { show: true, lineStyle: { color: '#1a305d', type: 'solid' } },
|
|
|
axisLine: { lineStyle: { color: '#31548e' } }
|
|
|
@@ -149,7 +108,7 @@ export default {
|
|
|
interval: 0,
|
|
|
color: '#9cb1d4',
|
|
|
fontWeight: 'bold',
|
|
|
- fontSize: Math.round(10 * scale), // 【修复】路口名字体随比例缩放
|
|
|
+ fontSize: px2echarts(10), // 5. 坐标轴字体
|
|
|
formatter: value => distances.includes(value) ? intersections[distances.indexOf(value)] : ''
|
|
|
},
|
|
|
splitLine: { show: true, lineStyle: { color: '#1a305d' } }
|
|
|
@@ -157,21 +116,21 @@ export default {
|
|
|
series: [
|
|
|
{
|
|
|
type: 'custom',
|
|
|
- renderItem: function (params, api) { return self.renderWave(params, api, scale); },
|
|
|
+ renderItem: function (params, api) { return self.renderWave(params, api); },
|
|
|
data: this.echartsWaveData,
|
|
|
clip: true,
|
|
|
z: 1
|
|
|
},
|
|
|
{
|
|
|
type: 'custom',
|
|
|
- renderItem: function (params, api) { return self.renderRedBackground(params, api, scale); },
|
|
|
+ renderItem: function (params, api) { return self.renderRedBackground(params, api); },
|
|
|
data: this.echartsRedData,
|
|
|
clip: true,
|
|
|
z: 2
|
|
|
},
|
|
|
{
|
|
|
type: 'custom',
|
|
|
- renderItem: function (params, api) { return self.renderGreenLight(params, api, scale); },
|
|
|
+ renderItem: function (params, api) { return self.renderGreenLight(params, api); },
|
|
|
data: this.echartsGreenData,
|
|
|
clip: true,
|
|
|
z: 3
|
|
|
@@ -180,38 +139,40 @@ export default {
|
|
|
});
|
|
|
},
|
|
|
|
|
|
- // 注意:这里的 renderItem 都把 scale 参数传进去了
|
|
|
- renderRedBackground(params, api, scale) {
|
|
|
+ // 6. renderItem 里的所有死像素全换成了 px2echarts
|
|
|
+ renderRedBackground(params, api) {
|
|
|
const y = api.value(0);
|
|
|
const startX = api.coord([0, y])[0];
|
|
|
const endX = api.coord([this.maxDataTime, y])[0];
|
|
|
- // 高度和 Y轴偏移量 都要乘上 scale
|
|
|
- const rectHeight = Math.round(6 * scale);
|
|
|
- const rectOffsetY = Math.round(3 * scale);
|
|
|
-
|
|
|
return {
|
|
|
type: 'rect',
|
|
|
- shape: { x: startX, y: api.coord([0, y])[1] - rectOffsetY, width: endX - startX, height: rectHeight },
|
|
|
+ shape: {
|
|
|
+ x: startX,
|
|
|
+ y: api.coord([0, y])[1] - px2echarts(3),
|
|
|
+ width: endX - startX,
|
|
|
+ height: px2echarts(6)
|
|
|
+ },
|
|
|
style: { fill: '#f02828' }
|
|
|
};
|
|
|
},
|
|
|
|
|
|
- renderGreenLight(params, api, scale) {
|
|
|
+ renderGreenLight(params, api) {
|
|
|
const y = api.value(0);
|
|
|
const p1 = api.coord([api.value(1), y]);
|
|
|
const p2 = api.coord([api.value(2), y]);
|
|
|
- // 高度和 Y轴偏移量 都要乘上 scale
|
|
|
- const rectHeight = Math.round(8 * scale);
|
|
|
- const rectOffsetY = Math.round(4 * scale);
|
|
|
-
|
|
|
return {
|
|
|
type: 'rect',
|
|
|
- shape: { x: p1[0], y: p1[1] - rectOffsetY, width: p2[0] - p1[0], height: rectHeight },
|
|
|
+ shape: {
|
|
|
+ x: p1[0],
|
|
|
+ y: p1[1] - px2echarts(4),
|
|
|
+ width: p2[0] - p1[0],
|
|
|
+ height: px2echarts(8)
|
|
|
+ },
|
|
|
style: api.style({ fill: '#68e75f' })
|
|
|
};
|
|
|
},
|
|
|
|
|
|
- renderWave(params, api, scale) {
|
|
|
+ renderWave(params, api) {
|
|
|
const yBottom = api.value(0), yTop = api.value(1);
|
|
|
const xBL = api.value(2), xBR = api.value(3), xTL = api.value(4), xTR = api.value(5);
|
|
|
const text = api.value(6), dir = api.value(7);
|
|
|
@@ -221,9 +182,6 @@ export default {
|
|
|
const angle = -Math.atan2(ptTL[1] - ptBL[1], ptTL[0] - ptBL[0]);
|
|
|
const fillColor = dir === 'up' ? this.upWaveColor : this.downWaveColor;
|
|
|
|
|
|
- // 动态字体大小
|
|
|
- const fontSize = Math.round(12 * scale);
|
|
|
-
|
|
|
return {
|
|
|
type: 'group',
|
|
|
children: [
|
|
|
@@ -242,7 +200,7 @@ export default {
|
|
|
style: {
|
|
|
text: text,
|
|
|
fill: this.waveLabelColor,
|
|
|
- font: `bold ${fontSize}px sans-serif`, // 【修复】绿波带上的说明文字按比例缩放
|
|
|
+ font: `bold ${px2echarts(12)}px sans-serif`,
|
|
|
textAlign: 'center',
|
|
|
textVerticalAlign: 'middle'
|
|
|
}
|
|
|
@@ -258,9 +216,9 @@ export default {
|
|
|
if (this.currentViewTime > this.maxDataTime - this.viewWindow) {
|
|
|
this.currentViewTime = 0;
|
|
|
}
|
|
|
- if (this.chart) {
|
|
|
- // 这里仅仅更新 X 轴视图,非常轻量
|
|
|
- this.chart.setOption({
|
|
|
+ if (this.$_chart) {
|
|
|
+ // 注意这里也要换成 $_chart
|
|
|
+ this.$_chart.setOption({
|
|
|
xAxis: { min: this.currentViewTime, max: this.currentViewTime + this.viewWindow }
|
|
|
});
|
|
|
}
|
|
|
@@ -272,13 +230,6 @@ export default {
|
|
|
clearInterval(this.scrollTimer);
|
|
|
this.scrollTimer = null;
|
|
|
}
|
|
|
- },
|
|
|
-
|
|
|
- resize() {
|
|
|
- if (this.chart) {
|
|
|
- this.chart.resize();
|
|
|
- this.updateChart(); // 对外暴露的 resize 方法也加上重新渲染配置
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
};
|