技术要点:

  1.img 绘制到canvas

  2.绘制完成以后进行拖拽,缩放

  3.使用canvas画图,在绘制的img上进行标记划线,当然可以实现跟过功能,例如百度地图的功能,做单个标记,区域标记等。

  4.实现坐标等转换,标记区域的所有坐标都是基于相对原始图片的坐标,便于其他操作。

实际项目中的开发实现效果截图如下:

点击边界标记,就可以开始左键划线功能,会自动形成闭合区域,点击右键结束划线。同时可以删除当前绘制的区域。

  

区域标记完成以后,就可以进行设备的选择,设备从左侧列表点击以后,放到右侧canvas 的区域,放下后还可以继续拖拽改变其位置,而且保持对应关系。

这些标记区域和标记点都可以基于底图的缩放和拖拽进行位置的等比例渲染,但是保存的坐标始终是基于原图的。

部分效果源码,本地看的话需要给img 图片路径,正确的路径。

  1. <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>标记</title>
    </head>
    <style>
    html, body {
    height: 100%;
    min-height: 100%;
    overflow: hidden;
    }
  2.  
  3. * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    border:none;
    }
  4.  
  5. .canvasWrap {
    width: 100%;
    height: 100%;
    background: #ccc;
    }
    .mark_list{
    position: absolute;
    top: 20px;
    right: 10px;
  6.  
  7. }
    .mark_list li{
    float: left;
    width: 100px;
    border-radius: 4px;
    border: 1px solid #ccc;
    list-style: none;
    line-height: 30px;
    text-align: center;
    color:#333;
    background: #fff;
    cursor: pointer;
    }
    .mark_list li:hover{
    background: #009a8f;
    color:#fff;
    }
    </style>
    <body>
  8.  
  9. <div class="canvasWrap" id="wrap">
    <canvas id="draw">
  10.  
  11. </canvas>
  12.  
  13. <ul class="mark_list">
    <li class="border_mark">标记区域</li>
    <li>标记点位</li>
    </ul>
    </div>
  14.  
  15. </body>
    <script>
    function MarkPoints(Imgurl) {
    this.imgX = 0;//在画布上图片的X偏移量
    this.imgY = 0;//在画布上图片的Y偏移量
    this.imgScale = 1;//图片的缩放比例
    this.rateNum;//图片高度自适应比例,图片等比居中展示在canvas
    this.scaleFlag = 0;//缩放因子,最大缩放9,最小缩放-9
    this.context;
    this.img;
    this.pos={};//每次拖拽前坐标保存
    this.dragFlag=false;//是否可拖拽当前img,默认不能
    this.markFlag=false;//标记区域开启关闭flag
    this.CreatLinepoints = [];//每次创建新区域的坐标集合
    this.allMarkLins = [];//已创建的区域集合,例如[[{x,y},{x,y},{x,y}],[{n,m},{n,m},{n,m}]]目前只需要一个区域,所以数组内部只有一项
    this.getImgLoad(Imgurl);
    this.init();
    document.oncontextmenu = new Function("event.returnValue=false;");
    document.onselectstart = new Function("event.returnValue=false;");
  16.  
  17. }
  18.  
  19. MarkPoints.prototype = {
    getImgLoad: function (Imgurl) {
    var _this = this;
    var wrap = document.getElementById('wrap');
    _this.canvas = document.getElementById('draw');
    _this.context = draw.getContext('2d');
    _this.canvas.height = wrap.offsetHeight;
    _this.canvas.width = wrap.offsetWidth;
    _this.img = new Image();
    _this.img.onload = function () {
    _this.imgX = 0;
    _this.imgY = 0;
    _this.imgScale = 1;
    _this.imgScale=_this.rateNum = _this.canvas.height / _this.img.naturalHeight;
    _this.imgX = (_this.canvas.width - _this.img.naturalWidth * _this.imgScale * _this.rateNum) / 2;//默认进来当前图像剧中显示
    /*画出当前图片*/
    _this.drawImg();
    }
    _this.img.src = imgUrl;
  20.  
  21. },
    getNewPoints: function (points) {
    var _this=this;
    var newPointAry = [];
    for (var i = 0; i < points.length; i++) {
    var obj = {};
    obj.x = points[i].x * _this.imgScale + _this.imgX;
    obj.y = points[i].y * _this.imgScale + _this.imgY;
    if (points[i].hasOwnProperty('mac')) {
    obj.mac = points[i].mac;
    obj.name = points[i].name || '';
    }
  22.  
  23. newPointAry.push(obj);
    }
    return newPointAry;
    },
    drawImg: function () {
    var _this = this;
    _this.context.clearRect(0, 0, _this.canvas.width, _this.canvas.height);
    _this.context.drawImage(_this.img, 0, 0, _this.img.naturalWidth, _this.img.naturalHeight, _this.imgX, _this.imgY, _this.img.naturalWidth * _this.imgScale * _this.rateNum, _this.img.naturalHeight * _this.imgScale * _this.rateNum);
    if ( _this.allMarkLins.length) {
    for (var m = 0; m < _this.allMarkLins.length; m++) {
    var points = _this.allMarkLins[m];
    var newPoints = _this.getNewPoints(points);
    for (var i = 0; i < newPoints.length; i++) {
    var can = _this.context;
    can.beginPath();
    can.arc(newPoints[i].x, newPoints[i].y, 6, 0, Math.PI * 2, true);
    can.fillStyle = "#FF423E";
    can.fill();
    can.strokeStyle = "#FFF";
    can.stroke();//画空心圆
    can.closePath();
  24.  
  25. if (points.length >= 2 && i >= 1) {
    can.strokeStyle = "#FF423E";
    can.lineWidth = 2;
    can.beginPath();
    can.moveTo(newPoints[i - 1].x, newPoints[i - 1].y);
    can.lineTo(newPoints[i].x, newPoints[i].y);
    can.fillStyle = "#ff0000";
    can.fill();
    can.stroke();
    can.closePath();
    }
    }
    if (points.length >= 3) {
    can.strokeStyle = "#FF423E";
    can.lineWidth = 2;
    can.beginPath();
    can.moveTo(newPoints[newPoints.length - 1].x, newPoints[newPoints.length - 1].y);
    can.lineTo(newPoints[0].x, newPoints[0].y);
    can.stroke();
    can.closePath();
    }
    }
    ;
    }
  26.  
  27. },
    init:function () {
    var _this=this;
    _this.canvas.onmousedown=function(event){
    _this.clickDown(event);
    };
    _this.canvas.onmousemove=function(event){
    _this.mouseMove(event)
    };
    _this.canvas.onmouseup=function (event) {
    _this.mouseUp(event);
    }
    _this.canvas.onmousewheel=function (event) {
    _this.onmouseWheel(event);
    }
    document.getElementsByClassName('border_mark')[0].onclick=function () {
    _this.MarkBorderline();
    }
    },
    /*计算当前鼠标位置距离canvas的偏移量*/
    xyToCanvas:function(ele,x,y){
    var _this=this;
    var obj = _this.canvas.getBoundingClientRect();
    return {
    x: x - obj.left,
    y: y - obj.top
    };
    },
    /*鼠标单击事件*/
    clickDown:function(event){
    var _this=this;
    if (_this.markFlag) {
    _this.canvas.style.cursor = "none";
    if (event.button == 2) {
    _this.markFlag = false;
    _this.dragFlag = true;
    _this.canvas.style.cursor = "normal";
    _this.drawImg();
    return;
    } else {
    var posXY = _this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
    posXY.x = (posXY.x - _this.imgX) / _this.imgScale;
    posXY.y = (posXY.y - _this.imgY) / _this.imgScale;
    _this.CreatLinepoints.push(posXY);
    _this.allMarkLins.pop();
    _this.allMarkLins.push(_this.CreatLinepoints);
    _this.drawImg();
    }
    return;
    }
    if ( event.button == 0) {//点击鼠标左键
    _this.dragFlag = true;
    _this.canvas.style.cursor = "move";
    _this.pos = _this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
    }
    },
    /*鼠标移动事件*/
    mouseMove:function(event){
    var _this=this;
    /*拖拽*/
    if (_this.dragFlag) {
    _this.canvas.style.cursor = "move";
    var pos1 = _this.xyToCanvas( _this.canvas, event.clientX, event.clientY);
    var x = pos1.x - _this.pos.x;
    var y = pos1.y - _this.pos.y;
    _this.pos = pos1;
    _this.imgX += x;
    _this.imgY += y;
    _this.drawImg();
    }
    /*边界标记*/
    if (!_this.dragFlag&& _this.markFlag) {
    var pos1 = _this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
  28.  
  29. var can = _this.context;
    can.clearRect(0, 0, _this.canvas.width, _this.canvas.height);
    _this.drawImg();
    /*画跟随圆点*/
    can.beginPath();
    // can.fillText('[' + point.x + ', ' + point.y + ']', 15, 25 * (points.length + 1))
    can.arc(pos1.x, pos1.y, 6, 0, Math.PI * 2, true);
    can.fillStyle = "#FF423E";
    can.fill();
    can.strokeStyle = "#FFF";
    can.stroke();//画空心圆
    can.closePath();
  30.  
  31. /*当前的坐标未结束那么继续 跟随直线*/
    if (!_this.CreatLinepoints.length) return;
    can.strokeStyle = "red";
    can.beginPath();
    can.moveTo(_this.CreatLinepoints[_this.CreatLinepoints.length - 1].x * _this.imgScale + _this.imgX, _this.CreatLinepoints[_this.CreatLinepoints.length - 1].y * _this.imgScale + _this.imgY);
    can.lineTo(pos1.x, pos1.y);
    can.stroke();
    can.closePath();
    }
    },
    /*鼠标放开事件*/
    mouseUp:function (event) {
    var _this=this;
    _this.dragFlag=false;
    if (_this.markFlag) {
    _this.canvas.style.cursor = "none";
    return;
    }
    _this.canvas.style.cursor='default';
  32.  
  33. },
    /*鼠标滚轮事件*/
    onmouseWheel:function(event){
    var _this=this;
    var pos =_this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
    event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltaY * (-40));
    if (event.wheelDelta > 0 && _this.scaleFlag < 9) {
    _this.imgScale *= 2;
    _this.imgX = _this.imgX * 2 - pos.x;
    _this.imgY = _this.imgY * 2 - pos.y;
    _this.scaleFlag += 1;
    }
    if (event.wheelDelta < 0 && _this.scaleFlag > -9) {//缩小
    _this.imgScale *= 0.5;
    _this.imgX = _this.imgX * 0.5 + pos.x * 0.5;
    _this.imgY = _this.imgY * 0.5 + pos.y * 0.5;
    _this.scaleFlag -= 1;
    }
    _this.drawImg();
    },
    /*边界标记*/
    MarkBorderline: function () {
    var _this=this;
    _this.markFlag = true;//切换为true,禁止拖拽,只能标记
    _this.canvas.style.cursor = "none";
    _this.CreatLinepoints = [];
    _this.allMarkLins.push([]);
    },
    /*删除标记区域*/
    deleteArea: function (id) {
    var _this = this;
    _this.allMarkLins.splice(id, 1);
    _this.drawImg();
    },
  34.  
  35. }
    var imgUrl = 'img/girl.jpg';//图片路径
    new MarkPoints(imgUrl);
  36.  
  37. </script>
    </html>

