@ -0,0 +1,12 @@ |
|||
|
|||
|
|||
import request from '@/utils/request' |
|||
|
|||
// 地图 情报板接口
|
|||
export function getEnergyBoardAPi(params){ |
|||
return request({ |
|||
url:'/iot/board/list', |
|||
method:'get', |
|||
params |
|||
}) |
|||
} |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,167 @@ |
|||
<template> |
|||
<div class="video-box" v-loading="loading"> |
|||
<video class="demo-video" ref="player" muted autoplay @dblclick="fullScreen"></video> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import flvjs from 'flv.js' |
|||
|
|||
export default { |
|||
props: { |
|||
url: { |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
// id: '1', |
|||
player: null, |
|||
lastDecodedFrames: 0, |
|||
loading: false |
|||
} |
|||
}, |
|||
watch: { |
|||
url: { |
|||
handler() { |
|||
if (this.player) { |
|||
this.player.unload(); |
|||
this.player.destroy(); |
|||
this.player = null; |
|||
} |
|||
this.playVideo() |
|||
}, |
|||
deep: true |
|||
} |
|||
}, |
|||
mounted() { |
|||
if (this.player) { |
|||
//this.player.stream.close(); |
|||
this.player.unload(); |
|||
this.player.destroy(); |
|||
this.player = null; |
|||
} |
|||
this.$nextTick(() => { |
|||
this.playVideo() |
|||
//延迟过大手动跳帧 |
|||
/* setInterval(() => { |
|||
if (this.player.buffered.length) { |
|||
let end = this.player.buffered.end(0);//获取当前buffered值 |
|||
let diff = end - this.player.currentTime;//获取buffered与currentTime的差值 |
|||
console.log('跳帧',diff) |
|||
if (diff >= 0.5) {//如果差值大于等于0.5 手动跳帧 这里可根据自身需求来定 |
|||
this.player.currentTime = this.player.buffered.end(0);//手动跳帧 |
|||
} |
|||
} |
|||
}, 2000); //2000毫秒执行一次*/ |
|||
}) |
|||
}, |
|||
beforeDestroy() { |
|||
this.destroyFlv() |
|||
}, |
|||
methods: { |
|||
fullScreen() { |
|||
if (this.$refs.player.requestFullScreen) { |
|||
this.$refs.player.requestFullScreen() |
|||
} else if (this.$refs.player.mozRequestFullScreen) { |
|||
this.$refs.player.mozRequestFullScreen() |
|||
} else if (this.$refs.player.webkitRequestFullScreen) { |
|||
this.$refs.player.webkitRequestFullScreen() |
|||
} |
|||
}, |
|||
playVideo() { |
|||
const time1 = new Date().getTime(); |
|||
if (flvjs.isSupported()) { |
|||
let video = this.$refs.player; |
|||
if (video) { |
|||
this.loading = true; |
|||
//创建播放器实例 |
|||
this.player = flvjs.createPlayer({ |
|||
type: 'flv', |
|||
isLive: true, |
|||
url: this.url |
|||
},{ |
|||
enableStashBuffer: false, |
|||
autoCleanupSourceBuffer: true, |
|||
lazyLoad:false |
|||
}); |
|||
this.player.attachMediaElement(video); |
|||
try { |
|||
this.player.load(); |
|||
this.player.play().then(() => { |
|||
console.log("首屏打开用时", new Date().getTime() - time1); |
|||
this.loading = false |
|||
}) |
|||
this.listenVideo() |
|||
} catch (error) { |
|||
this.loading = false |
|||
console.log("播放失败", error) |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
// 监听视频流是否断流或者卡顿 |
|||
listenVideo() { |
|||
const that = this; |
|||
this.player.on(flvjs.Events.ERROR, (errorType, errorDetail, errorInfo) => { |
|||
console.log("视频播放出错,错误类型:", errorType,"错误详情:",errorDetail,"错误信息:",errorInfo); |
|||
// 视频出错后销毁重建 |
|||
that.destroyFlv(); |
|||
that.playVideo(); |
|||
} |
|||
); |
|||
// 视频断流 |
|||
this.player.on("statistics_info", function (res) { |
|||
if(that.lastDecodedFrames === 0){ |
|||
that.lastDecodedFrames = res.decodedFrames |
|||
return |
|||
} |
|||
if(that.lastDecodedFrames != res.decodedFrames){ |
|||
that.lastDecodedFrames = res.decodedFrames |
|||
}else{ |
|||
that.lastDecodedFrames = 0 |
|||
//that.destroyFlv() |
|||
//that.playVideo() |
|||
} |
|||
}); |
|||
}, |
|||
//销毁 |
|||
destroyFlv() { |
|||
if (this.player) { |
|||
this.player.pause(); |
|||
this.player.unload(); |
|||
this.player.detachMediaElement(); |
|||
this.player.destroy(); |
|||
this.player = null; |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
} |
|||
</script> |
|||
<style lang="scss"> |
|||
.video-box { |
|||
width: 100%; |
|||
height: 100%; |
|||
|
|||
video { |
|||
width: 100%; |
|||
height: 100%; |
|||
object-fit: fill; |
|||
} |
|||
.demo-video{ |
|||
background-color: #4b4b4b; |
|||
} |
|||
|
|||
//播放按钮 |
|||
/*video::-webkit-media-controls-play-button { |
|||
display: none; |
|||
} |
|||
video::-webkit-media-controls-current-time-display { |
|||
display: none; |
|||
} |
|||
video::-webkit-media-controls-timeline { |
|||
display: none; |
|||
}*/ |
|||
} |
|||
</style> |
@ -0,0 +1,192 @@ |
|||
export const actualLocationList = [ |
|||
{ |
|||
code: "K54", |
|||
name: "殷家林枢纽", |
|||
intervalDistance: 0.0, |
|||
lng: 116.865231, |
|||
lat: 36.57973, |
|||
}, |
|||
{ |
|||
code: "K59", |
|||
name: "大学城收费站", |
|||
intervalDistance: 4.895, |
|||
lng: 116.814343, |
|||
lat: 36.581052, |
|||
}, |
|||
{ |
|||
code: "K72", |
|||
name: "长清收费站", |
|||
lng: 116.865231, |
|||
intervalDistance: 13.558, |
|||
lat: 36.57973, |
|||
}, |
|||
|
|||
{ |
|||
code: "K79", |
|||
name: "长清服务区", |
|||
intervalDistance: 6.163, |
|||
lng: 116.697041, |
|||
lat: 36.428953, |
|||
}, |
|||
{ |
|||
code: "K83", |
|||
name: "松林枢纽", |
|||
intervalDistance: 4.875, |
|||
lng: 116.496503, |
|||
lat: 36.292459, |
|||
}, |
|||
{ |
|||
code: "K86", |
|||
name: "孝里收费站", |
|||
intervalDistance: 2.614, |
|||
lng: 116.638217, |
|||
lat: 36.38667, |
|||
}, |
|||
{ |
|||
code: "K99", |
|||
name: "*平阴北收费站", |
|||
intervalDistance: 13.251, |
|||
lng: 116.447305, |
|||
lat: 36.32884, |
|||
}, |
|||
{ |
|||
code: "K105", |
|||
name: "平阴收费站", |
|||
intervalDistance: 6.154, |
|||
lng: 116.482042, |
|||
lat: 36.276899, |
|||
}, |
|||
{ |
|||
code: "K114", |
|||
name: "平阴停车区", |
|||
intervalDistance: 8.501, |
|||
lng: 116.459654, |
|||
lat: 36.204811, |
|||
}, |
|||
{ |
|||
code: "K117", |
|||
name: "孔村枢纽", |
|||
intervalDistance: 3.473, |
|||
lng: 116.454379, |
|||
lat: 36.173621, |
|||
}, |
|||
{ |
|||
code: "K126", |
|||
name: "平阴南收费站", |
|||
intervalDistance: 8.345, |
|||
lng: 116.445836, |
|||
lat: 36.100732, |
|||
}, |
|||
{ |
|||
code: "K139", |
|||
name: "东平服务区", |
|||
intervalDistance: 13.594, |
|||
lng: 116.417716, |
|||
lat: 35.984511, |
|||
}, |
|||
{ |
|||
code: "K145", |
|||
name: "东平收费站", |
|||
intervalDistance: 6.116, |
|||
lng: 116.414862, |
|||
lat: 35.930073, |
|||
}, |
|||
{ |
|||
code: "K155", |
|||
name: "东平湖枢纽", |
|||
intervalDistance: 9.719, |
|||
lng: 116.381047, |
|||
lat: 35.844376, |
|||
}, |
|||
{ |
|||
code: "K159", |
|||
name: "沙河停车区", |
|||
lng: 116.366287, |
|||
intervalDistance: 3.504, |
|||
lat: 35.823098, |
|||
}, |
|||
{ |
|||
code: "K173", |
|||
name: "*梁山东收费站", |
|||
intervalDistance: 14.794, |
|||
lng: 116.222211, |
|||
lat: 35.684336, |
|||
}, |
|||
{ |
|||
code: "K179", |
|||
name: "梁山收费站", |
|||
intervalDistance: 5.446, |
|||
lng: 116.222163, |
|||
lat: 35.684368, |
|||
}, |
|||
{ |
|||
code: "K186", |
|||
name: "梁山服务区", |
|||
intervalDistance: 6.665, |
|||
lng: 116.177576, |
|||
lat: 35.638136, |
|||
}, |
|||
{ |
|||
code: "K190", |
|||
name: "嘉祥西收费站", |
|||
intervalDistance: 4.434, |
|||
lng: 116.142081, |
|||
lat: 35.610556, |
|||
}, |
|||
{ |
|||
code: "K208", |
|||
name: "王官屯枢纽", |
|||
intervalDistance: 18.484, |
|||
lng: 116.073032, |
|||
lat: 35.462815, |
|||
}, |
|||
]; |
|||
|
|||
export const canvasList = [ |
|||
{ |
|||
name: "殷家林枢纽", |
|||
code: "K54+394", |
|||
line: "G3京台高速", |
|||
isFoot: true, |
|||
distance: 171, |
|||
}, |
|||
{ name: "大学城收费站", code: "K59+289", distance: 50.563 }, |
|||
{ name: "长清收费站", code: "K72+847", distance: 140.05 }, |
|||
{ name: "长清服务区", code: "K79+010", distance: 63.66 }, |
|||
{ |
|||
name: "松竹枢纽", |
|||
code: "K83+885", |
|||
line: "S0101济南绕城高速二环西环段", |
|||
distance: 50.358, |
|||
}, |
|||
{ name: "孝里收费站", code: "K86+499", distance: 27 }, |
|||
{ name: "平阴北收费站", code: "K99+750", distance: 136.88 }, |
|||
{ name: "平阴收费站", code: "K105+904", distance: 63.57 }, |
|||
{ name: "平阴停车区", code: "K114+405", distance: 87.815 }, |
|||
{ |
|||
name: "孔村枢纽", |
|||
code: "K117+878", |
|||
line: "G22青兰高速", |
|||
distance: 35.876, |
|||
}, |
|||
{ name: "平阴南收费站", code: "K126+223", distance: 86.2 }, |
|||
{ name: "东平服务区", code: "K139+817", distance: 140.42 }, |
|||
{ name: "东平收费站", code: "K145+933", distance: 63 }, |
|||
{ |
|||
name: "东平湖枢纽", |
|||
code: "K155+652", |
|||
line: "S30董梁高速", |
|||
distance: 70, |
|||
}, |
|||
{ name: "沙河停车区", code: "K159+156", distance: 70 }, |
|||
{ name: "梁山东收费站", code: "K173+950", distance: 70 }, |
|||
{ name: "梁山收费站", code: "K179+396", distance: 70, icon: "tag4" }, |
|||
{ name: "梁山服务区", code: "K186+061", distance: 70 }, |
|||
{ name: "嘉祥西收费站", code: "K190+495", distance: 70 }, |
|||
{ |
|||
name: "王官屯枢纽", |
|||
code: "K208+979", |
|||
line: "G1511日兰高速", |
|||
distance: 120, |
|||
}, |
|||
]; |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,176 @@ |
|||
<template> |
|||
<div class="bg" id="content"></div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { getScaleByActualData } from './utils' |
|||
|
|||
function setFont(size, bold = 'normal', family = '微软雅黑') { |
|||
return `${bold} ${size}px ${family}` |
|||
} |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
list: getScaleByActualData() |
|||
} |
|||
}, |
|||
|
|||
mounted() { |
|||
this.$nextTick(() => { |
|||
this.mapInit() |
|||
}) |
|||
}, |
|||
methods: { |
|||
async mapInit() { |
|||
var content = document.getElementById('content') |
|||
const width = content.clientWidth |
|||
const height = content.clientHeight |
|||
const zoom = 3 |
|||
const canvas = document.createElement('canvas') |
|||
canvas.style.width = width + 'px' |
|||
canvas.style.height = height + 'px' |
|||
const w = width * zoom |
|||
const h = height * zoom |
|||
canvas.width = w |
|||
canvas.height = h |
|||
content.appendChild(canvas) |
|||
|
|||
const ctx = canvas.getContext('2d') |
|||
ctx.textAlign = 'center' |
|||
ctx.textBaseline = 'middle' |
|||
var imgbg = await this.loadImage(require('./images/bg.png')) |
|||
ctx.drawImage(imgbg, 0, 0, w, h) |
|||
ctx.font = setFont(12 * zoom) |
|||
ctx.fillStyle = '#ffffff' |
|||
ctx.fillText('G2001济南绕城高速', 66 * zoom, h / 2 - 21 * zoom) |
|||
ctx.fillText('G3021德上高速', w - 50 * zoom, h / 2 - 21 * zoom) |
|||
|
|||
ctx.font = setFont(12 * zoom, 'italic 900', 'Arial Black') |
|||
|
|||
ctx.fillStyle = '#ddc85a' |
|||
ctx.fillText('项目起点', 210 * zoom, h / 2 - 50 * zoom) |
|||
ctx.fillText('K55+378.7', 210 * zoom, h / 2 - 33 * zoom) |
|||
ctx.fillText('项目终点', w - 210 * zoom, h / 2 - 50 * zoom) |
|||
ctx.fillText('K208+153.4', w - 210 * zoom, h / 2 - 33 * zoom) |
|||
|
|||
ctx.font = setFont(12 * zoom) |
|||
|
|||
ctx.translate(-25 * zoom, 210 * zoom) |
|||
|
|||
this.drawCongestionAreas(ctx, 1404, 80, zoom) |
|||
// this.drawCongestionAreas(ctx, 1365, 150, zoom) |
|||
|
|||
for (let i of this.list) { |
|||
await this.drawTag(i, zoom, ctx) |
|||
} |
|||
}, |
|||
async drayLine(zoom, text, ctx, isFoot = false) { |
|||
ctx.setLineDash([4 * zoom, 4 * zoom]) |
|||
ctx.strokeStyle = '#37B5D4' |
|||
ctx.lineWidth = 6 |
|||
if (isFoot) { |
|||
ctx.translate(0, 12 * zoom) |
|||
ctx.strokeRect(0, 0, 1, 161 * zoom) |
|||
ctx.translate(0, -12 * zoom) |
|||
} else { |
|||
ctx.strokeRect(0, 0, 1, 135 * zoom) |
|||
} |
|||
|
|||
ctx.fillStyle = '#37B5D4' |
|||
ctx.translate(0, -16 * zoom) |
|||
|
|||
if (isFoot) { |
|||
ctx.fillText(text, 0, 54) |
|||
ctx.translate(0, 226 * zoom) |
|||
ctx.fillText(text, 0, -90) |
|||
} else { |
|||
ctx.fillText(text, 0, 18) |
|||
} |
|||
}, |
|||
async drawTag(info, zoom, ctx) { |
|||
let type = '3' |
|||
if (info.name.indexOf('枢纽') !== -1) { |
|||
type = '0' |
|||
} else if (info.name.indexOf('服务区') !== -1) { |
|||
type = '2' |
|||
} else if (info.name.indexOf('停车区') !== -1) { |
|||
type = '1' |
|||
} |
|||
if (type === '0') { |
|||
ctx.translate(info.distance * zoom, -188 * zoom) |
|||
ctx.font = setFont(12 * zoom, '600') |
|||
await this.drayLine(zoom, info.line, ctx, info.isFoot) |
|||
if (info.isFoot) { |
|||
ctx.translate(-10 * zoom, -180 * zoom) |
|||
} else { |
|||
ctx.translate(-10 * zoom, 35 * zoom) |
|||
} |
|||
if (info.name === '东平湖枢纽') { |
|||
const r = -110 |
|||
ctx.rotate((r * Math.PI) / 180) |
|||
ctx.strokeRect(-15 * zoom, 0, 1, 90 * zoom) |
|||
ctx.rotate((r * Math.PI) / -180) |
|||
ctx.fillText('S33济徐高速', 124 * zoom, -27 * zoom) |
|||
} |
|||
} else { |
|||
ctx.translate(info.distance * zoom, -169 * zoom) |
|||
} |
|||
let y = 42 - (info.name.length - 5) * 10 |
|||
ctx.fillStyle = '#ffffff' |
|||
ctx.font = setFont(12 * zoom) |
|||
var img = await this.loadImage(require(`./images/${info.icon || `tag${type}`}.png`)) |
|||
ctx.drawImage(img, 0, 0, 21 * zoom, 117 * zoom) |
|||
ctx.translate(11.1 * zoom, y * zoom) |
|||
|
|||
for (var i = 0; i < info.name.length; i++) { |
|||
ctx.fillText(info.name[i], 0, i * 15 * zoom) |
|||
} |
|||
|
|||
ctx.translate(0, (169 - y) * zoom) |
|||
ctx.font = setFont(10.5 * zoom) |
|||
ctx.fillText(info.code, 0, -139) |
|||
}, |
|||
loadImage(url) { |
|||
return new Promise((resolve, reject) => { |
|||
const img = new Image() |
|||
img.setAttribute('crossOrigin', 'anonymous') |
|||
img.src = url |
|||
img.onload = () => { |
|||
// 当图像加载完成后进行resolve |
|||
resolve(img) |
|||
} |
|||
img.onerror = () => { |
|||
reject(new Error('图像加载失败')) |
|||
} |
|||
}) |
|||
}, |
|||
drawCongestionAreas(ctx, x, y, zoom) { |
|||
const { e: translateX, f: translateY } = ctx.getTransform() |
|||
x = translateX * -1 + x * zoom |
|||
y = translateY * -1 + y * zoom |
|||
|
|||
const rectWidth = 180 * zoom |
|||
const rectHeight = 8 * zoom |
|||
|
|||
const grad = ctx.createLinearGradient(x, 0, x + rectWidth, 0) |
|||
grad.addColorStop(0, 'rgba(217,50,8,0)') |
|||
grad.addColorStop(0.27, '#D73208') |
|||
grad.addColorStop(0.42, '#D93208') |
|||
grad.addColorStop(0.81, '#F5FF83') |
|||
grad.addColorStop(1, 'rgba(215,50,8,0)') |
|||
|
|||
ctx.fillStyle = grad |
|||
|
|||
ctx.fillRect(x, y, rectWidth, rectHeight) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.bg { |
|||
height: 100%; |
|||
width: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,20 @@ |
|||
import { actualLocationList, canvasList } from "./data.js"; |
|||
|
|||
export function getScaleByActualData() { |
|||
const actualDis = actualLocationList.reduce( |
|||
(count, item) => count + item.intervalDistance, |
|||
0 |
|||
); |
|||
|
|||
const idleCanvasWidth = 1440; |
|||
|
|||
const ratio = idleCanvasWidth / actualDis; |
|||
|
|||
return canvasList.map((item, index) => { |
|||
if (index) { |
|||
item.distance = actualLocationList[index].intervalDistance * ratio; |
|||
} |
|||
|
|||
return item; |
|||
}); |
|||
} |