前端必备————图片转换成css或js方法

来源:互联网 发布:阿里云上海机房地址 编辑:程序博客网 时间:2024/06/16 04:10

https://zhuanlan.zhihu.com/p/24551014?utm_source=tuicool&utm_medium=referral

作者:小爝
链接:https://zhuanlan.zhihu.com/p/24551014
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

今天圣诞节,昨天平安夜,看了大家各种雪花特效,徒手css画苹果,麋鹿,圣诞树什么的,我也想着自己拿代码写个圣诞老人给大家开心一下。

然后我选了这么一张图:

很萌吧,看起来非常好画,这里我就不说拿css怎么画了,反正我没画……,因为我太懒了。。。

我想了想既然是拿css画图,为什么不能直接用js生成呢,也就是说拿程序直接把一张图片转换成css的写法那不是更好?

说干就干,我第一件事就是打开了google,输入了img2css…… 这个关键字,你看我多机智……

果然没有让我失望,已经有老外实现了,还有人在15年底转发了微博,果然out了,项目在这里:img2css

很强大,瞬间转换出来了,然后我想着这玩意是怎么实现的呢?看了看源码,主要是先拿canvas draw了一张图,然后通过拿整个图的像素点信息,就是getImageData方法,返回的ImageData对象保存了每一个像素点的rgba的相关信息,再遍历这个矩阵拿到每一个像素点的hex值,当然你不拿hex也可以,就是输出后的空间太大了。rgba(x,x,x,x)和#fxc 这种写法肯定是hex更节约空间,作者还考虑到了颜色name缩写的转换,最后再利用box-shadow 1*1的黑科技给所有的像素点都来了一个复制shadow的效果,ok,转换完毕之后输出到一个div上就成了。

我们看下效果:

好吧,B都让他装了,我不服,我不干,我想了想依赖浏览器的canvas来转换不好,我要装逼还要开浏览器,我写个nodejs版本的吧,其他的步骤都好说,主要是getImageData这个方法在nodejs里怎么搞呢?

幸亏我原来用过一个pure的javascript图形库可以干到,这个库叫 oliver-moran/jimp 里面read的方法可以拿到bitmap,里面的data就是了~于是第一个版本的img2css nodejs版本就搞出来了。

最后写到一个指定的html文件里就完活了,然后正当我想装一下逼的时候,发现,这尼玛生成的html比图片本身都要大啊。

大了足足好几十倍。。。

当然了,谁让你一个点一个点的去画了,重复的颜色肯定占字节数大,这里开始想有没有优化的空间了?

我想到了两个办法:

1,css3的属性path-clip的polygon用法。

2,svg的path标签。

这2个都是用同样的单一颜色来优化用1*1的方法来绘图的办法,1方法可能需要多个N个不相邻的多边形图层叠加来实现,svg则可以只用一个path来描述一个相同颜色,肯定svg更优,此时我又打开了google,输入了img2svg的关键字……

果然,又有人提前把逼装好了……直接看这个库吧:59naga/pixel-to-svg 这样我们就可以绕过一些无用的细节。

他的做法也是拿图片的所有位图点信息,然后每一个点转换成rgba的格式,存储到一个map中,把所有相同颜色的点的x,y坐标进行了保存,最后通过每一个相连的width的连接来进行的优化。可以看下具体实现的代码:

看这个地方就可以了,D那个构造函数是把相关的坐标转换成对应的x,y,w,h的一个svg path写法的类。

通过不断的去画同一个颜色的path,如果遇到了x轴相邻的节点,直接把节点的width+1。最后我们看下优化后生成的结果:

可能大家看不懂,如果对应看一下svg path的缩写含义,就应该明白了:

这样就保证了所有的0,0,0,0的rgba的绘制共用了同一个path标签,并且会进行相邻的宽度合并,看下生成的svg大小:

恩,还是比png原图要大,但是比用box-shadow一个点一个点的去画要小了3倍,如果你不服,还想优化怎么办?最简单的办法了,相近的颜色进行合并就好了,这里我针对svg这一步再进行一次优化,最简单的办法,我们把png使用其他压缩工具先压缩一下,直接去除里面的相邻相近颜色,代码如下,这里用的imagemin:

这次我们用10%的压缩比,先看下效果图:

还不错吧,看起来虽然有了一些些噪点,我们看一下大小。

test2 是css的结果,test是直接转svg没优化的结果,test3 是10%压缩比例的svg效果,基本接近原图,当然也是因为这个圣诞老人比较简单。

好了,最后所有的代码发一下吧,比较简单一共没有超过100行,一个命令行的img2css/svg就搞定了,如果有更多优化空间,大家一起试试呗。

var Jimp = require('jimp');var imagemin = require('imagemin');var imageminPngquant = require('imagemin-pngquant');var fs = require('fs');var imgpath = process.argv[2];var htmlpath = process.argv[3];var type = process.argv[4] || 'css';var quality = process.argv[5] || 60;var convert = require('pixel-to-svg').convert;function rgb2hex(r, g, b) {    return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);}if (fs.existsSync(imgpath) && htmlpath) {    if (type === 'css') {        Jimp.read(imgpath).then((image) => {            var data = image.bitmap.data,                h = image.bitmap.height,                w = image.bitmap.width;            var RGBMatrix = [];            for (var y = 0; y < h; y++) {                RGBMatrix[y] = [];                for (var x = 0; x < w; x++) {                    RGBMatrix[y][x] = {                        r: data[y * w * 4 + x * 4],                        g: data[y * w * 4 + x * 4 + 1],                        b: data[y * w * 4 + x * 4 + 2],                        a: data[y * w * 4 + x * 4 + 3]                    };                }            }            var shadow = RGBMatrix.map((row, rowIndex) => {                return row.map((col, colIndex) => {                    var color = rgb2hex(col.r, col.g, col.b);                    return `${color} ${colIndex ? colIndex + 'px' : 0} ${rowIndex ? rowIndex + 'px' : 0}`;                }).join(',');            }).join(',');            var html = `<div style="height:1px;width:1px;box-shadow:${shadow};"></div>`;            fs.writeFileSync(htmlpath, html, 'utf-8');            console.info(`${htmlpath} file created!`);        });    } else if (type === 'svg') {        imagemin([imgpath], {            plugins: [                imageminPngquant({                    quality: quality                })            ]        }).then(files => {            var file = files[0];            Jimp.read(file.data, (err, image) => {                var svg = convert(image.bitmap);                fs.writeFileSync(htmlpath, svg, 'utf-8');                console.info(`${htmlpath} file created!`);            });        });    }} else {    console.error(`${imgpath} or ${htmlpath} not exists!`);}

祝各位圣诞快乐,如果对工程化相关的主题感兴趣,比如为什么前端面试的时候会问,徒手写个命令行工具的实现思路时,你该怎么办呢?


0 0
原创粉丝点击