当前位置: 首页 > 文档资料 > HTML5 在线教程 >

12.20 HTML Canvas绘制图像

优质
小牛编辑
127浏览
2023-12-01

在PhotoShop中有专门的效果工具,Canvas也提供了一些基本的效果接口。这种效果在画布中实现是很简单的,特别是现在你已经掌握了像素的操作方法。

反转颜色

这个效果将反转图像的颜色值。基本方法就是用255减去像素现在的颜色值(150),所得的就是反转后的颜色(255-150=105)。 让我们尝试一些不同的操作,然后看看最后的代码,这里并没有新知识。

 var image = new Image(); 
image.src = "example.jpg"; 
$(image).load(function() { 
context.drawImage(image, 0, 0, 384, 284, 0, 0, 300, 220);
var imageData = context.getImageData(0, 0, canvas.width(), canvas.height()); 
var pixels = imageData.data; 
var numPixels = pixels.length; 
context.clearRect(0, 0, canvas.width(), canvas.height()); 
for (var i = 0; i < numPixels; i++) { 
pixels[i*4] = 255-pixels[i*4]; // Red
pixels[i*4+1] = 255-pixels[i*4+1]; // Green
pixels[i*4+2] = 255-pixels[i*4+2]; // Blue
};
context.putImageData(imageData, 0, 0); 
}); 

前面几行代码创建了一个新的Image对象,然后加载上一节使用的示例图像。等到图像加载完成之后,将图像绘制到画布上,并将包含所有像素的ImageData对象保存到一个变量中。在保存了像素之后,清除画布,开始循环处理原始图像中所有的像素。

在循环中,我们使用与上一节中第一个例子(红色正方形)相同的方法来防问像素,并用255减去当前值。不需要对阿尔法值进行任何处理,因为我们要保留原始图像中的这个值。

最后是将像素绘制回画布,这样就得到一个反转颜色的图像(参见图1)。

反转图像的颜色

灰度

另一个有趣的效果是灰度,这也许是更有用的一种效果。将彩色图像变为灰色(有时候也称为黑白色,但是这种说法并不准确), 除了访问和修改颜色值,实现代码与反转颜色例子中的代码完全相同。

 for (var i = 0; i < numPixels; i++) { 
var average = (pixels[i*4]+pixels[i*4+1]+pixels[i*4+2])/3; 
pixels[i*4] = average; // Red 
pixels[i*4+1] = average; // Green
pixels[i*4+2] = average; // Blue 
}; 

将彩色转换为灰度要求计算出现有颜色值的平均值,即将它们加在一起然后除以颜色个数。 这个平均颜色将作为三种颜色(红、绿和蓝)的值。其结果是将每一种颇色转换为灰度(参见图2)。

将图像转换为灰度

像素化

你是否曾经看到过新闻或文件中人物脸孔被像素化的情况?这是一种强大的特效,它可以将图像变得不可识别,但并不真正删除整个部分。 实际上重新在画布上创建会相对简单一些,只需要将图像按栅格分割,或者对每个片段的颜色取平均值,或者选取每个片段的颜色。我们将使用的代码与上一节马赛克的例子很相似。

var image = new Image(); 
image.src = "example.jpg"; 
$(image).load(function() { 
context.drawImage(image, 0, 0, 384, 284, 0, 0, 300, 220); 
var imageData = context.getImageData(0, 0, canvas.width(), canvas.height()); 
var pixels = imageData.data; 
context.clearRect(0, 0, canvas.width(), canvas.height()); 

    var numTileRows = 20; 
var numTileCols = 20; 
var tileWidth = imageData.width/numTileCols; 
var tileHeight = imageData.height/numTileRows; 

for (var r = 0; r < numTileRows; r++) { };
}); 

循环之前的代码都是一样的。访问图像,等待图像加载,将它绘制到画布中,保存ImageData对象,从画布清除该图像,然后给分割的图像赋值确定块(片段)的数量和尺寸。

这两个循环的工作方式与马赛克的例子是一样的:第一个循环处理每一行块,第二个循环则处理当前行中的每一个块。而新的代码位于循环中,访问颜色值和创建像素化效果。

这里将使用第二种方法来获取像素化效果的颜色值,为每一个块选择一种颜色。最简单的方法是使用块的中心位置像素,将以下代码添加到第二个循环中,就可以得到这个信息:

var x = (c*tileWidth)+(tileWidth/2); 
var y = (r*tileHeight)+(tileHeight/2); 
var pos = (Math.floor(y)*(imageData.width*4))+(Math.floor(x)*4); 