基于canvas绘图 缩放 做标记的更多相关文章

  1. Canvas绘图之平移translate、旋转rotate、缩放scale

    画布操作介绍 画布绘图的环境通过translate(),scale(),rotate(), setTransform()和transform()来改变,它们会对画布的变换矩阵产生影响. 函数 方法 描 ...

  2. HTML5 学习总结(四)——canvas绘图、WebGL、SVG

    一.Canvas canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术.<canvas> 标记和 ...

  3. canvas绘图、WebGL、SVG

    目录 一.Canvas 1.1.创建canvas元素 1.2.画线 1.3.绘制矩形 1.4.绘制圆弧 1.5.绘制图像 1.6.绘制文字 1.7.随机颜色与简单动画 二.WebGL 2.1.HTML ...

  4. HTML5 学习笔记(四)——canvas绘图、WebGL、SVG

    一.Canvas canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术.<canvas> 标记和 ...

  5. WindowsPhone开发—— 使用手绘图片做景区导览地图

    前些日子在做景区App遇到需求,使用手绘图片做一个简易的地图,支持放大缩小平移以及显示景点Mark,安卓上可以使用一个叫做“mAppWidget”的开源库来完成,WP上有人建议用ArcGIS,但是考虑 ...

  6. HTML学习总结(四)【canvas绘图、WebGL、SVG】

    一.Canvas canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术.<canvas> 标记和 ...

  7. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

  8. HTML5 Canvas绘图如何使用

    --------------复制而来--原地址http://jingyan.baidu.com/article/ed15cb1b2e642a1be369813e.html HTML5 Canvas绘图 ...

  9. canvas绘图API详解

    canvas绘图API详解 1.context的状态 矩阵变换属性 当前剪辑区域 context的其他状态属性: strokeStyle, fillStyle, globalAlpha, lineWi ...

