最近在掘金里看到了这篇文章:产品经理:你能不能用div给我画条龙? ,很有意思,通过canvas提取图片的像素,然后将图片转成点阵图。
按照文章,尝试了好多次,都没有成功。最后只能研究一下原理,自己写一个demo。
参考文章: canvas小练习-图片点阵化demo
需要了解的知识点:
canvas drawImage() 方法:在画布上绘制图像、画布或视频。
canvas getImageData() 方法:复制画布上指定矩形的像素数据
canvas putImageData() 方法:将图像数据放回画布
以下示例基于vue
<template>
<div style="height:2000px;">
<canvas
id="myCanvas"
style="border: 1px solid #d3d3d3">
</canvas>
<el-button type="primary" @click="copy">复制</el-button>
</div>
</template>
<script>
export default {
data() {
return {
// 上下文对象
ctx: undefined,
// 要绘制图片的大小
w: 200,
h: 200
};
},
mounted() {
// 获取canvas对象
let c = document.getElementById('myCanvas');
// 获取canvas上下文
this.ctx = c.getContext('2d');
// 加载图片,不要直接写字符串
let src = require('./imgs/a.png');
// 创建image对象
let image = new Image();
image.src = src;
let that = this;
// 图片加载
image.onload = function() {
// 设置canvas对象的宽高(不一定要等于图像大小)
c.width = image.width;
c.height = image.height;
// 绘制图像,图像,偏移量,宽高(可以跟图片一样大,也可以自定义)
that.ctx.drawImage(image,0,0,that.w,that.h);
};
},
methods: {
copy() {
// 获取图片的像素值
var imageData = this.ctx.getImageData(0, 0, this.w, this.h);
// 计算有多少个rgb值
let sum = this.w * this.h * 4 ;
for(let i = 0;i < sum;i += 4) {
if(imageData.data[i] != 255 && imageData.data[i + 1] != 255 && imageData.data[i + 2] != 255) {
if(i % 12 != 0) {
// 黑
imageData.data[i] = 255;
imageData.data[i + 1] = 255;
imageData.data[i + 2] = 255;
}else{
// 白
imageData.data[i] = 0;
imageData.data[i + 1] = 0;
imageData.data[i + 2] = 0;
}
}
}
// 对象,偏移量
this.ctx.putImageData(imageData, 0,200);
}
}
};
</script>
<style scoped lang="scss">
</style>
注意点,或者说某些地方为什么要这么做
// 加载图片,不要直接写字符串。否则在vue中不能显示出图片
let src = require('./imgs/a.png');
// 获取图片的像素值
var imageData = this.ctx.getImageData(0, 0, this.w, this.h);
getImageData
:这个函数会返回一个对象,里面有一个data属性,是一个数组。数组中的元素,4个一组,表示该像素点的rgba
// 计算有多少个rgb值
let sum = this.w * this.h * 4 ;
像素点就是宽乘以高,例如宽高都是10px
的矩形,像素点就是100,但是一个像素点由4个值组成(rgba),所以要乘以4,否则会丢失一些东西。
核心其实就是如何处理像素点。要想点阵显示的内容多,关键在于如何处理。
以当前这个例子来说:
for(let i = 0;i < sum;i += 4) {
因为一个像素点是4个值,所以要加4,4个数一组进行处理。
if(imageData.data[i] != 255 && imageData.data[i + 1] != 255 && imageData.data[i + 2] != 255) {
if(i % 12 != 0) {
// 黑
imageData.data[i] = 255;
imageData.data[i + 1] = 255;
imageData.data[i + 2] = 255;
}else{
// 白
imageData.data[i] = 0;
imageData.data[i + 1] = 0;
imageData.data[i + 2] = 0;
}
}
这个福字图片,字是红色的,周围是白色的。我们要显示的点阵是这个字,是让这个字以点的形式显示;不能让周围的白色也变成点。所以如果是白色就不处理,而白色的rgb是(255,255,255)
为啥要取余12呢?因为必须黑色和白色间隔显示才能显示点阵,不能所有颜色一样。一个像素是4个值,所以必须是4的倍数。值越小,越密集。值越大越松缓,这里取12是显示的效果比较好。
局限还是如何处理像素点。比如福字周围不是白色,而是有多种颜色,那就要想办法不处理这些颜色。