vue+canvas实现简易画板

摘要:
现在,交互是高亮显示要删除的行,然后双击以删除它。

vue+canvas实现简易画板第1张

 

需求:

默认后台返回的数据渲染到画布上,然后用户可以编辑重新画线,并且可以点击要移除的线条进行移除。

现在做的交互是选中需要移除的线条高亮显示,然后双击进行移除。

<div id="app">
      <canvas 
          
          
          
          
        @mousedown="drawLineMousedown($event)" 
        @mousemove="drawLineMousemove($event)" 
        @mouseup="drawFinish()" 
        @click="select($event)" 
        @dblclick="deleteLine($event)">
      </canvas>
      <input type="button"   value="铅笔" @click="addClick($event)">
      <input type="button"   value="清屏" @click="clearBoard()">
      <input type="button"   value="橡皮擦" @click="addClick($event)">
</div>

  

      new Vue({
        el: "#app",
        data: {
          baseline: [
            {
              idx: 0,
              x: 10,
              y: 300,
            },
            {
              idx: 1,
              x: 310,
              y: 300,
            }
          ],
          contour: [
            {
              "idx": 0,
              "x": 10,
              "y": 300
            },
            {
              "idx": 1,
              "x": 70,
              "y": 230
            },
            {
              "idx": 2,
              "x": 130,
              "y": 150
            },
            {
              "idx": 3,
              "x": 190,
              "y": 150
            },
            {
              "idx": 4,
              "x": 250,
              "y": 230
            },
            {
              "idx": 5,
              "x": 310,
              "y": 300
            }
          ],
          junctions:  [
                {
                x: null,
                y: null,
                points: [
                    {
                    idx: 1,
                    x: 111,
                    y: 111
                    },
                    {
                    idx: 2,
                    x: 233,
                    y: 323
                    },
                    {
                    idx: 3,
                    x: 422,
                    y: 435
                    }
                ]
                }
            ],
          temPoints:[],
          isDraw:false,
          idx:0,
          className:"",
          isSelect:""
        },
        mounted:function(){
          this.drawBaseLine(this.baseline);;
          this.drawLine(this.junctions);
        },
        methods: {
          addClick(e){
            this.className = e.target.className;
          },
          //清屏
          clearBoard(){
            const myCanvas = document.getElementById("myCanvas");
            const ctx = myCanvas.getContext("2d");
            let canvasW = ctx.canvas.clientWidth;
            let canvasH = ctx.canvas.clientHeight;
            this.junctions = [];
            ctx.clearRect(0, 0, canvasW, canvasH);
            this.drawBaseLine(this.baseline);
            this.drawContour(this.contour);
          },
          //绘制基准线
          drawBaseLine(baseline){
            const myCanvas = document.getElementById("myCanvas");
            const ctx = myCanvas.getContext("2d");
            if(baseline.length > 0){
              for(let i = 0; i < baseline.length; i++){
                if (i == 0) {
                  ctx.beginPath();
                  ctx.strokeStyle = 'black';
                  ctx.moveTo(baseline[i].x, baseline[i].y);
                } else if (i == baseline.length - 1) {
                  ctx.lineTo(baseline[i].x, baseline[i].y);
                  ctx.stroke();
                } else {
                  ctx.lineTo(baseline[i].x, baseline[i].y);
                }
              }
            }
          },
          //绘制轮廓线
          drawContour(contour){},
          //画线 junctions 多段线集合
          drawLine(junctions) {
            const myCanvas = document.getElementById("myCanvas");
            const ctx = myCanvas.getContext("2d");
            if(junctions.length > 0){
              for(let i = 0; i < junctions.length; i++){
                let points = junctions[i].points;
                for (let j = 0; j < points.length; j++) {
                  if (j == 0) {
                    ctx.beginPath();
                    if(this.isSelect === i){
                      ctx.strokeStyle = 'red';
                    }else{
                      ctx.strokeStyle = 'black';
                    }
                    ctx.moveTo(points[j].x, points[j].y);
                  } else if (j == points.length - 1) {
                    ctx.lineTo(points[j].x, points[j].y);
                    ctx.stroke();
                  } else {
                    ctx.lineTo(points[j].x, points[j].y);
                  }
                }
              }
            }
          },
          //鼠标按下
          drawLineMousedown(e){
            if(this.className == 'pencil'){ 
              let idx = this.idx;
              const myCanvas = document.getElementById("myCanvas");
              const ctx = myCanvas.getContext("2d");
              // debugger;
              let offsetLeft = ctx.canvas.offsetLeft;
              let offsetTop = ctx.canvas.offsetTop;
              let x = e.clientX - offsetLeft,y = e.clientY - offsetTop;
              ctx.beginPath();
              ctx.strokeStyle = 'black';
              ctx.lineTo(x,y);
              this.isDraw = true;
              this.temPoints.push({idx,x,y});
              this.idx ++; 
            }
          },
          //鼠标移动
          //temPoints是存储画线的集合,junctions是默认返回的线+画线的集合
          drawLineMousemove(e){
            if(this.className == 'pencil'){ 
              if(this.isDraw){
                let idx = this.idx ++;
                const myCanvas = document.getElementById("myCanvas");
                const ctx = myCanvas.getContext("2d");
                let offsetLeft = ctx.canvas.offsetLeft;
                let offsetTop = ctx.canvas.offsetTop;
                let x = e.clientX - offsetLeft,y = e.clientY - offsetTop;
                let last = this.temPoints[this.temPoints.length - 1];
                //防止抖动移动问题
                if (Math.sqrt(Math.pow(last.x - x, 2) + Math.pow(last.y - y, 2)) >= 5) {
                  ctx.lineTo(x,y);
                  ctx.stroke();
                  this.temPoints.push({idx,x,y});
                }
              }
            }
          },
          //鼠标抬起
          drawFinish(){
            if(this.className == 'pencil'){ 
              const myCanvas = document.getElementById("myCanvas");
              const ctx = myCanvas.getContext("2d");
              if(this.isDraw){
                ctx.closePath();
                //将数据存入集合,清空原有数据
                let points = this.temPoints;
                this.temPoints = [];
                if (points.length > 1){
                  this.junctions.push({points});
                }
                this.idx = 0;
                this.isDraw = false;
              }
            }
          },
          //删除线
          deleteLine(e){
            if(this.className == 'delete'){
              const myCanvas = document.getElementById("myCanvas");
              const ctx = myCanvas.getContext("2d");
              let canvasW = ctx.canvas.clientWidth;
              let canvasH = ctx.canvas.clientHeight;
              //删除数据
              if ((this.isSelect + '') != '' && this.isSelect >= 0) {
                this.junctions.splice(this.isSelect, 1);
              }
              this.isSelect = "";
              //清空画板重新绘图
              ctx.clearRect(0, 0, canvasW, canvasH);
              this.drawBaseLine(this.baseline);
              this.drawContour(this.contour);
              this.drawLine(this.junctions);
            }
          },
          //选择要删除的线
          select(e){
            if(this.className == 'delete'){
                const myCanvas = document.getElementById("myCanvas");
                const ctx = myCanvas.getContext("2d");
                let canvasW = ctx.canvas.clientWidth;
                let canvasH = ctx.canvas.clientHeight;
                let offsetLeft = ctx.canvas.offsetLeft;
                let offsetTop = ctx.canvas.offsetTop;
                let x = e.clientX - offsetLeft,y = e.clientY - offsetTop;
                let p = {x:x,y:y};
                let result = this.pointInSegments(p,this.junctions);
                if(result.isonLine){ //在线上
                  //修改数据
                  this.isSelect = result.index;
                  //清空画板重新绘图
                  ctx.clearRect(0, 0, canvasW, canvasH);
                  this.drawBaseLine(this.baseline);
                  this.drawContour(this.contour);
                  this.drawLine(this.junctions);
              }
            }
          },
          //判断是否选中线
          pointInSegments(p, junctions) {
            let isonLine = false,index = "";
            if(junctions.length>0){
              for (let i = 0; i < junctions.length; i++ ){
                let points = junctions[i].points;
                if(points.length>1){
                  for (let j = 1; j < points.length; j++ ){
                    let pi = points[j-1];
                    let pj = points[j];
                    if (this.isOnLine(pi, pj, p)) {
                      isonLine = true;
                      index = i;
                      return {isonLine,index};
                    }
                  }
                }
              }
            }
            return {isonLine,index};
          },
          isOnLine(p1, p2, p){
            let minX = Math.min(p1.x, p2.x);    // 较小的X轴坐标值
            let maxX = Math.max(p1.x, p2.x);    // 较大的X轴坐标值
            let minY = Math.min(p1.y, p2.y);    // 较小的Y轴坐标值
            let maxY = Math.max(p1.y, p2.y);    // 较大的Y轴坐标值
            let offset = 10; //偏移量
            if (p1.y === p2.y) {
                // 水平线
                if ((p.x >= minX && p.x <= maxX) && (p.y >= minY - offset && p.y <= maxY + offset)) {
                      return true;
                }
                return false;
            }else if (p1.x === p2.x) {
                // 垂直线
                if ((p.y >= minY && p.y <= maxY) && (p.x >= minX - offset && p.x <= maxX + offset)) {
                  return true;
                }
                return false;
            }else{
              // 斜线 (先判断点是否进入可接受大范围(矩形),然后再根据直线上的交叉点进行小范围比较)
              if ((p.x >= minX && p.x <= maxX) && (p.y >= minY - offset && p.y <= maxY + offset)) {
                //求Y轴坐标
                //方法1:根据tanθ= y/x = y1/x1, 即y = (y1/x1)*x  (该方法有局限性,垂直线(p2.x - p1.x)=0,不能用)
                //var y = ((p2.y - p1.y) / (p2.x - p1.x)) * (px - p1.x);

                //方法2:先求弧度hudu,根据cosθ=x/r, r=x/cosθ,求得r,再根据sinθ=y/r, y=sinθ*r, 求得y 
                let hudu = Math.atan2(p2.y - p1.y, p2.x - p1.x);        // 直线的弧度(倾斜度)
                // 用三角函数计出直线上的交叉点
                let r = (p.x - p1.x) / Math.cos(hudu);                   // 直角三角形的斜边(或理解成圆的半径)
                let y = Math.sin(hudu) * r;                             // Y轴坐标

                let pm = { x: p.x, y: p1.y + y };                         // 直线上的交叉点
                if ((Math.abs(p.x - pm.x) <= offset) && (Math.abs(p.y - pm.y) <= offset)) {
                    return true;                                      
                }
              }
              return false;
            }
          },
        }
      });

  

  

  

  

 

