原生态canvas制做画图小专用工具的踩坑和爬坑

日期:2020-10-09 类型:科技新闻 

关键词:建站平台有哪些,如何建设网站,免费自助建站,如何建立一个网站,网站建站的

近期在写1个相近截图里的简单画图的小专用工具,画线,画矩形框,画圆,能够挑选色调,就像这样

 写的全过程中遇到了1些坑,还好爬出来了,也获得几位巨头的指导,略微触碰了1下zrender,在这里纪录1下。

坑1,绘图全过程的预览

用canvas画线没甚么难题,moveTo和lineTo就行了,来1段编码凑篇幅(:joy:)

电脑鼠标按下,刚开始绘图,纪录原始电脑鼠标的部位
startPaint = (e)=>{
    this.startPaintFlag = true;
    this.paintstartX = e.clientX;
    this.paintstartY = e.clientY;
}

电脑鼠标抬起,终止绘图
stopPaint = (e)=>{
    this.startPaintFlag = false;    
}

painting = (e)=>{
    const {activeColor, activeShape} = this.state;
    
    绘图全过程中
    if(this.startPaintFlag){
      const ctx = this.canvas.getContext("2d");
      ctx.strokeStyle = activeColor;
      ctx.lineWidth = 2;
      ctx.beginPath();
    
      if(activeShape === 'pen'){
        ctx.moveTo(this.paintstartX, this.paintstartY);
        ctx.lineTo(e.clientX, e.clientY);
      }
      if(activeShape === 'circle'){
        const r = Math.sqrt(Math.pow(e.clientX - this.paintstartX, 2) + Math.pow(e.clientY - this.paintstartY, 2));
        ctx.arc(this.paintstartX, this.paintstartY, r, 0, 2*Math.PI)
      }
      if(activeShape === 'rect'){
        ctx.rect(this.paintstartX, this.paintstartY, e.clientX - this.paintstartX, e.clientY - this.paintstartY);
      }
      ctx.stroke();
      
      纪录此次挪动的最终部位,供下1次绘图应用
      this.paintstartX = e.clientX;
      this.paintstartY = e.clientY;
    }
}

可是画矩形框和圆就出事了了,由于截图专用工具画矩形框和圆的情况下,是有预览实际效果的,便是我1边画,我1边能看到我画出的样子是多大,假如依照画线的思路来,会获得这样的1串图型

很好了解,由于mousemove的情况下1直在更改部位和间距。那末假如我1刚开始就纪录下部位,在画矩形框和圆的情况下不更改原始部位呢,那末会获得下列图型

也很好了解,由于沒有擦除,每次绘图的图型都在画布上。那擦除不就行了?不太好,由于在画布上画的并不是仅有1个图型,假如我先画线,再画圆,那末1擦除,以前画的线就没了,让人纠结QAQ

我有过1个思路是,只擦除这个图型內部的样子,例如上面这1堆同舟圆,我只擦除最终绘图的那个圆內部的內容,可是還是不好,1层面內部将会有其他线事前绘图了,1层面图型能够拖拽变大,还可以拖拽变小,那末如何办呢?

在叹了1口空气,喝了1杯水,凝望了1下窗外以后,想出了1个方法,我在画布上再叠1个画布不就行了,我在叠上去的这个画布上便可认为所欲以便并不是咩。

来个示用意,黑色框框是大家展现用的画布,黑色样子表明早已绘图上去的內容,鲜红色框框是大家在画有预览实际效果的图型时所应用的临时性画布,鲜红色样子表明大家电脑鼠标拖拽全过程中绘图的预览內容,每次必须绘图预览內容大家就转化成1个临时性画布,放在高层,无拘无束的绘图,绘图结束(电脑鼠标抬起)将临时性画布消毁,在展现用的画布(黑色框框)上绘图最后的图型

来1段编码协助了解

startPaint = (e) => {
  this.startPaintFlag = true;
  this.paintstartX = e.clientX;
  this.paintstartY = e.clientY;

  画矩形框和圆形时临时性转化成1个canvas
  if (this.state.activeShape !== "pen") {
    this.tempCanvas = document.createElement("canvas");
    this.tempCanvas.width = this.canvas.width;
    this.tempCanvas.height = this.canvas.height;
    
    设定1些精准定位款式
    this.tempCanvas.style.cssText = "position: absolute; top: 0; left: 0; z-index: 0;";
    
    append到必须的器皿元素里
    document.querySelector(".contain").appendChild(this.tempCanvas);
  }
};

