在canvas中如何在不使用格子背景圖片的狀況下給圖像畫一個格子背景

以前用canvas繪製圖片的時候,若是圖片有格子背景javascript

須要找一張格子背景的圖片看成canvas的background-image。html

或者使用linear-gradient來畫一個格子背景。java

格子背景圖
格子背景圖

才能實現給圖片添加格子背景的效果:git

圖片一
圖片一

因此就在想,能不能不用圖片來實現格子背景圖,而是使用canvas的強大繪製功能,由於格子背景圖也是數據,經過繪製數據來實現格子背景。github

繪製格子背景

首先 添加一個 canvascanvas

<canvas id="img-box" width="400" height="400"></canvas>
複製代碼

而後使用 createImageData 建立一個新的空白 ImageData 對象。 而後往新的空白 ImageData 對象中設置表明格子背景的數據。數組

在往 ImageData 對象設置數據以前,咱們須要瞭解一下 ImageData 對象的 data 屬性。函數

data屬性的值是一個元素都是數字的數組,並且每一個數字的範圍在 0-255ui

例如這樣的:spa

console.log(imageData.data) // [255, 255, 255, 255, 78, 78, 78, 96 ....]
複製代碼

它們每四個數據合起來表示一個像素點,每一個數據的意義按順序來表述爲 red, green, blue, alpha

接下來就往 ImageData 對象中設置表明格子背景的數據。

let canvas = document.getElementById("img-box");
let ctx = canvas.getContext("2d");

function createTransparentData(width, height) {
  let emptyBox = ctx.createImageData(width, height);
  let emptyBoxData = emptyBox.data

  // 經過 canvas寬高 來遍歷一下 canvas 上的全部像素點
  for (let i = 0; i < height; i++)
    for (let j = 0; j < width; j++) {
        let point = i * width + j << 2 // << 至關於 * 4
        let rgbData = (i >> 2) + (j >> 2) & 1 == 1 ? 204 : 255; // >> 2 至關於 / 4 取整, & 1至關於 % 2
        emptyBoxData[point] = rgbData;
        emptyBoxData[point + 1] = rgbData;
        emptyBoxData[point + 2] = rgbData;
        emptyBoxData[point + 3] = 255
    }
  return emptyBox
}

const transparentData = createTransparentData(canvas.width, canvas.height)

ctx.putImageData(transparentData, 0, 0);

複製代碼

效果圖以下:

格子背景效果圖
格子背景效果圖

融合圖像和背景圖

拿到背景圖的數據數組(transparentData)了,接下來就要把圖像的數據背景圖的數據(transparentData)融合了。

首先須要獲取圖像的數據。因此先往canvas上畫一張圖片,爲了更好的適應各類狀況,這裏特地選了一張多邊形圖片。

<canvas id="img-box" width="400" height="400"></canvas>
<button class="btn" onClick="addTransparent()">添加格子背景</button>
複製代碼
let canvas = document.getElementById("img-box");
let ctx = canvas.getContext("2d");
let img = new Image();
img.src = 'https://raw.githubusercontent.com/Fathands/images/master/canvas-share/pic.png';
img.onload = function() {
  ctx.drawImage(img, 0, 0, 400, 400);
}

複製代碼

此時已經在canvas上把圖像畫上去了:

原始圖
原始圖

而且爲圖片添加格子背景的操做都放在 addTransparent 函數中,而且把 addTransparent 函數綁定到 「添加格子背景」 按鈕上

接下來是重點。須要獲取:

1.原始圖的有效圖像數據,就是下圖中紅色邊框框住的部分

2.有效圖像的起點,寬和高, 起點是以圖像的左上角爲原點的。

以下圖所示:

有效圖像數據圖
有效圖像數據圖

在這以前,咱們還須要獲取原圖中全部表示透明度的數據

function getAllAlphaData() {

  let originImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

  let alphaDataLength = new Uint8Array(originImageData.data.length >> 2)
  let alphaDataList = new Uint8Array(alphaDataLength)

  let originData = new Uint8Array(originImageData.data)
  let len = alphaDataList.length
  for (let i = 0; i < len; i++) {
    alphaDataList[i] = originData[(i << 2) + 3]
  }
  return alphaDataList
}

複製代碼

拿到全部表示透明度的數據後,咱們就能夠來獲取有效圖像數據,有效圖像的起點,寬和高了