前两行将得到当前块中心像素从0开始表示的(x,y)坐标。这个计算方法与马赛克例子非常相似,先找到块边缘的(x,y)坐标位置,然后加上一半宽度或高度,从而确定中心。 然后将(x,y)坐标传入标准公式,这样就得到CanvasPixelArray中该像素的索引值.但你可能注意到了,(x,y)坐标值在Math对象的floor方法中进行了取整处理。 其原因是,除(x,y)是整数,否则这个返回的索引将是错误的,所以我们使用floor方法将值取整为下一个最小整数(侧如,3.567取整后变成3)。

最后,我们得到了访问颜色值和绘制像素化效果所需要的全部信息。将下面的代码插入到变量pos的声明语句之后。

 var red = pixels[pos]; var green = pixels[pos+1]; 
var blue = pixels[pos+2]; 
context.fillStyle = "rgb("+red+", "+green+", "+blue+")"; 
context.fillRect(x-(tileWidth/2), y-(tileHeight/2), tileWidth, tileHeight); 

这里没有新代码,它只是访问红色、绿色和蓝色值,然后使用这些值来设置fillStyle。最后一步是在块的位置上绘制一个正方形,它是使用所访问的颜色填充的。结果是将示例图像变成一个独特的像素化效果(参见图3)。

使用画布实现图像像素化

我们可以进一步将正方形修改为圆形(参见图4)。

context.beginPath(); 
context.arc(x, y, tileWidth/2, 0, Math.PI*2, false); 
context.closePath(); 
context.fill(); 

现在效果更酷了!

在画布中使用圆形替换正方形实现图像像素化
$(document).ready(function () { var canvas1 = $("#canvas1"); var context1 = canvas1.get(0).getContext("2d"); var canvas2 = $("#canvas2"); var context2 = canvas2.get(0).getContext("2d"); var canvas3 = $("#canvas3"); var context3 = canvas3.get(0).getContext("2d"); var canvas4 = $("#canvas4"); var context4 = canvas4.get(0).getContext("2d"); // Inverting var image = new Image(); image.src = "/themes/guide/images/soyuz-spacecraft.jpg"; $(image).load(function () { context1.drawImage(image, 0, 0, 384, 284, 0, 0, 300, 220); var imageData = context1.getImageData(0, 0, canvas1.width(), canvas1.height()); var pixels = imageData.data; var numPixels = pixels.length; // Clear the image context1.clearRect(0, 0, canvas1.width(), canvas1.height()); // Access and change pixel values for (var i = 0; i < numPixels; i++) { pixels[i * 4] = 255 - pixels[i * 4]; // Red pixels[i * 4 + 1] = 255 - pixels[i * 4 + 1]; // Green pixels[i * 4 + 2] = 255 - pixels[i * 4 + 2]; // Blue }; // Draw image data to the canvas context1.putImageData(imageData, 0, 0); //灰度 context2.drawImage(image, 0, 0, 384, 284, 0, 0, 300, 220); var imageData2 = context2.getImageData(0, 0, canvas2.width(), canvas2.height()); var pixels2 = imageData2.data; var numPixels2 = pixels2.length; // Clear the image context2.clearRect(0, 0, canvas2.width(), canvas2.height()); // Access and change pixel values for (var i = 0; i < numPixels2; i++) { var average = (pixels2[i * 4] + pixels2[i * 4 + 1] + pixels2[i * 4 + 2]) / 3; pixels2[i * 4] = average; // Red pixels2[i * 4 + 1] = average; // Green pixels2[i * 4 + 2] = average; // Blue }; // Draw image data to the canvas context2.putImageData(imageData2, 0, 0); // 像素化 context3.drawImage(image, 0, 0, 384, 284, 0, 0, 300, 220); var imageData3 = context3.getImageData(0, 0, canvas3.width(), canvas3.height()); var pixels3 = imageData3.data; // Clear the image context3.clearRect(0, 0, canvas3.width(), canvas3.height()); // Number of tiles var numTileRows = 20; var numTileCols = 20; // Dimensions of each tile var tileWidth = imageData3.width / numTileCols; var tileHeight = imageData3.height / numTileRows; // Loop through each tile for (var r = 0; r < numTileRows; r++) { for (var c = 0; c < numTileCols; c++) { var x = (c * tileWidth) + (tileWidth / 2); var y = (r * tileHeight) + (tileHeight / 2); // Use Math.floor to convert the pixel positions to integers var pos = (Math.floor(y) * (imageData3.width * 4)) + (Math.floor(x) * 4); var red = pixels3[pos]; var green = pixels3[pos + 1]; var blue = pixels3[pos + 2]; context3.fillStyle = "rgb(" + red + ", " + green + ", " + blue + ")"; // 绘制像素化矩形 context3.fillRect(x - (tileWidth / 2), y - (tileHeight / 2), tileWidth, tileHeight); // 绘制像素化圆点 context4.fillStyle = "rgb(" + red + ", " + green + ", " + blue + ")"; context4.beginPath(); context4.arc(x, y, tileWidth/2, 0, Math.PI*2); context4.closePath(); context4.fill(); }; }; }); });