以前用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);
}
// .......
複製代碼
最後的效果圖: