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

news/2024/11/29 5:28:58/

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!`);
}

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



http://www.ppmy.cn/news/366385.html

相关文章

maya餐具图片_有哪些价格低但是逼格高很文艺又实用的物品呢?

信的恋人火漆印章(《穿越宇宙》宇航员版) “写信真是一件温柔的事&#xff0c;细腻的小心思就藏在横竖撇捺之中&#xff0c;像是一只害羞的小兽躲在情意绵绵的字里行间&#xff0c;被火漆封印起来&#xff0c;等着解封的那一刻窜出来&#xff0c;跳进启信人眼底的柔波里。” 送…

推荐一个美中不失优雅的博客网主页(素材参考---麋鹿鲁哟)

读者, 你好&#xff0c;这里是豪豪在线客服为你服务&#xff0c;接下来我将想你献上等你许久的专属于个人博客主页&#xff0c;等跟着我的步骤&#xff0c;相信可以在很短时间内&#xff0c;你也可以拥有专属于自己的博客主页面。在这之前&#xff0c;请在心中默念&#xff0c;…

springboot整合xxl-job

文章目录 前言一、xxl-job是什么&#xff1f;二、使用步骤1.下载源码,并部署好2.模仿xxl-job-executor-sample-springboot 自己建立一个服务1 引入xxl-job核心依赖2 创建服务,配置yml3 创建一个配置类,用于读取上述配置,并配置好handel信息4 创建一个执行器的任务类,用于执行真…

String在Java中真的是不可变吗

在Java中&#xff0c;String确实是不可变的。这意味着一旦创建了一个String对象&#xff0c;它的值就不能被修改。当你对一个String对象执行一些操作&#xff08;如拼接、替换等&#xff09;&#xff0c;实际上是创建了一个新的String对象&#xff0c;原始的String对象保持不变…

HAProxy概述、搭建Web群集

HAProxy概述、搭建Web群集 一、HAProxy概述1、HAProxy的主要特性2、常见的Web集群调度器3、Haproxy应用分析4、Haproxy调度算法原理 二、LVS、Nginx、HAproxy的区别三、LVS、Nginx、HAproxy的优缺点1、Nginx的优点&#xff1a;2、Nginx的缺点&#xff1a;3、LVS的优点&#xff…

Visual Studio Code Arduino资源占用和效率对比

Visual Studio Code&Arduino资源占用和效率对比 系统资源占用&#xff1a;编译效率&#xff1a; 这段时间在玩ESP32&#xff0c;闲来无事对比了一下Visual Studio Code后面简称VS和Arduino的效率和资源占用&#xff0c;只是大致的对比&#xff0c;没有斤斤计较。 配置为&am…

WinForm——软件加载读条界面卡死问题

WinForm——软件加载读条界面卡死问题 前言一、问题现象二、测试部分代码1.Loading窗体2.加载代码Program处 三、分析原因四、解决方案代码1.Loading窗体2.加载代码Program处 前言 在制作软件开启界面&#xff0c;读条加载时&#xff0c;在Program中new了个Loading窗体&#x…

MyBatis XML 映射文件

XML 映射文件的基本结构 MyBatis 的 XML 映射文件包含以下几个部分&#xff1a; mapper 元素&#xff1a;定义了 XML 映射文件的根元素&#xff0c;其中包含了命名空间和 SQL 语句等信息。select、insert、update、delete 元素&#xff1a;分别用于定义查询、插入、更新、删除…