随机推荐

  1. graphics基础

  2. Android Weekly Notes Issue #319

    Android Weekly Issue #319 July 22nd, 2018. Android Weekly Issue #319 本期内容包括: MotionLayout加动画; Kotlin ...

  3. debian下烧写stm32f429I discovery裸机程序

    需要安装openocd软件.如果已安装默认的openocd,需要先卸载系统默认的openocd(默认版本是0.5.0,版本太低),然后再安装. 在安装前需要安装libusb库文件: -dev libu ...

  4. HDU3336 Count the string —— KMP next数组

    题目链接:https://vjudge.net/problem/HDU-3336 Count the string Time Limit: 2000/1000 MS (Java/Others)     ...

  5. 在线接口管理工具-eoapi

    为了方便和前端沟通,临时在局域网搭建了一个接口管理工具,查了一些资料都说eoapi不错,那就试了一下: 1.安装 要在服务器或者自己的电脑,准备web环境,Linux可以是Apache/nginx , ...

  6. linux进程学习-进程描述符的存储

    当进程被新建时,内核会给进程分配一个8K的空间作为进程的内核堆栈.同时我们知道task_struct结构体也会被创建,但有意思的是,内核不会给task_struct单独分别空间,而是直接将其扔到8k的 ...

  7. VC6.0实用小技巧

    VC6.0的若干实用小技巧 .检测程序中的括号是否匹配 把光标移动到需要检测的括号(如大括号{}.方括号[].圆括号()和尖括号<>)前面,键入快捷键 “Ctrl+]”.如果括号匹配正确, ...

  8. bzoj 3527: [Zjoi2014]力 快速傅里叶变换 FFT

    题目大意: 给出n个数\(q_i\)定义 \[f_i = \sum_{i<j}{\frac{q_iq_j}{(i-j)^2}} - \sum_{i>j}\frac{q_iq_j}{(i-j ...

  9. Chrome检查更新总失败?安装细则讲解

    现在 Google Chrome 的稳定版都已经发布 68.0 版本了,我机上还是 54, 本想在线更新一下,结果点击菜单项中的“关于 Google Chrome”后,进入的界面提示“更新失败(错误: ...

  10. Floyd(稠密图,记录路径)

    #include<iostream> #include<algorithm> #include<cstdio> #include<cstdlib> #i ...