1.svg文件的结构
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="667" height="910.266" viewBox="0 0 667 910.266">
<defs>
<pattern id="pattern" preserveAspectRatio="none" width="100%" height="100%" viewBox="0 0 2436 1773">
<!-- 这里放需要展示的svg和需要根据状态替换的svg图 需要转换base64 根据id获取 -->
<image id="img_gg" width="500" height="500" xlink:href="data:image/gif;base64," />
</pattern>
</defs>
<g transform="translate(-150 200) scale(0.4)">
<g>
<!-- svg图底图部分 不用操作 -->
<image width="1712" height="1000" transform="translate(0 5.17) scale(1.47)" xlink:href="data:image/png;base64," />
<!-- 这里是设备 需要绑定code和name -->
<use class="sbBox" code="WZGG000001" name="1#机井灌溉三相水泵" transform="translate(75.99 342.28) scale(1.3)" xlink:href="#img_gg" />
</g>
</g>
</svg>
2. 需要使用一个插件 d3.js
npm install d3
// 在utils文件夹下 创建useSvg.js
import Vue from 'vue'
import * as d3 from 'd3'
import { getToken } from '@/utils/auth'
export const useSvg = (isZoom = false, id = 'svgcanvas') => {
const handlers = new Map()
const getSvg = async(svgUrl, elementValue, renderId, cb, height) => {
const headers = new Headers({
'Authorization': 'Bearer ' + getToken() // 例如,设置认证令牌
})
await fetch(svgUrl, { headers: headers })
.then(res => res.text())
.then(text => {
const parser = new DOMParser()
const resXML = parser.parseFromString(text, 'image/svg+xml')
const svgDomVal = resXML.documentElement.cloneNode(true)
if (!svgDomVal) return
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g')
const gtags = svgDomVal.querySelectorAll('g')
if (gtags.length > 0) {
g.appendChild(gtags[0])
}
svgDomVal.appendChild(g)
elementValue = svgDomVal
svgDomVal.style.width = '100%'
svgDomVal.style.height = height || '80vh'
svgDomVal.style.boxSizing = 'border-box'
g.setAttribute('id', id)
const ProfileComponent = Vue.extend({
render: function(createElement) {
return createElement('div', { class: 'svgcontent', domProps: { innerHTML: new XMLSerializer().serializeToString(svgDomVal) }})
}
})
const vm = new ProfileComponent().$mount()
const elId = document.getElementById(renderId)
if (elId) {
elId.appendChild(vm.$el)
if (isZoom) {
const svg = d3.select(elId)
const svgcanvas = document.getElementById(id)
const zoom = d3.zoom()
.on('zoom', event => {
d3.select(svgcanvas).attr('transform', event.transform)
})
.scaleExtent([0.5, 3])
svg.call(zoom)
}
const adomNodeAll = document.querySelectorAll('[device-id]')
adomNodeAll.forEach(node => {
node.style.cursor = 'pointer'
const handler = (event) => cb(node, event)
handlers.set(node, handler)
node.addEventListener('click', handler)
})
}
})
}
const removeEventListeners = () => {
handlers.forEach((handler, node) => {
node.removeEventListener('click', handler)
})
handlers.clear()
}
return {
getSvg,
beforeDestroy: removeEventListeners // 将removeEventListeners添加到Vue实例的beforeDestroy钩子
}
}
3.详细代码
<template>
<div class="home_itemWrap_map">
<div id="svgTemplateByWindPv" ref="windPvSvgRef" />
</div>
</template>
<script>
import { useSvg } from '@/utils/useSvg'
import * as d3 from 'd3'
export default {
name: 'MapWrap',
components: {},
data() {
return {
dataList: [
{
name: '灌溉设备',
monitorVos: [
{
code: 'WZGG000001',
dlsc: 'GG',
name: '1#机井灌溉三相水泵',
runStatus: 1,
switchStatus: 1
}
]
},
{
name: '充电桩设备',
monitorVos: []
},
{
name: '电采暖设备',
monitorVos: []
}
]
}
},
computed: {},
mounted() {
this.initEchart(this.dataList)
},
methods: {
// 地图 加载
initEchart(dataList) {
const { getSvg } = useSvg(true)
let svgUrlData = require('@/assets/homeSvg/sysSvg.svg')
// let svgUrlData = require('@/assets/homeSvg/yxwind.svg')
getSvg(
svgUrlData,
this.$refs.windPvSvgRef,
'svgTemplateByWindPv',
this.handleClick,
'100%'
).then(() => {
this.setAtributeToEl(dataList)
})
},
// 给元素 增加 事件 移入移出 点击等
setAtributeToEl(obj) {
this.resetZoom()
// console.log('地图 数据', obj)
let newArr = []
obj.map((item) => {
newArr = newArr.concat(item.monitorVos)
})
this.setXlink(newArr)
// console.log('地图 数据 newArr', newArr)
},
// 按照code 替换xlink:href
setXlink(apiData) {
// console.log('地图 数据 newArr', apiData)
const useElements = document.querySelectorAll('use.sbBox')
// 遍历每个<use>元素
useElements.forEach((useElement) => {
// 遍历API返回的数据
apiData.forEach((data) => {
// 如果code匹配
if (useElement.getAttribute('code') === data.code) {
let newHref = ''
if (data.dlsc === 'GG') {
newHref =
data.runStatus && data.switchStatus ? '#img_gg' : '#img_gg_gz'
} else if (data.dlsc === 'DCN') {
newHref =
data.runStatus && data.switchStatus
? '#img_dcn'
: '#img_dcn_gz'
} else if (data.dlsc === 'CDZ') {
newHref =
data.runStatus && data.switchStatus
? '#img_cdz'
: '#img_cdz_gz'
}
// 更新xlink:href属性
useElement.setAttribute('xlink:href', newHref)
}
})
})
},
// 重置 画框
resetZoom() {
// let width = document.getElementById('svgTemplateByWindPv').offsetWidth / 8
const elId = document.getElementById('svgTemplateByWindPv')
const svg = d3.select(elId)
const svgcanvas = document.getElementById('svgcanvas')
const zoom = d3.zoom().on('zoom', function(event) {
d3.select(svgcanvas).attr('transform', event.transform)
})
svg && svg.call(zoom)
svg.call(zoom.transform, d3.zoomIdentity.translate(0, 0).scale(1))
},
// 处理点击事件
handleClick(node) {
console.log('处理点击事件', node)
}
}
}
</script>