• vue+canvas实现简易画板


     

    需求:

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

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

    <div id="app">
          <canvas 
            id="myCanvas" 
            width="600px" 
            height="380px" 
            class="canvas" 
            @mousedown="drawLineMousedown($event)" 
            @mousemove="drawLineMousemove($event)" 
            @mouseup="drawFinish()" 
            @click="select($event)" 
            @dblclick="deleteLine($event)">
          </canvas>
          <input type="button" class="pencil" value="铅笔" @click="addClick($event)">
          <input type="button" class="clearBoard" value="清屏" @click="clearBoard()">
          <input type="button" class="delete" 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文件中的样式出现‘污染’情况(html5 scoped特性)
    Vue.js中滚动条加载更多数据
    本地上传文件至服务器的技巧(linux文件压缩及解压文件)
    ubuntu下apache新建虚拟主机
    laravel5.4+vue+element简单搭建(gulp+laravel Elixir)
    java基础---->Java的格式化输出
    markdown 基本语法(转载)
    谷歌断点调试
    mac 远程连接 云服务器
    棋盘覆盖问题
  • 原文地址:https://www.cnblogs.com/theblogs/p/15594577.html
Copyright © 2020-2023  润新知