vue2 实现svg图移动缩放、改变图中设备状态

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>

Vue中,可以使用第三方库svg-pan-zoom来实现SVG像的缩放移动svg-pan-zoom是一个独立的JavaScript库,可以轻松与Vue集成。以下是在Vue中使用svg-pan-zoom实现SVG缩放移动的基本步骤: 1. 安装svg-pan-zoom库 可以使用npm或yarn来安装svg-pan-zoom库。 ``` npm install svg-pan-zoom --save ``` 2. 导入svg-pan-zoom库 在Vue组件中导入svg-pan-zoom库。 ```javascript import svgPanZoom from 'svg-pan-zoom'; ``` 3. 在Vue组件中创建SVG元素 使用SVG元素来创建SVG像,并将其添加到Vue组件中。 ```html <template> <div> <svg ref="svg"> <circle cx="50" cy="50" r="40" fill="red"></circle> </svg> </div> </template> ``` 4. 初始化svg-pan-zoom 在Vue组件的mounted钩子函数中初始化svg-pan-zoom库。 ```javascript mounted() { const svg = this.$refs.svg; const panZoom = svgPanZoom(svg, { zoomEnabled: true, controlIconsEnabled: true, fit: true, center: true, minZoom: 0.1, maxZoom: 10, zoomScaleSensitivity: 0.2, panEnabled: true, contain: false, refreshRate: 'auto', beforePan: null, onPan: null, onZoom: null }); } ``` 在上述代码中,我们使用svg-pan-zoom的构造函数来初始化库,并将SVG元素传递给它。通过传递选项参数,我们可以自定义缩放和平移的行为。 5. 在Vue组件中使用svg-pan-zoom 可以使用svg-pan-zoom提供的API方法来实现SVG像的缩放和平移。 ```javascript // 缩放到指定比例 panZoom.zoom(2); // 缩放到原始比例 panZoom.resetZoom(); // 平移到指定位置 panZoom.pan({x: 50, y: 50}); // 平移到原始位置 panZoom.resetPan(); ``` 以上就是在Vue中使用svg-pan-zoom实现SVG缩放移动的基本步骤。注意,这只是一个简单的示例,实际应用中可能需要更复杂的代码来实现更高级的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值