原文請見個人github博客>>html
接觸canvas應該是在去年半次元作製品計劃吧,想一想也很久了,不過,那會兒天天累得和狗同樣,週末還要上課。經驗總結也基本都記錄在OneNote,思惟跳躍性的記錄也就不適合做爲博客發佈。
如今來了新公司,一段時間忙,一段時間閒成狗,因此就會再從新總結,寫寫博客。c++
引用自MDN:
<canvas> 是 HTML5 新增的元素,可用於經過使用JavaScript中的腳原本繪製圖形。例如,它能夠用於繪製圖形,製做照片,建立動畫,甚至能夠進行實時視頻處理或渲染。git
我認爲canvas最好的教程就是MDN的,canvas基礎補充請戳這裏>>github
咱們能夠將已經加載好的圖片畫到canvas上。
繪製圖片的api接口:drawImage(image, x, y, width, height)
其中 image 是 image 或者 canvas 對象,x 和 y 是其在目標 canvas 裏的起始座標。width 和 height,這兩個參數用來控制 當像canvas畫入時應該縮放的大小。
drawImage其實還有四個額外參數,通常用來作截圖,這裏由於文章不涉及就不贅述了。有興趣的小夥伴能夠戳這裏>>canvas
getImageData是canvas提供的一個很是強大的接口,它能夠獲取canvas的全部的像素點的值。不過,值的展示形式和通常的rgba或rgb等屬性不一樣,全部的值會被記錄在一個Uint8ClampedArray
的一維數組裏面。api
The Uint8ClampedArray typed array represents an array of 8-bit unsigned integers clamped to 0-255; if you specified a value that is out of the range of [0,255], 0 or 255 will be set instead; if you specify a non-integer, the nearest integer will be set. The contents are initialized to 0.數組
翻譯一下:dom
Uint8ClampedArray類型數組表示一個8-bit無符號整數,即0-255區間;若是你設了一個的值超出了[0, 255]的範圍,他們會被0或者255代替(小於0代替爲0,大於255替代爲255);若是你設了一個非整數,會被替代爲這個小數最接近的整數。全部的初始值爲0;ide
見圖:
若是,canvas將每一個像素點的值按照rgba這樣的順序一個一個的存入Unit8ClampedArray裏面。
所以,數組的長度爲length = canvas.width canvas.height 4。動畫
知道了這種關係,咱們不妨把這個一維數組想象成二維數組,想象它是一個平面圖,如圖:
一個格子表明一個像素
w = 圖像寬度
h = 圖像高度
這樣,咱們能夠很容易獲得點(x, y)在一維數組中對應的位置。咱們想想,點(1, 1)座標對應的是數組下標爲0,點(2, 1)對應的是數組下標4,假設圖像寬度爲2*2,那麼點(1,2)對應下標就是index=((2 - 1)*w + (1 - 1))*4 = 8
。
推導出公式:index = [(y - 1) w + (x - 1) h] * 4
咱們既然已經可以拿到圖像的每個像素點,那麼咱們就能夠隨心所欲啦!
不過客官別急,咱們還有點小知識要補充,避免代碼實現的過程陷入迷茫~
The CanvasRenderingContext2.createImageData() method of the Canvas 2D API creates a new, blank ImageData object with the specified dimensions. All of the pixels in the new object are transparent black.
翻譯(非直譯):
createImageData是在canvas在取渲染上下文爲2D(即
canvas.getContext('2d')
)的時候提供的接口。做用是建立一個新的、空的、特定尺寸的ImageData對象。其中全部的像素點初始都爲黑色透明。
咱們會用到ctx.createImageData(width, height)
這個接口,width和height是新ImageData對象的初始長寬。
ImageData是一個對象,其實咱們在canvas.getImageData拿到的對象就是ImageData,它內部由width,height,Uint8ClampedArray組成,
如:{data: Uint8ClampedArray(958400), width: 400, height: 599}
The CanvasRenderingContext2D.putImageData() method of the Canvas 2D API paints data from the given ImageData object onto the bitmap. If a dirty rectangle is provided, only the pixels from that rectangle are painted. This method is not affected by the canvas transformation matrix.
翻譯:
CanvasRenderingContext2D.putImageData() 方法做爲canvas 2D API 以給定的ImageData對象繪製數據進位圖。若是提供了髒矩形,將只有矩形的像素會被繪製。這個方法不會影響canvas的形變矩陣。
看上去有點迷糊,矩陣都出來了。不過不用擔憂,咱們只關注第一句就好,忽略「若是「以後的文字。
咱們將會用到ctx.putImageData(imagedata, dx, dy)
接口,imageData就是用戶提供的ImageData對象,dx和dy分別是canvas座標系的x點和y點,將從這個(dx,dy)開始輸入數據。
終於迎來了最後的階段!
直接上代碼:
html:
<div class="box"> <img id="img" src="index.png"> </div> <canvas id="canvas"></canvas>
js
draw() function draw() { const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') const img = document.getElementById('img') // 等圖片加載完之後才能獲取圖片信息 img.onload = function() { const style = window.getComputedStyle(img) const w = style.width const h = style.height const ws = w.replace(/px/, '') const hs = h.replace(/px/, '') canvas.width = ws canvas.height = hs ctx.drawImage(img, 0, 0, canvas.width, canvas.height) // 修改顏色準備 const originColor = ctx.getImageData(0, 0, ws, hs) // 保存ImageData裏的Uint8ClampedArray數據 const originColorData = originColor.data // 建立一個空的圖像,這時canvas裏其實已經沒原來的圖像了 const output = ctx.createImageData(ws, hs) const outputData = output.data // 詭異畫風按鈕綁定 const weirdBtn = document.getElementById('weird') weirdBtn.addEventListener('click', function() { // 詭異畫風數據處理(咱們能夠用各類處理方法處理圖像數據,達到想要的效果) weird(originColorData, outputData, ws, hs) ctx.putImageData(output, 0, 0) }) } } // 詭異 function weird(originColorData, outputData, ws, hs) { let random let randomData let index; let r, g, b; // 逐行掃描 for (let y = 1; y <= hs; y++) { // 逐列掃描 for (let x = 1; x <= ws; x++) { // rgb處理 for (let c = 0; c < 3; c++) { random = Math.random(0, 255) * 100 randomData = Math.abs(random - originColorData[index]) index = ((y-1) * ws + (x-1)) * 4 + c outputData[index] = randomData } // alpha處理,咱們就讓透明度一直未1就行了 outputData[index + 3] = 255; } } }
經過對imageData的處理,咱們能夠控制每一個像素點,而後你想處理出不一樣的效果,只須要改寫weird方法就能夠了。我寫了5種濾鏡效果,效果以下gif圖: