簡單的canvas圖像處理

本文將闡述如何使用canvas做一些最簡單的圖像處理,如水印添加、灰度濾鏡等。

先用HTML代碼創(chuàng)建一個(gè)canvas標(biāo)簽。并用js對這個(gè)canvas進(jìn)行最簡單的初始化。

<body>
  <canvas id="canvas" width="650" height="328"></canvas>
</body>

<script>
  var canvas = document.getElementById("canvas");
  var context = canvas.getContext("2d");
</script>

以上代碼創(chuàng)建了一個(gè)2D畫布。

接下來我們往canvas中引入一張圖片,需要使用drawImage這個(gè)api,代碼如下。

var image = new Image();    
image.src = "img/img.png";

image.onload = function() {
  context.drawImage(image, 0, 0);
}

注意要在image加載完成后再調(diào)用drawImage函數(shù)。接下來我們可以看到圖片成功顯示在了界面上

圖片1

我們還可以通過調(diào)整drawImage的參數(shù)來讓圖片以不同的尺寸展示。接下來我們詳細(xì)看看drawImage的參數(shù)類型。

根據(jù)MDN的定義,drawImage一共支持如下9個(gè)參數(shù)。
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
看到這里不要懵逼…

接下來我來慢慢解析一下這些參數(shù)的意義。

第一個(gè)參數(shù)image應(yīng)該不需要額外解釋,就是傳入一個(gè)圖片對象。
第2、3、4、5個(gè)參數(shù)其實(shí)可以當(dāng)作一個(gè)整體來看。
代表了從這張圖片的(sx, sy)坐標(biāo)開始,取長為sWidth,sHeight的一部分圖片。
第6、7、8、9個(gè)參數(shù)其實(shí)也可以當(dāng)作一個(gè)整體來看。
代表了將圖片渲染到畫布的(dx, dy)坐標(biāo),在畫布上渲染的實(shí)際長度為dWidth, 實(shí)際高度為dHeight。

比如我可以這樣修改一下之前程序中的drawImage函數(shù)。

context.drawImage(image, 300, 100, 300, 200, 50, 80, 150, 100)
圖片2

得到了如下的效果。

當(dāng)傳遞參數(shù)不為9個(gè)時(shí),有如下情況:

  1. 傳3個(gè)參數(shù)時(shí)。(image, dx, dy),圖片為原尺寸
  2. 傳5個(gè)參數(shù)時(shí)。(image, dx, dy, dWidth, dHeight)

接下來我們來嘗試給圖片添加一個(gè)水印

drawImage這個(gè)方法中,我們不光能給它傳一個(gè)圖像作為參數(shù),其實(shí)我們還可以把一個(gè)canvas作為參數(shù)傳遞給它。

接下來讓我們來思考一下,我們是不是可以嘗試把水印也制作成一個(gè)canvas,之后將這個(gè)水印canvas也渲染到呈現(xiàn)圖片的主canvas當(dāng)中去呢?

讓我們來嘗試一下:

var markCanvas = document.createElement("canvas");
var markContext = markCanvas.getContext('2d');
markCanvas.width = 150;
markCanvas.height = 40;

markContext.font = "20px serif";
markContext.fillStyle = "rgba(255, 255, 255, 0.5)";
markContext.fillText("這是水印", 0, 20);

首先用js創(chuàng)建一個(gè)canvas元素,并給它設(shè)置好寬高。在這個(gè)canvas里,我輸出了一段文字,并設(shè)置文字的顏色和大小等屬性。

接下來在原先的代碼中增加一些

image.onload = function() {
  context.drawImage(image, 0, 0);
  context.drawImage(markCanvas, 500, 250, 150, 40);  //渲染水印canvas
}

在渲染完圖片后,我們緊接著又將水印作為canvas渲染了進(jìn)去。

其實(shí)這種做法有一個(gè)專業(yè)術(shù)語,叫做離屏canvas

getImageData和putImageData實(shí)現(xiàn)濾鏡

如果你需要對圖片進(jìn)行像素級(jí)的操作,那么你很有可能會(huì)用到這兩個(gè)API。

