[JS筆記]實作 Canvas 繪製框線、drawImage 裁切圖片

在圖片上可以使用 Canvas 繪製框線,疊加在圖片上,讓圖片看起來像是有畫上線段,還可以使用 drawImage()裁切圖片!

這個筆記使用 HTML、CSS、JavaScript 最單純的網頁三元素來進行示範,這樣未來要插入其他地方都可以先方便理解。

Canvas 繪製框線呈現結果

呈現上會分成以下這三張圖:

  • 原圖並於圖片上繪製藍色框線
  • 裁切後使用 canvas 預覽
  • 裁切後使用 base64 格式預覽
最終呈現結果 Canvas 繪製框線
最終呈現結果

其中,base64 格式預覽看起來被切到,純粹是我截圖沒有截到最下面,事實上,下面兩張圖片的預覽結果都是相同的喔!

另外,這張狗狗圖片是從 Unsplash 圖庫找的,附上連結:https://unsplash.com/photos/LATYeZyw88c

或是下圖右鍵進行另存圖片也可以呦!

狗狗圖片
狗狗。圖片來源:Unsplash

資料夾結構

資料夾結構
資料夾結構

Canvas 繪製框線 + 裁切程式碼範例

【HTML:index.html】

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas</title>
    <link rel="stylesheet" href="./css/style.css">
</head>

<body>
    <h2>原圖</h2>
    <div class="oriImg">
        <!-- 原圖 -->
        <img id="cutImg" class="cutImg" src="./img/dog.jpeg" alt="item" />
        <!-- canvas 繪製區 -->
        <div id="drawCanvas" class="drawCanvas">
            <!-- canvas 框新增在這裡 -->
        </div>
    </div>
    <div>
        <h2>canvas 預覽</h2>
        <canvas id="canvasCutBgOper"></canvas>
        <br>
        <h2>base64 格式預覽</h2>
        <img src="" alt="" id="resultBgOper" />
    </div>

    <script src="./js/main.js" type="module"></script>
</body>
</html>

【CSS 檔案:style.css】

.oriImg {
  position: relative;
}
.drawCanvas {
  position: absolute;
  top: 0;
  left: 0;
}

【JS 進入點:main.js】

/* *********

  主檔案
  
********* */

// 圖片載入的 Promise
import { imgLoad } from "./component/imgLoad.js";
import { drawCanvas } from "./component/drawCanvas.js";
import { cropImg } from "./component/cropImg.js";

function init() {
  // 等圖片載入前端
  imgLoad().then((res) => {
    // 繪製 canvas 框
    let rtnObj = drawCanvas();
    // 裁切檔案並於前端顯示
    cropImg(rtnObj);
  });
}

init();

【圖片載入:imgLoad.js】

/* *********

  圖片載入的 Promise
  
********* */
export function imgLoad() {
  return new Promise((resolve, reject) => {
    const bgImg = new Image();
    bgImg.src = document.querySelector("#cutImg").getAttribute("src");
    bgImg.onload = function () {
      console.log("載入完成!");
      resolve("Finish return");
    };
  });
}

【canvas 繪製框線:drawCanvas.js】

這裡要注意的是「繪製框線資料 xywh」是假資料,記得套用上你需要的位置和寬高喔!

/* *********

  繪製 canvas 框線
  
********* */

export function drawCanvas() {
  // 原圖
  const cutImg = document.querySelector("#cutImg");
  // 抓取原圖寬高
  let originalImgW = cutImg.width;
  let originalImgH = cutImg.height;

  // 繪製框線資料 xywh
  const objRegion = [
    0.41749998927116394, 0.596666693687439, 0.5849999785423279,
    0.6966666579246521,
  ];

  // 加入 Canvas 新的節點繪製
  let drawCanvas = document.querySelector("#drawCanvas");
  // 欲插入的節點
  let newCanvasArea = document.createElement("canvas");
  newCanvasArea.setAttribute("id", `myCanvas1`);
  // 插入節點
  drawCanvas.appendChild(newCanvasArea);

  //這一行去抓取目前要繪製的 canvas 標籤
  let currentCanvas = document.getElementById(`myCanvas1`);

  //讓 canvas 的高度和寬度等於你預期的畫布
  currentCanvas.height = originalImgH;
  currentCanvas.width = originalImgW;

  // 找物件計算公式,算出左上和右下的點,乘以圖片寬或高
  let x1 = (objRegion[0] - objRegion[2] / 2) * originalImgW;
  let y1 = (objRegion[1] - objRegion[3] / 2) * originalImgH;
  let x2 = (objRegion[0] + objRegion[2] / 2) * originalImgW;
  let y2 = (objRegion[1] + objRegion[3] / 2) * originalImgH;

  // 算出寬和高
  let calc_w = x2 - x1;
  let calc_h = y2 - y1;
  // 運算後起始點座標 x1, y1, x2, y2:", x1, y1, x2, y2
  // 運算後繪製出該物件(人物)的寬高:", calc_w, calc_h

  // 指定繪圖方式
  let ctx = currentCanvas.getContext("2d");
  // 宣告使用
  ctx.beginPath();
  // 線段寬度
  ctx.lineWidth = 3;
  // 線段顏色
  ctx.strokeStyle = "#3399FF";
  // 使用rect(x,y,w,h)繪製四角形
  ctx.rect(x1, y1, calc_w, calc_h);
  // 繪製相連點的線
  ctx.stroke();

  // 回傳座標和寬高
  let rtnObj = {
    x1,
    y1,
    calc_w,
    calc_h,
  };
  return rtnObj;
}

【裁切檔案並於前端顯示:cropImg.js】

/* *********

  裁切檔案並於前端顯示
  
********* */
export function cropImg(rtnObj) {
  return new Promise((resolve, reject) => {
    // 預計裁切後的 canvas 圖片,顯示在畫面上
    let afterCropCanvas = document.querySelector("#canvasCutBgOper");
    // 預覽的寬高跟裁切的寬高相同
    afterCropCanvas.width = rtnObj.calc_w;
    afterCropCanvas.height = rtnObj.calc_h;

    // 繪製裁切
    let ctx = afterCropCanvas.getContext("2d");
    // 創建 Image
    let drawCropImg = new Image();
    drawCropImg.src = "../img/dog.jpeg";
    // 完成載入時
    drawCropImg.onload = async function () {
      // 使用 drawImage 裁切圖片
      // 參數依序是 drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
      // 參數依序是 drawImage(圖片網址, sx 裁切圖片起點, sy 裁切圖片起點, sWidth 裁切寬, sHeight 裁切高, dx 繪製起點, dy 繪製起點, dWidth 調整後的圖片寬, dHeight 調整後的圖片寬)
      ctx.drawImage(
        drawCropImg,
        rtnObj.x1,
        rtnObj.y1,
        rtnObj.calc_w,
        rtnObj.calc_h,
        0,
        0,
        rtnObj.calc_w,
        rtnObj.calc_h
      );

      // 也可以用 base64 格式顯示在 img 標籤上,作法如下:
      // 裁切後的圖片,轉換成 data: 加上 MIME TYPE 等編碼及資料,這可以直接放在 src 顯示在畫面
      let imgbase64 = afterCropCanvas.toDataURL("image/jpg");
      // 裁切後的圖片顯示在畫面 img 標籤
      document.querySelector("#resultBgOper").src = imgbase64;
      resolve("finish");
    };
  });
}

總結

以上就是 Canvas 繪製框線、裁切檔案的功能實作囉!

完成實作範例
完成 Canvas 實作範例


延伸閱讀:

分享這篇文章

發佈留言