function getValidImageInfo(O, i) {

  const alphaDataList = getAllAlphaData()

  let originWidth = canvas.width // 原始圖像的寬
  let originHeight = canvas.height // 原始圖像的高
  let x = 0 // 有顏色的數據部分 最小橫座標
  let y = 0 // 有顏色的數據部分 最小縱座標
  let width = 0 // 從右往左 至有顏色的部分的 橫向距離
  let bottomHeight = 0 // 從底部到 有顏色的部分的 縱向距離
  let flag

  flag = false
  for (let i = 0; i < originHeight; i++) { // 從頭開始 從上到下的掃描
      let line = i * originWidth;
      for (let j = 0; j < originWidth; j++)
          if (alphaDataList[line + j] !== 0) // 不等於0 就是 有透明度
              flag = true; // 設爲true
      if (flag) // 若是這一行有一個透明度不爲0的就跳過,不然最小縱座標+1
          break;
      else
          y++
  }

  flag = false;
  for (let i = originHeight - 1; i >= 0; i--) { // 從底部開始 從下到上的掃描
      let line = i * originWidth;
      for (let j = 0; j < originWidth; j++)
          if (alphaDataList[line + j] !== 0)
              flag = true;
      if (flag) // 若是這一行有一個透明度不爲0的就跳過,不然底部縱向距離+1
          break;
      else
          bottomHeight++
  }

  flag = false;
  for (let j = 0; j < originWidth; j++) { // 從左到右的掃描
      for (let i = 0; i < originHeight; i++)
          if (alphaDataList[i * originWidth + j] !== 0)
              flag = true;
      if (flag)
          break;
      else
          x++
  }

  flag = false;
  for (let j = originWidth - 1; j >= 0; j--) { // 從右到左的掃描
      for (let i = 0; i < originHeight; i++)
          if (alphaDataList[i * originWidth + j] !== 0)
              flag = true;
      if (flag)
          break;
      else
          width++
  }

  return {
    x: x, 
    y: y, 
    width: originWidth - x - width, 
    height: originHeight - y - bottomHeight
  }
}
複製代碼

咱們這張圖片的出來的數據以下:

const validData = getValidImageInfo()
console.log(validData)
// {
// height: 343
// width: 283
// x: 49
// y: 24
// }
複製代碼

即這張圖片的有效顏色的起點爲 [49, 24] ,有效圖像的 寬爲 283,高爲 343

拿到有效顏色圖像的 起點,寬高以後。就能夠獲取了 有效顏色的數據對象

以下:

const validImageData = new Uint8Array(ctx.getImageData(validData.x, validData.y, validData.width, validData.height).data)

複製代碼

拿到 有效顏色的數據對象 以後,接下來就是把 經過createTransparentData獲取的透明顏色的數據對象transparentData 有效顏色的數據對象validImageData 融合到一塊兒。

// .......

function addTransparent() {

  const transparentData = createTransparentData(canvas.width, canvas.height)
  const transparentDataInfo = transparentData.data
  const validData = getValidImageInfo()

  const validImageData = new Uint8Array(ctx.getImageData(validData.x, validData.y, validData.width, validData.height).data)

  let validImageX = validData.x // 有效圖像橫座標
  let validImageXandWidth = validData.x + validData.width // 有效圖像橫座標 加上 寬度
  let validImageY = validData.y // 有效圖像縱座標
  let validImageYandHeight = validData.y + validData.height;  // 有效圖像縱座標 加上 高度

  for (let i = 0; i < canvas.height; i++) // 遍歷高度
    for (let j = 0; j < canvas.width; j++) { // 遍歷寬度
        let numberPoint = i * canvas.width + j // 第幾個點
        let pixelPoint = numberPoint * 4
        let pointerX = j // 橫座標
        let pointerY = i // 縱座標

        // 設置邊界
        if (pointerX < validImageX || pointerX >= validImageXandWidth || pointerY < validImageY || pointerY >= validImageYandHeight) {} else {
            let red = transparentDataInfo[pixelPoint] // 透明背景的r
            let green = transparentDataInfo[pixelPoint + 1] // 透明背景的g
            let blue = transparentDataInfo[pixelPoint + 2] // 透明背景的b
            let alpha = transparentDataInfo[pixelPoint + 3] // 透明背景的a

            // 融合像素點
            let K = ((pointerY - validData.y) * validData.width + (pointerX - validData.x)) * 4;
            alpha = validImageData[K + 3] / 255;
            red = validImageData[K + 0] * alpha + red * (1 - alpha);
            green = validImageData[K + 1] * alpha + green * (1 - alpha);
            blue = validImageData[K + 2] * alpha + blue * (1 - alpha);

            // 從新賦值
            transparentDataInfo[pixelPoint] = red;
            transparentDataInfo[pixelPoint + 1] = green;
            transparentDataInfo[pixelPoint + 2] = blue;
            transparentDataInfo[pixelPoint + 3] = 255
        }
    }
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.putImageData(transparentData, 0, 0);
}

// .......

複製代碼

最後的效果圖:

最後效果圖
最後效果圖
相關文章
相關標籤/搜索