我們通過getImageData可以將圖像轉(zhuǎn)換為一個(gè)數(shù)組。具體用法如下

  var canvas2 = document.getElementById("canvas2");
  var context2 = canvas2.getContext("2d");
  
  //...

  context2.drawImage(image, 0, 0);
  
  //從圖片的(dx, dy)坐標(biāo)開始,獲取長為dWidth,高為dHeight區(qū)域的圖像
  var imageData = context2.getImageData(image, dx, dy, dWidth, dHeight);

我們可以嘗試將imageData里打印出來

圖片3

里面的data是一個(gè)一維數(shù)組,記錄了圖像中所有坐標(biāo)的信息。

但是要注意,在這個(gè)每一個(gè)像素點(diǎn),在這個(gè)一維數(shù)組里都有四位數(shù)來代表它,這四位數(shù)分別代表了r、g、b、a

也就是說,我們要獲取第x個(gè)像素點(diǎn)的信息,應(yīng)該采用這樣的方式:

var imageData = context2.getImageData(image, dx, dy, dWidth, dHeight);
var pxData = imageData.data;

//第x個(gè)像素的rgba信息
var obj = {
  r: pxData[4 * x],
  g: pxData[4 * x + 1],
  b: pxData[4 * x + 2],
  a: pxData[4 * x + 3]
}

接下來我們可以嘗試用以上api來制作一個(gè)灰度濾鏡。

//context先繪執(zhí)正常圖片
context.drawImage(image2, 0, 0);

//先從第一個(gè)context中獲取圖片信息
var imageData = context.getImageData(0, 0, 1000, 667);
var pxData = imageData.data;

//canvas區(qū)域的長為1000,寬為667
for(var i = 0; i < 1000 * 667; i++) {

  //分別獲取rgb的值(a代表透明度,在此處用不上)
  var r = pxData[4 * i];
  var g = pxData[4 * i + 1];
  var b = pxData[4 * i + 2];

  //運(yùn)用圖像學(xué)公式,設(shè)置灰度值
  var grey = r * 0.3 + g * 0.59 + b * 0.11;

  //將rgb的值替換為灰度值
  pxData[4 * i] = grey;
  pxData[4 * i + 1] = grey;
  pxData[4 * i + 2] = grey;
}

//將改變后的數(shù)據(jù)重新展現(xiàn)在canvas上
context.putImageData(imageData, 0, 0, 0, 0, 1000, 667);

效果如下:

圖片4

canvas在retina屏上顯示模糊的bug

要解決這個(gè)問題,你需要將canvas本身的寬高設(shè)置成實(shí)際的兩倍(假定devicePixelRatio為2),然后再將其CSS設(shè)置為原始尺寸即可。而且經(jīng)過這樣的處理,對性能還會(huì)更好。

比如要渲染一個(gè)500px * 300px的圖片,就可以采用如下的方式

canvas.width=1000;
canvas.height=600;
canvas.style.width="500px";
canvas.style.height="300px";

關(guān)于詳細(xì)原因,可以查看這篇文章High DPI Canvas

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 使用圖像 canvas 具有操作圖像的能力??梢杂糜趧?dòng)態(tài)的圖像合成或者作為圖像的背景,以及游戲界面(Sprites...
    閑不住的李先森閱讀 810評論 0 0
  • 最基本的使用創(chuàng)建一個(gè)畫布所有的操作都在畫布的context上面canvas是基于狀態(tài)而不是基于對象的,比如說顏色都...
    親愛的孟良閱讀 1,741評論 0 4
  • 一:canvas簡介 1.1什么是canvas? ①:canvas是HTML5提供的一種新標(biāo)簽 ②:HTML5 ...
    GreenHand1閱讀 4,878評論 2 32
  • 前文 相信接觸過一些canvas的小伙們都應(yīng)該會(huì)有這樣的一句感嘆: canvas 強(qiáng) 真的強(qiáng)!不僅可以靜態(tài)的創(chuàng)建一...
    LinDaiDai_霖呆呆閱讀 2,795評論 0 3
  • 臘月二十七,我居然一整天呆在房間,沒有下樓,也沒有吃飯。 晚上感覺肚子好餓,下樓洗澡,才發(fā)現(xiàn)爸爸還在弄柑果,沒有睡...
    小珍君閱讀 191評論 0 1

友情鏈接更多精彩內(nèi)容