canvas實現塗鴉功能

本文主要實現了canvas塗鴉功能,包括塗鴉,前進後退、清除畫布以及畫筆大小修改。

實現思路

主要是監聽鼠標按下、移動、鬆開事件,將鼠標按下的值賦值給moveTo的x和y值,作爲起始位置。在移動事件中,將鼠標距離可視區x和y值賦給lineTo,再將路徑閉合即可。

canvas 塗鴉API及屬性

  • ctx.beginPath(): 開始一條路徑,或重置當前的路徑
  • ctx.moveTo(x,y): 把路徑移動到畫布中的指定點,不創建線條
  • ctx.lineTo(x,y): 添加一個新點,然後創建從該點到畫布中最後指定點的線條(該方法並不會創建線條)
  • ctx.stroke(): 實際地繪製出通過 moveTo() 和 lineTo() 方法定義的路徑。默認顏色是黑色
  • ctx.fillStyle: 設置或返回用於填充繪畫的顏色、漸變或模式
  • ctx.strokeStyle: 設置或返回用於筆觸的顏色、漸變或模式
  • ctx.lineWidth: 設置或返回當前線條的寬度,以像素計
Canvas塗鴉主要用到的方法:moveTo、lineTo

實現過程

塗鴉方法
// 實現撤銷和重做的功能
let canvasHistory = [];
let step = -1;

let canX;//畫布左上角的x座標
let canY;//畫布左上角y座標

/*
* canvas 畫布標籤
* ctx 元素的context
* undo 撤銷元素id
* activeColor 畫筆顏色
*/

// 實現塗鴉功能
let drawing = function(canvas, ctx, undo, activeColor){
 let _ = this;
 canvas.onmousedown = function(ev){
   let domObj = this.getBoundingClientRect();
   canX = domObj.left;//獲取畫布左上角的x座標
   canY = domObj.top;//獲取畫布左上角的y座標

   // 鼠標在canvas元素區域中的座標之後對座標進行了縮放,從而獲得了鼠標相對於 canvas 元素繪圖表面的座標
   // 處理放大縮小時,塗鴉畫不上去問題(踩了好長時間的坑~~~~~)
   let x = (ev.clientX - canX) * (canvas.width / domObj.width);
   let y = (ev.clientY - canY) * (canvas.height / domObj.height);

   // 畫的時候動態修改畫布顏色(默認是黑色畫筆)
   ctx.fillStyle = activeColor;
   ctx.strokeStyle = activeColor;
   
   ctx.beginPath();
   ctx.moveTo(x,y);
   canvas.onmousemove = function(ev){
     let targetX = (ev.clientX - canX) * (canvas.width / domObj.width);
     let targetY = (ev.clientY - canY) * (canvas.height / domObj.height);

     ctx.lineWidth = _.penSize; // 畫筆大小
     ctx.lineTo(targetX,targetY);
     ctx.stroke();
   };
   canvas.onmouseup = function(){
     canvas.onmousemove = null;
     canvas.onmouseup = null;
     canvasDraw();
   };

   // 繪製方法
   function canvasDraw() {
     step++;
     if (step < canvasHistory.length) {
       canvasHistory.length = step; // 截斷數組
     }
     // 添加新的繪製到歷史記錄
     canvasHistory.push(canvas.toDataURL());
     if (step > 0) {
       undo.classList.add('active');
     }
   }
 };
};
撤銷方法(上一步)
/*
* canvasHistory 存儲橡皮擦歷史記錄數組
* eraserStep 步長
* undo 撤銷元素id
* redo 再做元素id
* ctx  獲取這個元素的context——圖像稍後將在此被渲染
*/

let canvasUndo = async function(undo, redo, ctx, canvasId, imgSrc){
  let _ = this;
  if (eraserStep > 0) {
    eraserStep--;
    let canvasPic = new Image();
    canvasPic.src = canvasHistory[eraserStep];
    canvasPic.onload = function () {
      ctx.drawImage(canvasPic, 0, 0);
    };
    undo.classList.add('active');
    redo.classList.add('active');
  } else if(eraserStep === 0){
    eraserStep--;
    canvasDrawImg(canvasId, imgSrc); // 回到畫布初始狀態
    undo.classList.remove('active');
    Message({
      type: 'warning',
      message: '不能再繼續撤銷了',
      duration: 2000
    });
  }
};
重做方法(下一步)
let canvasRedo = async function(redo, ctx, canvas){
  let _ = this;
  if (step < canvasHistory.length - 1) {
    step++;
    let canvasPic = new Image();
    canvasPic.src = canvasHistory[step];
    canvasPic.onload = function () {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(canvasPic, 0, 0);
    };
  } else {
    redo.classList.remove('active');
    Message({
      type: 'warning',
      message: '已經是最新的記錄了',
      duration: 2000
    });
  }
};
撤銷方法
let resetEvent = async function(undo, redo, canvasId){
  let _ = this;
  canvasDrawImg(canvasId, _.matUrl);
  canvasHistory = [];
  undo.classList.remove('active');
  redo.classList.remove('active');
};
canvas上繪製畫布
// canvas上繪製畫布
let canvasDrawImg = function(canvasId, imgSrc){
  let canvas = document.getElementById(canvasId);
  if (canvas) {
    let ctx = canvas.getContext('2d');
    // 畫布大小寬高619.5、400px
    canvas.width = 619.5;
    canvas.height = 495;
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 在給定矩形內清空一個矩形
    let img = new Image();
    img.src = imgSrc;
    img.setAttribute("crossOrigin",'Anonymous');
    img.onload = function () {
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
    }
  }
};

踩過的坑

  1. 塗鴉時鼠標與畫筆位置出現偏移,位置無法吻合,出現這種問題的原因可能是頁面佈局導致,比如塗鴉容器設置了position:relative之類的,重點可以從這塊入手,儘量要用
  2. 塗鴉部分進行放大縮小之後,出現鼠標與畫筆位置偏移,位置無法吻合(排查了好久~~~),原因是塗鴉時鼠標點擊和鼠標移動時獲取的x、y有問題,需要改成如下形式:
    在這裏插入圖片描述
    之前寫的是下面這種:
    在這裏插入圖片描述
  3. 如何保證在寬高固定的canvas上繪製之後圖片大小依舊保持不變,
    即可以將最終得到的base64重新畫在另外一張畫布上,該畫布的寬高設置爲圖片的原始寬高即可,該canvas可以設置在頁面不可見部分

整體就這麼多啦,大家看時有什麼問題歡迎隨時溝通

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章