antvl7绘制地图以及vue项目中使用多线程worker

vue项目中如何使用多线程worker

项目背景:写一个定时器计时,但是和另外一个计时器产生事件循环问题,导致计时不准,所以把计时器放到另外一个线程里,避免两个计时器及渲染冲突

1.安装worker-loader

npm install worker-loader

2. 配置webpack

在vue.config.js文件的defineConfig里加上配置参数

 //多线程worker-loader配置项
   chainWebpack: config => {
      config.module
        .rule('worker-loader')
        .test(/\.worker\.js$/)
        .use({
          loader: 'worker-loader',
          options: {
            inline: true
          }
        })
        .loader('worker-loader')
        .end()

        // 解决:worker 热更新问题
      config.module.rule('js').exclude.add(/\.worker\.js$/);
    }

3.使用

先在src目录下新建workers文件夹,接着在里面新建worker.js,在js文件里添加下面的测试代码:

addEventListener('message', e => {
    const { data } = e
    console.log(data)
    setTimeout(() => {
        return postMessage('线程完成')
    }, 1000)
})
export default {}

之后就可以新建一个vue文件,加入下面代码进行测试:
引入js文件时需要使用worker-loader!@,并且路径是从src目录下开始数层级的,并非你执行的vue文件开始,这就是我一直不能正常跑起来的原因。

<script>
import Worker1 from 'worker-loader!@/workers/worker1'
export default {
	data(){
		return {
			events :[]
		}
	},
	mounted(){
		const worker1 = new Worker1()
        setInterval(()=>{
            worker1.postMessage('开启线程1')
            worker1.onmessage = e => {
                if(e.data.result == 1){
                    that.events = e.data.data
                }
            }
        },10000)
	},
}

项目完整代码:
src => workers => worker.js

/**该多线程脚本用途
*在规定时间内快速走完24小时的时间
*/

/**
 * 
 * @param {*} ste 定时器时间(计算的频率)
 * @param {*} countNum 计数的步伐
 * @param {*} totalTime 计时总时间,目前计时24小时的
 */
const onTimer = (ste,countNum,totalTime) => {
    let count = 0  //计数器
    const total =  totalTime * 60 * 60
    let HH = "00"  //返回去的时针
    let MM = "00"  //返回去的分针
    let SS = "00"  //返回去的秒针
    let timer = setInterval(() => {
        if (count >= total) { // 到了24小时自动停止
        if (timer) {
            clearInterval(timer)
            timer = null
        }
        SS = "00"
        MM = "00"
        HH = "24"
            postMessage({code:0, HH ,MM ,SS })
            return
        }
        count += countNum

        let countS = count % 60
        SS = formatter2(countS)

        let countM = Math.floor(count / 60) % 60
        MM = formatter2(countM)
        
        let countH = Math.floor(count / 60 / 60) % 60
        HH = formatter2(countH)

        postMessage({code:1, HH ,MM ,SS })
        return
    }, ste)
}
/**
 * 
 * @param {*} time 传入时间改成两位数返回
 * @returns 
 */
const formatter2=(time) => {
        let time2 = time+ ""
        let time3 = time2.split(".")[0]
        if (time3 < 10) {
            return '0' + time3
        } else {
            return time3
        }
    }

addEventListener('message', e => {
    const { data } = e
    if (data.code === 1) {
        const {ste,countNum,totalTime} = data
        onTimer(ste,countNum,totalTime)
    }
})
export default {}

主线程文件

<template>
  <div class="container">
    <div class="digitalFlop timeBox">{{ H }}:{{ M }}:{{ S }}</div>
    <div class="date">{{ year }}{{ month }}{{ day }}</div>
    <div class="date opend">当前开机数</div>
    <dv-digital-flop :config="configOpen" class="digitalFlop center"/>
    <div class="date closed">当日开机总数</div>
    <dv-digital-flop :config="configClose" class="digitalFlop bottom"/>

    <div id="map"></div>
  </div>
</template>

<script>
import { newgaode, getTerminal, getTime } from '@/api/table'
import { Scene, PointLayer, Popup } from '@antv/l7'
import { GaodeMap } from '@antv/l7-maps'
import Worker from 'worker-loader!@/workers/worker'

let layer = null //底层点位图实例对象
let layer2 = null //中间层点位图实例对象
let layer3 = null //顶层点位图实例对象
let layer4 = null //顶层点位图实例对象