免责声明:文章转载自《vue+canvas实现简易画板》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇转载:c++获取本机IP地址cap文件格式解析下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Delphi操作Excel大全

转自上帝的鱼--专栏 cdsn 个人收藏:Delphi 控制Excel(一) 使用动态创建的方法首先创建 Excel 对象,使用ComObj:var ExcelApp: Variant;ExcelApp := CreateOleObject( 'Excel.Application' );1) 显示当前窗口:ExcelApp.Visible := True;...

js数组中的每一项异步请求

数组中的每一个选项,都进行一次异步请求,然后所有请求完成再操作返回值,Promise.all     getInfo = (param) => {         const { getData } = this.props // getData为接口请求         return new Promise(resolve => {    ...

使用 Vue 开发 scrollbar 滚动条组件

Vue 应该说是很火的一款前端库了,和 React 一样的高热度,今天就来用它写一个轻量的滚动条组件; 知识储备:要开发滚动条组件,需要知道知识点是如何计算滚动条的大小和位置,还有一个问题是如何监听容器大小的改变,然后更新滚动条的位置; 先把样式贴出来: .disable-selection { -webkit-touch-callout: none...

C语言两个libxml2库使用的问题

最近使用libxml2想做点东西,翻看一些example后还是有些疑问,去segmentfault问了下,感谢@pingjiang的热心解答,问题解决,记录如下 (一)如下是一个XML文件,p为根结点 <p> <one>1</one> <two>2</two> <th...

VSCode插件开发全攻略(五)跳转到定义、自动补全、悬停提示

更多文章请戳VSCode插件开发全攻略系列目录导航。 跳转到定义 跳转到定义其实很简单,通过vscode.languages.registerDefinitionProvider注册一个provider,这个provider如果返回了new vscode.Location()就表示当前光标所在单词支持跳转,并且跳转到对应location。 为了示例更加有意...

axios 学习文档

什么是 axios? Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。axios Github 特性 从浏览器中创建 XMLHttpRequests 从 node.js 创建 http 请求 支持 Promise API 拦截请求和响应 转换请求数据和响应数据 取消请求 自动转换 JSON 数据 客户端支持...