基础知识:

  • drawImage(image, x, y, [width], [height])——绘制图像,参数image可以是HTML img元素、HTML5 canvas元素或video元素。
  • getImageData(x, y, width, height)——画布中访问像素数据,返回一个2D渲染上下文ImageData对象,它包含3个属性:width、height、data(存储全部像素信息CanvasPixelArray)。
  • FileReader——FileReader对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File Blob 对象指定要读取的文件或数据。
  • readAsDataURL()——开始读取指定的Blob对象或File对象中的内容。当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之。同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容。

实现方法:

  • 实例化FileReader对象,readAsDataURL读取图像内容
  • 通过result属性获取data集合,并赋给图像src属性
  • drawImage绘制图像到canvas上显示
  • getImageData获取canvas上的图像数据
  • 根据块的行数和列数遍历图像的每一块
    • 计算当前块的中心位置坐标
    • 计算CanvasPixelArray中块的中心像素索引值
    • 根据索引值获取像素集合中的像素颜色
    • 绘制圆形路径并填充

实现步骤:

基础性工作

1
2
3
4
5
6
7
<div><h2>Canvas-image-pixelation</h2>
<input type = "file" id = "file" name = "file"><br />小块尺寸
<input type="text" id = "size" name = "text" style="width:62px; margin-bottom: 5px">(2-25)
<button id="sizeOK">确定</button>
<button id="export">导出</button><br /><span></span><br /><!--换行为了避免按钮位置移动而触发失败-->
<canvas id="myCanvas" width="600" height=""></canvas>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext("2d");
var image = new Image(); //实例化一个Image对象
image.originCrossing = "anonymous"; //允许由从外部来源加载图像,并在canvas中使用
$('#file').change(function() {
//获取图像数据
});
image.onload=function(){
setImageWidth(600); //以宽度600为准,根据图像调整canvas高度
context.drawImage(image, 0, 0, canvas.width, canvas.height); //绘制图像
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
var pixels = imageData.data; // CanvasPixelArray
$('#sizeOK').click(function(){
var size = $('#size').val(); // 每个块的尺寸
// 原始图像的马赛克块个数
var numTileRows = Math.floor(image.height/size), //floor可能会舍弃边界右,下的一点
numTileCols = Math.floor(image.width/size);
context.clearRect(0, 0, $('canvas').width(), $('canvas').height()); // 从画布清除图像
//根据块数重新设置canvas宽高(此时width<=600)
setImageWidth(numTileCols*size, numTileRows*size, multiW, multiH);
// 实际的马赛克块个数
var numRows = canvas.height/size +1,
numCols = canvas.width/size +1;
for(var r=0; r<numRows; r++) { // 块行
for(var c=0; c<numCols; c++) { // 块列
//像素化绘制……
}
}
});
}

获取图像数据

1
2
3
4
5
6
7
8
9
$('#file').change(function() {
var imgFile = new FileReader(); //实例化一个FileReader对象
imgFile.readAsDataURL(this.files[0]); //读取文件
imgFile.onload = function () {
var imgBase64 = this.result; //读取base64数据
image.src = imgBase64;
}
$('span').text(' 加载中,请稍后...');
});

根据图像宽高比来改变canvas高度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function setImageWidth(w,h,mW=1,mH=1){ //mW,mH表示原图与canvas画布的宽高比
if(image.width>600){
if(mW > 1) {
canvas.width = w/mW;
canvas.height = h/mH;
} else {
canvas.width =600;
canvas.height = image.height / (image.width/600);
}
} else {
canvas.width = image.width;
canvas.height = image.height;
}
}

像素化绘制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
for(var r=0; r<numRows; r++) { // 块行
for(var c=0; c<numCols; c++) { // 块列
// 当前块的中心位置元素从0开始表示的(x,y)坐标
var x = c * size + size / 2,
y = r * size + size / 2;
// 绘制最后一列后面还有空余时,取空余宽度中间位置的x
if(c == Math.floor(canvas.width/size)){
x = c * size + (canvas.width - c * size)/2;
}
// 绘制最后一行下面还有空余时,取空余高度中间位置的y
if(r == Math.floor(canvas.height/size)){
y = r * size + (canvas.height - r * size)/2;
}
// CanvasPixelArray中该像素的索引值
var pos = (Math.floor(y-0.5)* (imageData.width * 4)) + (Math.floor(x-0.5) * 4);
var red = pixels[pos], // r
green = pixels[pos+1], // g
blue = pixels[pos+2]; // b
// 设置填充颜色
context.fillStyle = "rgb(" +red+ "," +green+ "," +blue+ ")";
//context.fillRect(x-size/2, y-size/2, size, size); //绘制方块
context.beginPath();
context.arc(x, y, size/2, Math.PI*2, false); //绘制圆形
context.closePath();
context.fill();
}
}

最终效果:


参考资料:

FileReader
CORS enabled image
手把手教你如何将图片“嵌入”网页中
js小工具—本地图片转换为base64编码数据
html file 上传后如何在旁边显示上传的图片
使用FileReader对象的readAsDataURL方法来读取图像文件
《Foundation HTML5 Canvas For Games and Entertainment》Chapter 5 Authors: Hawkes, Rob