let param = -1  //发送请求的计数器
const timeStep = 1.5 * 1000  //  每次递归请求的时间间隔,单位毫秒
const ste = 20  //左上角时间的递归频率
export default {
  name: 'DayofvisionVera',
  data() {
    return {
      array: [], //最底层图层的数组
      topArray: [], //最顶层图层的数组
      scene: null, //全局的场景最底层搞得地图的实例对象
      year: '-', //年
      month: '-', //月
      day: '-', //日
      configOpen: {
        //展示开机数量的翻牌器
        style: {
          fontSize: 55,
          fill: '#fff'
        },
        number: [0],
        content: '{nt}'
      },
      configClose: {
        //展示关机数量的翻牌器
        style: {
          fontSize: 55,
          fill: '#fff'
        },
        number: [0],
        content: '{nt}'
      },
      paramData : this.getParam(15),//切割后的参数
      timerOne: null,//递归函数里的定时器
      timeLater:null,//为了等背景地图全部加载完成再进行请求点位数据
      H:"00",//时
      M:"00",//分钟
      S:"00",//秒
    }
  },
  created() {
    if (this.timerOne) {
      clearInterval(this.timerOne)
      this.timerOne = null
    }
    // 获取数据的那一天
    this.getDataTime()
  },
  mounted() {
    //初始化地图
    this.$nextTick(() => {
        this.createMap()
      });
  },
  beforeDestroy: function () {
    //实例销毁前清除于定时器
    if (this.timerOne) {
      clearInterval(this.timerOne)
      this.timerOne = null
    }
  },
  methods: {
    // 初始化地图
    createMap() {
      this.scene = new Scene({
        id: 'map',
        map: new GaodeMap({
          center: [120.7, 29.2], //中心经度加大向右移动,纬度加大向上移动
          // center: [110, 37], // 展示全国的时候用这个
          // zoom: 3.8, // 展示全国的时候用这个
          // minZoom: 3,// 展示全国的时候用这个
          zoom: 7.1, //放大比例
          maxZoom: 15,
          minZoom: 4,
          style: 'dark', //风格
        })
      })

      // 创建
      this.scene.on('loaded', async () => {
        // 获取所有服务器数据
        newgaode().then(res => {
          if (res.code === 200) {
            this.array = res.data
            this.array4 = [...res.data]
            
            // 最底层最暗
            layer = new PointLayer({})
              .source(this.array, { parser: { type: 'json', x: 'l', y: 't' } })
              .shape('simple') //点的形状
              .size('size', size => 4)
              .color('#0F1C3E')//#0F1C3E从未开机(最暗色)  #0E316F开后关机(次暗色) #2273FF开机(最亮)
              .style({ opacity: 0.8, strokeWidth: 0 })

            //中间层次暗层
            layer2 = new PointLayer({ zIndex: 10 })
              .source(this.array, { parser: { type: 'json', x: 'l', y: 't' } })
              .shape('simple')
              .size('size', size => 4)
              .color('s', s => (s === '0' ? '#0E316F' : ''))
              .style({ opacity: 0.8, strokeWidth: 0 })

            // 顶层亮层
            layer3 = new PointLayer({ zIndex: 20 })
              .source(this.array, { parser: { type: 'json', x: 'l', y: 't' } })
              .shape('simple') 
              .size('size', size => 4)
              .color('s', s => (s === '1' ? '#2273FF' : ''))
              .style({ opacity: 0.8, strokeWidth: 0 })

            // 最顶层透明层
            layer4 = new PointLayer({ zIndex: 30 })
              .source(this.array4, { parser: { type: 'json', x: 'l', y: 't' } })
              .shape('simple')
              .size('size', size => size ? 13 : 4 )
              .color('color', color => (color ? '#2273FF' : 'rgba(0,0,0,0)'))
              .style({ opacity: 0.8, strokeWidth: 0 })

            this.scene.addLayer(layer)
            this.scene.addLayer(layer2)
            this.scene.addLayer(layer3)
            this.scene.addLayer(layer4)

            this.timeLater = setTimeout(()=>{
              // 多线程计时器,主线程只留一个定时器
              this.onManyThread()
              // 开启递归请求数据
              this.getTerminalData()
              // 绑定鼠标移入事件
              this.mouseEnter()
              clearInterval(this.timeLater)
              this.timeLater = null
            },2000)
          }
        })
      })
    },
    // 递归请求所有时间点的数据
    getTerminalData(){
      this.timerOne = setTimeout(()=>{
        param += 1
        if (this.paramData[param]) {
          this.getTerminalData()
          this.handlerList(this.paramData[param])//用来请求接口
        }else{
          clearInterval(this.timerOne)
          this.timerOne = null
        }
      },timeStep)
    },
    // 请求所有点位数据
    handlerList(time){
      getTerminal({time}).then(res => {
        if (res.code == 200) {
          const data = res.data
          if(!data[0]) retun
          const resKey = Object.keys(data[0])[0]
          const cur = data[1][resKey]
          const tod = data[2][resKey]
          
          const bigArray=data[0][resKey]
          layer2.setData(bigArray)
          layer3.setData(bigArray)
          // 更新左上角开机数量
          this.powerOnNum(cur,tod)
        }
      })
    },
    /**
     * 开机数量更新
     * @param {*} current 当前开机数量
     * @param {*} today 当日开机数量
     */
    powerOnNum(current,today){
      //更新当前开机点位的数量
      let opened = current
      this.configOpen.number = [Number(opened)]  
      this.configOpen = { ...this.configOpen }

      //更新当前关机点位的数量
      let cumulative = today
      this.configClose.number = [Number(cumulative)]
      this.configClose = { ...this.configClose }
    },
    /**
     * 计算参数的方法
     * @param {*} spacing 间隔多少分钟,单位分钟
     */
    getParam(spacing){
      if(!spacing) []
      let timeParam = []
      let count = -spacing
      let count2 = 0
      let interval = spacing
      let length = Math.ceil(24*60 / interval)
      
      for (let index = 0; index < length; index++) { 
        count += interval
        let MM = count % 60
        if (MM<10) {
          MM = "0" + count % 60
        }else{
          MM = count % 60
        }

        let HH  = Math.floor(count / 60) % 60
        if (HH<10) {
          HH = "0"+Math.floor(count / 60) % 60
        }else{
          HH = Math.floor(count / 60) % 60 == 24 ? "00" : Math.floor(count / 60) % 60
        }
        let startTime = `${HH}:${MM}`

        count2 += interval
        let MM2 = count2 % 60
        if (MM2<10) {
          MM2 = "0"+count2 % 60
        }else{
          MM2 = count2 % 60
        }
        let HH2  = Math.floor(count2 / 60) % 60
        if (HH2<10) {
          HH2 = "0"+Math.floor(count2 / 60) % 60
        }else{ 
            HH2 = Math.floor(count2 / 60) % 60 == 24 ? "00" : Math.floor(count2 / 60) % 60
        }
        let endTime = `${HH2}:${MM2}`
        timeParam.push(`${startTime}-${endTime}`)
      }
      return timeParam
    },
    // 获取数据的那一天时间
    getDataTime(){
      getTime().then(res=>{
        if(res.code == 200){
          let date = res.data.split("-")
          this.year = date[0]
          this.month = date[1]
          this.day = date[2]
        }
      })
    },
    // 新的绑定鼠标事件
    mouseEnter(){
      layer4.on('mousemove', e => {
        const popup = new Popup({
          offsets: [0, 0], //偏移量
          closeButton: false, //是否展示右上角关闭x
          maxWidth: '500px', //弹窗最大宽度
          closeOnClick: true //点击地图关闭弹窗
        })
          .setLnglat(e.lngLat)
          .setHTML(
            `<span style="user-select:text;cursor:default;">终端名称: ${e.feature.d}</span><br><span style="user-select:text;cursor:default;">经纬度: ${e.feature.l}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${e.feature.t}</span>`
          )
        this.scene.addPopup(popup)

        //鼠标划过变大点位
        this.array4.forEach(item => {
          item.color = item.v === e.feature.v 
          item.size = item.v === e.feature.v
        })
        layer4.setData(this.array)
      })
    },
    // 多线程计时器
    onManyThread(){
      const worker = new Worker()
      const totalTime = 24//展示24小时的计时
      const duration = timeStep * this.paramData.length
      const countNum = Number((totalTime * 60 * 60 * ste / duration).toFixed(2))//countNum步伐

      worker.postMessage({code:1,ste,countNum,totalTime})//code是否开始,ste频率
      worker.onmessage = e => {
        const { data } = e
        if (data.code === 1) {
          const {HH,MM,SS} = data
          this.H = HH
          this.M = MM
          this.S = SS
        }else{
          worker.terminate()
        } 
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.container {
  margin: -10px -12px;
  .digitalFlop {
    width: 160px;
    height: 50px;
    position: absolute;
    left: 30px;
    // top: 30px;
    top: 90px;
    z-index: 99;
  }
  .timeBox{
    color: #fff;
    font-size:58px;
    top: 83px;
    left: 95px;
    width: 250px;
    height: 58px;
    line-height: 58px; 
  }
  .date {
    // background-color:green;
    height: 24px;
    line-height: 24px;
    font-size: 22px;
    color: #fff;
    position: absolute;
    left: 110px;
    // top: 43px;
    top: 150px;
    z-index: 99;
  }
  .opend {
    left: 150px;
    top: 305px;
  }
  .closed {
    width: 150px;
    text-align: center;
    left: 135px;
    top: 458px;
  }
  .center {
    width: 150px;
    // top: 90px;
    // left: 365px;
    top: 245px;
    left: 130px;
  }
  .bottom {
    width: 150px;
    // top: 90px;
    // left: 535px;
    top: 398px;
    left: 130px;
  }
  #map {
    height: 98%;
    width: 100%;
    justify-content: center;
    ::v-deep.l7-popup-tip {
      border-top-color: #225cff !important;
    }
    ::v-deep.l7-popup-content {
      background-color: rgba(0, 0, 0, 0.7) !important;
      border: 1px solid #225cff;
      color: #226eff;
      font-size: 600;
    }
    ::v-deep.l7-control-logo {
      display: none;
    }
    ::v-deep.amap-copyright {
      display: none !important;
      height: 0 !important;
      font-size: 0px !important;
    }
  }
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值