map area canvas 自定义图片热区

<template>
  <div class="wh100 relative" id="elrdID">
    <!-- 图片热区 -->
    <img id="mapImg" style="opacity: 0.4" class="wh100" src="@/assets/images/mapImg.png" border="0" usemap="#planetmap" alt="Planets" />
    <map v-if="state.render" name="planetmap" id="planetmap">
      <area v-for="(item, index) in allPathData.allPath" shape="poly" :coords="coordsHandler(item)" href="test.html" @click.prevent="mapImgClick($event)" />
    </map>

    <!-- 开发环境下,绘制图片热区 -->
    <canvas v-if="state.showCanvas" class="wh100 absolute" id="canvas" style="left: 0; top: 0">你的浏览器还不支持canvas</canvas>
    <div v-if="state.showCanvas" class="absolute canvasBtn">
      <G-button class="ml15" size="small" @click="getCoord">获取点坐标</G-button>
      <G-button class="ml15" size="small" @click="exportCoords">导出所有点坐标</G-button>
      <G-button class="ml15" size="small" @click="startPath">开始绘制多边形</G-button>
      <G-button class="ml15" size="small" @click="closePath">保存本次形状</G-button>
      <G-button class="ml15" size="small" @click="exportJSON">导出全部路径</G-button>
      <G-button class="ml15" size="small" @click="clearPath">清空</G-button>
    </div>
  </div>
</template>
<script setup>
import * as elementResizeDetector from 'element-resize-detector'
import { reactive, watch, nextTick, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import allPathData from './path.json'

const state = reactive({
  canvas: null,
  context: null,
  onpath: [],
  //   downCurrXY: '',
  //   upCurrXY: '',
  showCanvas: false,
  allPath: [],
  currXY: '',
  render: true,
  mapImg: null,
  initMIW: 1,
  initMIH: 1,
  allPathData: {},
  coords: []
})
onMounted(() => {
  let elrd = elementResizeDetector()
  state.mapImg = document.getElementById('mapImg')
  state.initMIW = state.mapImg.width
  state.initMIH = state.mapImg.height
  //   监听元素宽高
  elrd.listenTo(document.getElementById('elrdID'), function (e) {
    if (state.mapImg) {
      state.render = false
      nextTick(() => {
        state.render = true
      })
    }
  })
  // 开发环境初始化canvas
  if (process.env.NODE_ENV == 'development') {
    state.showCanvas = true
    nextTick(() => {
      initCanvas()
    })
  }
})

function initCanvas() {
  state.canvas = document.getElementById('canvas')
  state.canvas.width = state.mapImg.width
  state.canvas.height = state.mapImg.height
  state.context = canvas.getContext('2d')
  state.context.strokeStyle = 'orange'
  state.context.lineWidth = selfAdaption(2)
  // canvas测试坐标↓↓↓↓↓↓
  state.context.beginPath()
  let sdd = '579.9998214190047,299,594.0005714591854,330,713.0069468007215,352,741.0084468810829,274,615.0016965194565,254,578.999767844706,300'
  let arr = sdd.split(',')
  let { w, h } = { ...getProportion() }

  arr.forEach((item, index) => {
    if (index == 0) {
      state.context.moveTo(arr[0] * w, arr[1] * h)
    } else if (index % 2 == 0) {
      state.context.lineTo(item * w, arr[index + 1] * h)
    }
  })
  state.context.stroke()
  // ↑↑↑↑↑↑↑↑↑↑↑
}

function getProportion() {
  let w = state.mapImg ? state.mapImg.width / allPathData.imgInitWH.width : 1
  let h = state.mapImg ? state.mapImg.height / allPathData.imgInitWH.height : 1
  return { w, h }
}
function getCoord() {
  state.canvas.removeEventListener('click', () => {})
  state.canvas.addEventListener('click', e => {
    let rect = canvas.getBoundingClientRect()
    var x = (event.clientX - rect.left) * (state.canvas.width / rect.width)
    var y = (event.clientY - rect.top) * (state.canvas.height / rect.height)
    console.log('当前canvas坐标:', x, y)
    state.coords.push({ x, y })
  })
}

function exportCoords() {
  exportHandler(state.coords)
}

function startPath() {
  state.canvas.removeEventListener('click', () => {})
  ElMessage({
    message: '开始绘制多边形',
    type: 'success'
  })
  state.context.beginPath()
  state.onpath = []
  state.canvas.addEventListener('click', e => {
    let rect = canvas.getBoundingClientRect()
    var x = (event.clientX - rect.left) * (state.canvas.width / rect.width)
    var y = (event.clientY - rect.top) * (state.canvas.height / rect.height)
    console.log('当前canvas坐标:', x, y)
    if (state.currXY == '') {
      state.context.moveTo(x, y)
      state.onpath.push(x, y)
    } else {
      let xy = state.currXY.split('|')
      state.context.moveTo(Number(xy[0]), Number(xy[1]))
      state.context.lineTo(x, y)
      state.context.stroke()
      state.onpath.push(x, y)
    }
    state.currXY = x + '|' + y
  })
}
function closePath() {
  ElMessage({
    message: '已保存本次闭合路径',
    type: 'success'
  })
  state.allPath.push(state.onpath)
  state.currXY = ''
  state.onpath = []
  state.canvas.removeEventListener('click', () => {})
  //   console.log(state.allPath, 'state.allPath')
}
function clearPath() {
  state.allPath = []
  state.onpath = []
  state.context.clearRect(0, 0, state.canvas.width, state.canvas.height)
}
function exportJSON() {
  let params = {
    imgInitWH: {
      width: state.mapImg.width,
      height: state.mapImg.height
    },
    allPath: state.allPath
  }
  exportHandler(params)
}
function exportHandler(params) {
  var data = JSON.stringify(params)
  //encodeURIComponent解决中文乱码
  let uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(data)
  //通过创建a标签实现
  let link = document.createElement('a')
  link.href = uri
  //对下载的文件命名
  link.download = 'hotZonePath.json'
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
}

// 坐标自适应
function coordsHandler(strArr) {
  if (strArr && strArr.length > 0) {
    let numw = state.mapImg ? state.mapImg.width / state.initMIW : 1
    let numh = state.mapImg ? state.mapImg.height / state.initMIH : 1
    let { w, h } = { ...getProportion() }
    let arr = strArr.map((item, index) => {
      if (index % 2 == 0) {
        return Number(item) * w
      } else {
        return Number(item) * h
      }
    })
    return arr.join()
  }
  return ''
}

function mapImgClick(e) {
  let str = `图片热区--当前坐标  ${e.offsetX},${e.offsetY}`
  console.log(str)
}
function selfAdaption(res) {
  let clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
  if (!clientWidth) return
  let fontSize = clientWidth / 1920
  return res * fontSize
}
</script>
<style lang="less" scoped>
.canvasBtn {
  right: 0;
  top: 0;
}
</style>

posted @ 2022-04-01 10:31  Edith6  阅读(390)  评论(0编辑  收藏  举报