2021-03-03 基于canvas操作像素点,获取目标数据

文章更新于 2021-03-03

实现过程主要用到的Canvas API

绘制图像: drawImage()
获取图像数据: getImageData()
重写图像数据: putImageData()
导出图像: toDataURL()

效果图

1.创建主画布,确定大小,位置等
2.创建与主画布相同大小的虚拟图层画布,绘制内容,返回toDataURL()
3.虚拟图层合并渲染到主画布,将图像以此渲染到主画布drawImage()
4.通过鼠标位置,获取虚拟图层像素值,存在像素则进行下一步
5.通过鼠标位置与图层数据源计算,获取目标数据
6.通过鼠标位置+地图div到窗口的顶部(左部)位置,绝对定位绘制提示信息框
7.动画展示信息框

1、获取目标数据的另一种思路是,在构建点、线、面entity数据时,利用gco获取单独entity虚拟图层数据与属性一起归类保存。
2、利用鼠标坐标点数据,索引entity虚拟图层数据,如果存在像素值,则说明命中此数据。

//主函数
drawCanvas() {
        let that = this;
        let canvas = document.getElementById('map');
        canvas.setAttribute('width', canvas.offsetWidth);
        canvas.setAttribute('height', canvas.offsetHeight);
        canvas.className = 'scanning-dashed-circle';
        let context = canvas.getContext('2d');
        await this.drawImage(require('../img/base.png'), canvas);
        let map = {//base地图原始大小
          width: 662,
          height: 366
        };
        let points = this.basePool;//点数据源
        let dataUrl = await this.drawPointLayer(require('../img/location.svg'), canvas, points, map);
        await this.drawImage(dataUrl, canvas);
        let getEntity = this.getLayerEntity(canvas, map, points);
        let getColor = this.getLayerPixel(canvas, dataUrl);
        let interPlay = this.setIntervalShow(points,getEntity);//初始化动画事件
        interPlay.play();//首次自动播放
        canvas.onclick = function(e) {
          let x = e.offsetX === undefined ? e.layerX : e.offsetX;
          let y = e.offsetY === undefined ? e.layerY : e.offsetY;
         if (getColor2(x, y) === '暂无数据') {
            that.mapOverview = false;
            interPlay.stop();//暂停播放
          } else {
            getEntity(x, y);
            setTimeout(interPlay.play(),3000);//延迟启动播放
          }
        };
      }
//往canvas中绘制图片
drawImage(src, canvas, x = 0, y = 0, width = canvas.width, height = canvas.height) {
        return new Promise(resolve => {
          let context = canvas.getContext('2d');
          let img = new Image();
          img.onload = function() {
            resolve(context.drawImage(this, x, y, width, height));
          };
          img.src = src;
        });
 }
// 绘制点图层,返回图层图像数据
async drawPointLayer(src, canvas, ps = [], map) {
        let layer = document.createElement("canvas");
        layer.width = canvas.width;
        layer.height = canvas.height;
        let ctx = layer.getContext("2d");
        let lineWidth = 5;
        for (const item of ps) {
          let x = Math.abs(canvas.width / map.width * item.x);
          let y = Math.abs(canvas.height / map.height * item.y);
          item.x_ = x;
          item.y_ = y;
          await this.drawImage(require('../img/location.svg'), layer, x - 16, y - 32, 32, 40).then(res => {
            //画笔绘制其他信息
            // ctx.lineWidth = 1;
            // ctx.strokeStyle = 'rgba(1,70,194,.5)';
            // ctx.beginPath();
            // ctx.arc(x, y, lineWidth + 1, 0, Math.PI * 2);
            // ctx.closePath();
            // ctx.stroke();
            // ctx.fillStyle = 'rgba(30,199,230,.8)';
            // ctx.beginPath();
            // ctx.arc(x, y, lineWidth, 0, Math.PI * 2);
            // ctx.closePath();
            // ctx.fill();
          });
        }
        return layer.toDataURL();
      }
//通过距离计算,获取点数据
getLayerEntity(canvas, map, data) {
        let that = this;
        let wScale = canvas.width / map.width;
        let hScale = canvas.height / map.height;
        return function(x, y) {
          // 求最短距离
          let dis = 9999999;
          let target = null;
          data.forEach(item => {
            let x_ = Math.abs(wScale * item.x);
            let y_ = Math.abs(hScale * item.y);
            let {x_, y_} = item;
            let dis_ = Math.sqrt(Math.pow((x_ - x), 2) + Math.pow((y_ - y), 2));
            if (dis > dis_ && dis_ < 50) {
              dis = dis_;
              target = item;
            }
          });

          if (target) {
            that.mapOverview = true;//显示信息框
            that.clickPoint = target;//复制点数据
            let map = document.getElementsByClassName('map-container')[0];
            let node = document.getElementsByClassName('map-overview')[0];
            let {t, l} = that.getOffset(map);//计算map div距离浏览器的像素数
            if (node) {
              node.style.top = (t + target.y_ - 40*4) + 'px';
              node.style.left = (l + target.x_ + 32) + 'px';
            }
          } else {
            that.mapOverview = false;//隐藏信息框
          }
          return target;
        };
      }
//虚拟图层中获取像素值
getLayerPixel(canvas, imgsrc, target) {
        target = target || {
          'rgba(0, 0, 0, 0)': '暂无数据'
        };
        let layer = document.createElement("canvas");
        layer.width = canvas.width;
        layer.height = canvas.height;
        let ctx = layer.getContext("2d");
        let img = new Image();
        let pixelDataSet = [];
        img.onload = function() {
          ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
          pixelDataSet = ctx.getImageData(0, 0, layer.width, layer.height).data;
        };
        img.src = imgsrc;
        return function(x, y) {
          let start = 4 * (x + (layer.width * y));
          let output = target[`rgba(${pixelDataSet[start]}, ${pixelDataSet[start + 1]}, ${pixelDataSet[start + 2]}, ${pixelDataSet[start + 3]})`];
          return output || [pixelDataSet[start], pixelDataSet[start + 1], pixelDataSet[start + 2], pixelDataSet[start + 3]].join(',');
        };
      }
//计算div距离浏览器的像素数
getOffset(obj) {
        let t = obj.offsetTop;
        let l = obj.offsetLeft; 
        // 判断是否有父容器,如果存在则累加其边距
        while (obj = obj.offsetParent) {
          t += obj.offsetTop; 
          l += obj.offsetLeft; 
        }
        return { t, l};
      }
//设置动画,轮播信息框
 setIntervalShow(obj, fun) {
        let i = 1;
        let inter = null;
        let play = function () {
          inter = setInterval(() => {
            if (i === obj.length) {
              i = 0;
            }
            let {x_, y_} = obj[i];
            fun.call({}, x_, y_);
            i++;
          }, 3000);
        };
        let stop = function () {
          clearInterval(inter);
        };
        return {play, stop};
      }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容