zoukankan      html  css  js  c++  java
  • 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;
                }
              },
            }
          });
    

      

      

      

      

     

  • 相关阅读:
    最大子数组求和并进行条件组合覆盖测试
    Ubuntu 16.04 c++ Google框架单元测试
    The directory '/home/stone/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If execu
    Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/cache/app-info -a -e /usr/bin/appstreamcli; then appstreamcli refresh > /dev/null; fi'
    个人博客作业三:微软小娜APP的案例分析
    补交 作业一
    补交 作业二:个人博客作业内容:需求分析
    嵌入式软件设计第12次实验报告
    嵌入式软件设计第11次实验报告
    嵌入式软件设计第10次实验报告
  • 原文地址:https://www.cnblogs.com/theblogs/p/15594577.html
Copyright © 2011-2022 走看看