stopPaint = (e) => {
  this.startPaintFlag = false;
  const { activeShape, activeColor } = this.state;
  if (activeShape !== "pen") {
    
    坦然器元素里删掉临时性画布
    document.querySelector(".contain").removeChild(this.tempCanvas);
    this.tempCanvas = null;
    
    将lastDrawData纪录的绘图数据信息,绘图到展现用的画布上
    const ctx = this.canvas.getContext("2d");
    ctx.strokeStyle = activeColor;
    ctx.lineWidth = 2;
    ctx.beginPath();
    if (activeShape === "circle") {
      const { x, y, r } = this.lastDrawData;
      ctx.arc(x, y, r, 0, 2 * Math.PI);
    } else {
      const { x, y, width, height } = this.lastDrawData;
      ctx.rect(x, y, width, height);
    }
    ctx.stroke();
    this.lastDrawData = null;
  }
};

painting = (e) => {
  const { activeColor, activeShape } = this.state;

  if (this.startPaintFlag) {
    const ctx = this.canvas.getContext("2d");
    ctx.strokeStyle = activeColor;
    ctx.lineWidth = 2;

    画线的逻辑性没动
    if (activeShape === "pen") {
      ctx.beginPath();
      ctx.moveTo(this.paintstartX, this.paintstartY);
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
      this.paintstartX = e.clientX;
      this.paintstartY = e.clientY;
    } else {

      有预览的图型绘图在临时性画布上
      const tempCtx = this.tempCanvas.getContext("2d");
      tempCtx.strokeStyle = activeColor;
      tempCtx.lineWidth = 2;

      每次绘图前消除画布
      tempCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
      tempCtx.beginPath();
      if (activeShape === "circle") {
        const r = Math.sqrt(
          Math.pow(e.clientX - this.paintstartX, 2) +
            Math.pow(e.clientY - this.paintstartY, 2)
        );
        tempCtx.arc(this.paintstartX, this.paintstartY, r, 0, 2 * Math.PI);

        绘图的数据信息纪录在1个自变量里
        this.lastDrawData = {
          x: this.paintstartX,
          y: this.paintstartY,
          r,
        };
      } else {
        tempCtx.rect(
          this.paintstartX,
          this.paintstartY,
          e.clientX - this.paintstartX,
          e.clientY - this.paintstartY
        );
        this.lastDrawData = {
          x: this.paintstartX,
          y: this.paintstartY,
          width: e.clientX - this.paintstartX,
          height: e.clientY - this.paintstartY,
        };
      }
      tempCtx.stroke();
    }
  }
};

坑2,对话框尺寸变动

假如画着画着,忽然客户将对话框尺寸变了,你说我是维持画布尺寸不会改变呢,還是让画布尺寸伴随着对话框更改而更改,假如对话框变小,那末维持画布尺寸不会改变是没甚么大难题的,可是对话框假如增大,画布地区又是自融入的,那末就迫不得已追随更改了,可是canvas的宽高更改的话,內容是会消除的,那末就遭遇两种挑选:

  1. 用自变量将绘图的每个座标纪录下来,宽高变动后再次绘图;
  2. 将画布的內容立即做为图象(这个描述禁止确,了解意思就行)储存下来

第1种我没试过,但是显而易见假如画的內容多了,应当会闪动1下,第2种,就我所知有两种方式

第1种,用getImageData储存图象,更改尺寸后,用putImageData绘图,putImageData只能剪裁图象,不可以拉伸
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
canvas.width = newWidth;
canvas.height = newHeight;
ctx.putImageData(imgData, 0, 0);

第2种,還是建立1个临时性画布,将图型绘图下来,更改尺寸后,用drawImage绘图,drawImage是能够拉伸图象的
const newCanvas = document.createElement("canvas");
newCanvas.width = canvas.width;
newCanvas.height = canvas.height;
newCanvas.getContext("2d").drawImage(canvas, 0, 0);
canvas.width = newWidth;
canvas.height = newHeight;
ctx.drawImage(newCanvas, 0, 0);

刚开始我选的是drawImage,对话框假如并不是等比放缩(1般不能能等比),图象能够拉伸,可是拉伸1两次图型就变得很模糊不清,因此将会还比不上不更改图象的占比为好,实际還是看情景

到此这篇有关原生态canvas制做画图小专用工具的踩坑和爬坑的文章内容就详细介绍到这了,更多有关canvas画图小专用工具內容请检索脚本制作之家之前的文章内容或再次访问下面的有关文章内容,期待大伙儿之后多多适用脚本制作之家!