我正在参加「兔了个兔」创意投稿大赛,详情请看:「兔了个兔」创意投稿大赛
本来想着兔年给各位大佬用css画个兔子助助兴,于是在网上找了一张爱宠大机密的那只痞帅痞帅的兔子的图片,准备临摹一下
于是我凭着感觉来实现了兔子,心里想着一步步来画,先从兔耳朵,于是照着心里的样子,实现成这样了。
看了下,这是啥?灵魂画手?于是乎产生了个想法,可不可以用dom去渲染,获取图片上每个点的颜色,然后拼起来?
获取图片上对应坐标点的颜色
难点:怎么获取图片上每个点的颜色?
-
- 创建PNG的画布
-
- 通过 getImageData() 方法获取对应像素点的信息
// 获取图片dom对象
const img = document.getElementById('my-image');
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
const pixelData = canvas.getContext('2d').getImageData(x, y, 1, 1).data; // x,y对应坐标位置
ImageData.data返回类型为Uint8ClampedArray的一维数组,每四个数组元素代表了一个像素点的RGBA信息,每个元素数值介于0~255;
前面的三个参数代表了RGB颜色,最后一个参数1代表了透明度A,范围为(0, 1)。但是ImageData.data的元素数值范围都在(0, 255)。因此这里需要按照0 => 0,1 => 255的方式对应下。
根据以上代码我们先来实现一个取色器,无非就是获取点击时的event对象里的offsetX和offsetY
dom渲染图片
取色器实现了,我们只需要获取每个点的坐标,将其当作每一个小块状div然后排列在一起
思路
-
- 获取图片宽高
-
- 通过双层循环遍历获取每个点的坐标
-
- 创建 fragment 将每个点坐标dom元素添加进去
-
- 最后添加到视图里
代码如下
// 绘制
function draw () {
styleSheets.insertRule('.item {display: inline-block;width: 1px; height:1px;}');
const fragment = new DocumentFragment();
for (let i = 1; i <= this.imgHeight; i++) {
for (let j = 1; j <= this.imgWidth;j++) {
const item = document.createElement('div');
// 设置item样式
item.classList.add('item')
// 获取图片对应坐标信息
const pixelData = this.canvas.getContext('2d').getImageData(j, i, 1, 1).data;
let bgColor = `rgba(${pixelData[0]},${pixelData[1]},${pixelData[2]},${pixelData[3] === 0 ? 0 : (pixelData[3] / 255)})`
// 设置背景色
item.style.backgroundColor = bgColor
fragment.appendChild(item);
}
}
// 添加进视图
content.appendChild(wrap);
}
优化 如果以1px为维度,当图片太大的时候渲染的dom数量会很多,肯定会很卡,我来优化一下,让其可以动态调整颗粒度
// 绘制
function draw (size) {
styleSheets.insertRule(`.item {display: inline-block;width: ${size}px; height:${size}px;}`); // 代码变更
const fragment = new DocumentFragment();
for (let i = 1; i <= this.imgHeight; i += size) { // 代码变更
for (let j = 1; j <= this.imgWidth;j += size) { // 代码变更
const item = document.createElement('div');
// 设置item样式
item.classList.add('item')
// 获取图片对应坐标信息
const pixelData = this.canvas.getContext('2d').getImageData(j, i, 1, 1).data;
let bgColor = `rgba(${pixelData[0]},${pixelData[1]},${pixelData[2]},${pixelData[3] === 0 ? 0 : (pixelData[3] / 255)})`
// 设置背景色
item.style.backgroundColor = bgColor
fragment.appendChild(item);
}
}
// 添加进视图
content.appendChild(wrap);
}
最后我们看一下效果,以下分别是颗粒度为1/2/5时的效果图
可以发现颗粒度越大,看起来就像马赛克了。(以下代码没有考虑性能和边界情况,仅供娱乐哈)