自己主动图片生成在前端开发中的一些尝试

zhchoutai      2022-02-08     643

关键词:

图片处理在前端开发过程中占领了不少的时间。非常是累人。在本文中我们不讨论怎样提高切图的效率。我们讨论还有一个问题:怎样处理设计稿中的一些简单图形。

不知道你又没有遇到过这样的烦恼:“设计师给你的精致的PSD中有一个简单图形,就是那用用多边形、圆形和线条组成的图形。这个图形用css3实现不了,或者能实现。但为了兼容某些浏览器不能用css3来实现,仅仅能切图。好的。你非常快切完了并在样式中引用了。

但没过多久需求上要求改下图片的颜色,而这是你已经将导出图片的那个PSD删除了,你还得又一次在设计稿中把这个图形抠出来”。或者来一个更变态的版本号,你做的项目支持换肤功能。在不同的皮肤以下这个图形会有不同的颜色。那么就须要你将这个图片导出为几个不同颜色的版本号。

假设后期有颜色的改动,那么你就须要又一次一一改动并导出,假设这时用于导出图片的psd没有了。你就得又一次从设计稿中扣出来,然后在一一改动后导出图片,稍不注意还可能造成两次图片的尺寸和位置不一样,造成错位。假设你没有遇到过上述情况。你应该是幸运的,而我两种都遇到过,在我做这些繁琐的改动的时候内心总是处于千万仅仅羊驼奔跑的场景。

难道就没有一种办法来简化这中操作吗?能不能像改动css一样,仅仅改动几个參数就完毕对图片的改动呢?要完毕这一目标我们必须完毕两项工作:1. 怎样用文本来描写叙述这些简单图形 2. 怎样将用文本描写叙述的简单图形转换为图片。对于第一项工作比較好办, 用svg来完毕就能够了。但第二项工作该怎样实现呢?

怎样将svg转换为图片

由于我们用SVG描写叙述的图形普通情况下都须要和页面的其它元素融合在一起,所以必须将我们的SVG图形转换为支持透明的png格式。那么怎样实现svg转png呢?在网上搜索了非常久一直没有特别理想的。找到的解决方式基本就两个套路,要么用命令行调用phantom(也有通过canvas来实现的。但canvas的支持还是基于phantom)。要么用命令行调用GraphicsMagick(简称gm)。而这两个软件在体积上都是巨无霸,安装起来非常繁琐尤其是在windows以下,对svg的支持也不是太友好。phantom须要借助canvas来实现SVG转png。太折腾。而gm须要安装第三方库才干处理svg, 而处理后的图片效果也不太好,我的測试图片的透明部分被填充了白色。透明都没处理好其它特性就没心情測试了,预计好不到那里去。

至此研究进入了死胡同,没能继续推进,直到有一个在调研还有一个项目的某个小功能的技术可行性时才意外的有所突破。当时我想做一个小工具以实现将一个非常长的图片进行分割后上传,以便于在手机端进行lazyload. 由于知道nodejs处理图片方面比較渣,所以非常识趣的没有使用nodejs来写而是改用了java。得益于java良好的生态系统非常easy完毕了我想要的功能。

完毕之后我不禁想。我这个功能为什么不用java来试试呢?在Google上搜索"java svg to png", 马上有了结果,Apache下有个专门处理svg的库batik。我原来有个非常好的同事总跟我说“解决一个问题最难的是怎样将自己的问题转换为合适的搜索关键词”。至此真是深有体会。

batik不仅对SVG的支持很好。功能也很强大。不仅能通过DOM API来操作SVG文档,并且还提供了将svg转换为其它格式、svg中嵌入js代码等许多很有用的功能。官方的jar包中有一个能够在命令行中执行,能够完毕将svg图片转换为其它格式,使用起来也很easy:

java -jar batik-rasterizer.jar foo.svg

这恰好是我们须要的功能。

至此基于batik的解决方式应该是眼下为止最完美的,jar文件不须要单独安装就能够执行。而对文件夹结构也没有要求。

全然能够把jar文件和js文件放到一个文件夹下然后和js代码一起公布。

尽管也须要安装java执行环境,但java执行环境的安装相对来说非常easy。并且公司每台电脑都安装了。所以这个条件是能够接受的。

导出可独立执行jar文件

batik自带的那个能够在命令行下执行的jar文件尽管能满足我们的但还是有两个地方存在不足。首先是这个jar文件不能独立执行,对项目中的其它jar文件有依赖,必须把它依赖的jar文件按特定的文件夹结构存放才干执行。这么多文件凌乱的放到一起,看着非常不爽。另一点让人不爽的是这个jar文件仅仅能通过命令行指定要操作的svg文件, 不支持通过命令行指定要转换的svg代码,所以你不能丢一个svg字符串让他处理,必须把字符串写到一个暂时文件里。这非常不方便和其它构建工具进行集成,并且而向代码文件夹中创建和删除文件可能会触发grunt的watch任务造成不必要的编译。基于这两个不能忍受的缺点,所以我们不能使用他自带的那个jar文件,须要自己编写一个可独立执行且支持通过命令行设置要转换的svg代码的jar文件。结合网上的资料整个功能非常easy实现,以下是代码实现:

package myless.func;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.xerces.impl.dv.util.Base64;

public class Converter {
    public static void main(String... args) throws Exception{   
        // 为了避免ms dos下蛋疼的编码问题,对參数做了base64编码
        byte[] svg_code  = Base64.decode(args[0]);      
        String save_path = new String(Base64.decode(args[1]), "utf-8");

        if(args.length >= 3 && args[2].toLowerCase().equals("--show-debug")){
            System.out.println("svg content: 
" + new String(svg_code, "utf-8"));
            System.out.println("save path  : 
" + save_path);
        }   

        InputStream  svg_stream = new ByteArrayInputStream(svg_code);
        OutputStream png_stream = new FileOutputStream(save_path);
        TranscoderInput  input_image  = new TranscoderInput(svg_stream);        
        TranscoderOutput output_image = new TranscoderOutput(png_stream); 

        PNGTranscoder transcoder = new PNGTranscoder(); 
        transcoder.transcode(input_image, output_image);

        svg_stream.close();
        png_stream.flush();
        png_stream.close();
    }
}

使用了eclipse的export功能完毕了导出可运行jar文件,详细操作步骤能够參考百度经验的这篇文章

将转换功能集成到less

一旦jar文件可以独立执行就非常easy被nodejs使用了。由于nodejs本身支持通过命令行调用系统命令。

并且0.12.0版本号之后加入了同步调用命令行的功能。因此我们可以这样实现用nodejs来完毕将SVG图片转换为png图片的功能。

  • nodejs程序通过当前文件的路径计算出须要调用的jar文件路径
  • nodejs生成须要转换的SVG代码并base64编码
  • nodejs设置转换后的图片的保存路径并base64编码
  • nodejs以同步方式调用命令行:
java -jar convert.jar base64-encode-svg base64-encode-save-path

以下的代码是grunt-myless中的转换函数实现。

由于转换的过程比較耗时,为了提高整体的编译进度做了推断是否须要重现转换的逻辑。仅仅有svg代码的md5值发生了变化才会又一次转换。

/**
 * @fileOverview 将SVG代码转换为png文件.
 * @param   String    转换后文件保存路径
 * @param   String... svg属性和svg代码
 * @return  String    保存绝对地址
 * @example
 *  div {
 *     background: svg-to-png(‘width=60px‘, ‘height=30px‘, ‘baseb4-encode=true‘, <<<EOF
 *        <!-- add you svg code here--> 
 *     EOF) center center no-repeat;    
 *  }
 */

var fs = require(‘fs‘);
var path = require(‘path‘);
var crypto = require(‘crypto‘);
var child_proc = require(‘child_process‘);
var jarPath = path.join(__dirname, ‘../jar/svg-to-png.jar‘);

module.exports = function(myless, savePath){
    ‘use strict‘;

    var util = myless.util, fileProps, error;
    var args = [].slice.call(arguments, 1).map(function(i){ return i.value; });
    var data = util.svg.parseInput.apply(null, args.slice(1));
    var picPath = util.file.getRefFilePath(savePath, false);
    var svgCode = util.svg.getSVGCode(data.attrs, data.cont);
    var contMd5 = crypto.createHash(‘md5‘).update(svgCode).digest(‘hex‘);

    // 若png文件已经存在,检查生成图片的svg内容是否发生变化,若没有变化不再又一次生成图片.
    if(fs.existsSync(picPath)){
        fileProps = util.file.getFileProps(picPath);
        if(fileProps["cont-md5"] == contMd5) { 
            util.console.log(‘svg-to-png: <green>svg conttent not change, return cached file!</green>‘);
            return picPath; 
        }
    }

    var codeBase64 = new Buffer(svgCode).toString(‘base64‘);
    var pathBase64 = new Buffer(picPath).toString(‘base64‘);
    var command = "java -jar " + jarPath + ‘ ‘ + codeBase64 + ‘ ‘ + pathBase64;

    try {
        util.file.mkFilePath(picPath);
        child_proc.execSync(command, { encoding: ‘utf8‘ });

    } catch(e) {
        error = e;

    }finally{
        if(error){
            if( fs.existsSync(picPath) ) { 
                fs.unlinkSync(picPath); 
            }        

            throw (‘‘
                + ‘svg-to-png.js: create png file : "‘ + savePath.value + ‘" error!
‘
                + ‘svg  content:‘ + svgCode
                + ‘error info  :‘ + error 
            );
        }
    }

    util.file.setFileProps(picPath, { "cont-md5" : contMd5 });
    return picPath;
}

项目中的实际应用

这个功能在我近期的正在开发这个这个项目中用来完毕对IE78的兼容性处理。

在这个项目中设计师设计对页面的部分区域设计了圆角和圆形背景效果,并且这个页面有6套皮肤。在不同的皮肤设置下这些细节颜色会发生变化。在这个项目中须要做对低端浏览器做兼容处理的主要有下面图中所看到的的四处

技术分享

当中倒计时区域的背景和边框与点赞button的背景和边框颜色均同样,而底部的奖品设置标题左側的背景圆圈与底部的奖品后面的背景圆圈颜色不同。

因此假设有5套不同皮肤的话,我仅仅须要測量5组不同的颜色数据,然后依据这5组数据生成图片就好了。因此使用了例如以下的less代码来完毕此功能

/**
 * @fileOverview  ie9下面浏览器兼容样式.
 * @since   2015.07.10
 */

.mix-circle-pic-bg(@saveTo, @color:#c7173d, @width:176) {
    @r: @width / 2; 
    @bg-image : svg-to-png(@saveTo, 
      [email protected]{width}‘, ‘height=@{width}‘, <<EOF
      <circle cx="@{r}" cy="@{r}" r="@{r}" stroke="none" fill="@{color}"/>
    EOF);

    *background-image: tbcdn-uri(@bg-image);
    background-image : data-uri(@bg-image);
    background-repeat: no-repeat;
}

.mix-theme(
        @theme, 
        @time-rect-color, 
        @time-border-color,
        @prize-title-circle-color,
        @prize-item-circle-color) { 

    @toolbar-circle-color: @time-rect-color;
    @toolbar-shadow-color: @time-border-color;

    [email protected]{theme} {
        .main .time-card { 
            background-color: transparent;  
            .time-card-inner { background-color: transparent; }

            @bg-image : svg-to-png(‘./img/@{theme}-time-card-bg.png‘,
                ‘width=290px‘, ‘height=169px‘, <<EOF  
                    <rect x="0" y="3" width="290" height="166" rx="6" ry="6" fill="@{time-border-color}"/>
                    <rect x="0" y="0" width="290" height="166" rx="6" ry="6" fill="@{time-rect-color}"/>
                EOF
            );

            background-image: data-uri(@bg-image);
            *background-image: tbcdn-uri(@bg-image);        
        }

        .main .toolbar-card {
            @bg-image: svg-to-png(‘./img/@{theme}-toolbar-bg.png‘,
                ‘width=100px‘, ‘height=104px‘, <<EOF
                    <circle cx="50" cy="54" r="50" fill="@{toolbar-shadow-color}"/>
                    <circle cx="50" cy="50" r="50" fill="@{toolbar-circle-color}"/>
                EOF
            ); 

            &:before { display: none; }
            background: data-uri(@bg-image) 0 0 no-repeat;
            *background: tbcdn-uri(@bg-image) 0 0 no-repeat;
        }

        .footer .prize-arrow-wrap {
            &:before { display: none !important; }  
            .mix-circle-pic-bg(‘./img/@{theme}-prize-title-bg.png‘,@prize-title-circle-color, 28); 
        }
        .footer .prize-card { 
            &:before { display: none !important; }   
            .mix-circle-pic-bg(‘./img/@{theme}-prize-item-bg.png‘, @prize-item-circle-color, 176); 
        }        
    }
} 

/*-= 主题部分 =---------------*/
// 主题名称前面加t-是由于这个几个颜色是less放到keywordless会自己主动转换为相应颜色的16进制值
// 我被这个bug坑了非常久,调试了半天才发现。
.mix-theme(t-pink, #c21339, #de2e54, #e4214b, #c7173d); 
.mix-theme(t-blue, #2581d5, #3da2ff, #2581d5, #2273bd);
.mix-theme(t-green, #7ea41a, #acd635, #96bc24, #719103);
.mix-theme(t-violet, #9978d5, #bc98ff, #9c7bd9, #7758ad);
.mix-theme(t-red, #cf1e29, #fa3f43, #d6202d, #bf222c);

总的来说执行后的效果还算不错,图片的质量与photoshop的略差一些,但也还算能用,面面是
用代码生成的不同颜色的点赞button背景:

技术分享技术分享技术分享技术分享技术分享

一些注意事项

  • batik支持半透明但不支持css3的rgba函数。须要使用fill-opacity属性来指定透明度。比如你想给一个rect制定半透明的填充色,你不能这样设置:"fill=‘rgba(0,0,0, 0.5)‘",须要这样:"fill=‘#000‘ fill-opacity=‘0.5‘"
  • pink, red 这类css中的颜色常量在less中会被替换为相应的16进制标示,命名变量时要避免使用

thanks

  • 感想@张霸、@思永在java和eclipse操作方面给予的技术支持

參考资料


一个自己主动依据xcode中的objective-c代码生成类关系图的神器

https://github.com/kimsungwhee/KSHObjcUML安装方法:1、下载项目2、执行3、会又一次开启一个新的xcode4、选择一个项目,点击Objc-UML会自己主动生成并打开生成的类图。效果图不上了。用用试试。 查看详情

自己搭一个文本描述生成图片的系统

...生成一些UI素材图片岂不是美滋滋。。那有没有开源的能自己搭一个简单的我们只需要描述就生成图片的工具呢?答案是肯定的。二、稳定扩散模型    稳定扩散模型在AI图像生成中的应用是比较新颖的一种方法。... 查看详情

xdoclet+ant自己主动生成hibernate配置文件

...的问题。近期接触了Xdoclet这个工具。它实际上就是一个自己主动代码生成的工具。Xdoclet不能单独执行,必须搭配其它工具一起使用,比方ant。假设ant的工具不会用。建议先补充一下ant的基本知识在来学习Xdoclet。   查看详情

android混合开发,html5自己主动更新爬过的坑

如今使用混合开发的公司越来越多,尽管出现了一些新技术,比方Facebook的reactnative、阿里的weex,但依旧阻挡不了一些公司採用h5的决心。当然,这也是从多方面考虑的选择。在三年前就使用过html5混合开发,当时做的是一款贵金... 查看详情

如何提升前端开发速度和效率

...求出现偏差。对于不理解的需求,要及时和PM沟通,不要自己揣测。做与整个项目风格不一致的页面。如果设计师的设计的和当前项目的风格不一致,与其沟通,看是否能用项目中统一的风格(样式)。想清楚实现思路后,再开... 查看详情

前端开发流程

...目中的介绍,一般有工作经验的人,常用的动态效果都会自己封装一个插件。比如说图片轮播,翻页,tab切换页面。  移动端的项目,用***js做的,用这个js实现了什么效果,在整体页面用什么布局模式,都适配那些, 查看详情

知识点

...rty属性后,有2种实现选择@synthesize编译器期间,让编译器自己主动生成getter/setter方法。当有自己定义的存或取方法时,自己定义会屏蔽自己主动生成该方法 @dynamic告诉编译器。不自己主动生成getter/setter方法。避免编译期间... 查看详情

前端开发之常用插件

...常用功能的时候,例如:时间选择,文件上传,下拉框等功能.自己写当然也是可以,但是有好用的插件为什么不使用呢?还要自己封装解决各种兼容性问题...  在此总结一些常用的插件,方便日后开发的时候,可以拿来就用    1)时... 查看详情

mock+proxy在sdk项目的自己主动化測试实战

项目背景广告SDK项目是为应用程序APP开发者提供移动广告平台接入的API程序集合,其形态就是一个植入宿主APP的jar包。提供的功能主要有以下几点:-为APP请求广告内容-用户行为打点-错误日志打点-反作弊团队现状在项目推进的... 查看详情

前端限制上传文件的类型

...,比如上传图片的就只能上传图片类型的文件。 现将自己在开发中的代码放到我的博客里,以备在以后的开发中再次遇到。就可以直接使用了。  这里就放一个小demo了。代码如下:<!DOCTYPEhtml><html><head><metac... 查看详情

基于admin.net框架的前端的一些改进和代码生成处理(代码片段)

...些技术框架,最近对该框架进行了一些研究分析,结合我自己开发框架的思路,对其前后端进行一定的修改调整,本篇随笔记录一些对该框架的相关修改内容。Admin.NET是一套基于Furion/.NET6实现的通用管理平台,模块插件式开发,... 查看详情

在div中自己主动换行

...-break:break-all和word-wrap:break-word都是能使其容器如DIV的内容自己主动换行。它们的差别就在于:1,word-break:break-all比如div宽200px,它的内容就会到200px自己主动换行,假设该行末端有个英文单词非常长(congratulation等),它会把单词... 查看详情

uniapp支付宝支付的前端开发经验分享

...为orderInfo的值可以参照文档和后端给的一些键值对在前端自己拼接,但是总是唤起失败,后来联系支付宝的蚂蚁技术支持中心的客服,经过沟通以后,才知道这个orderInfo必须在后端用支付宝SDK生成以后返回给前端,这样一来,果... 查看详情

聊聊工作需要知道的一些潜规则

...规章制度和项目流程。2但问问题要讲究方法,最起码是自己尝试过解决之后再去问。例如,项目启动不了,你要告诉给你解决问题的人:最近有没有改过什么东西;自己尝试过哪些解决办法;以及自己判断是什么原因造成的等3&... 查看详情

uniapp开发小程序中将部分页面截取生成图片----最详细教程!!!

...面上使用:用法插件官网都有,在这里只说本人自己用的方法:JSON board:设置海报所需的JSON数据进行绘制isCanvasToTempFilePath:把当前画布指定区域的内容导出生成指定大小的图片,并返回文件临时路径。cu... 查看详情

动态图片生成方案

...8-Oh6pg一、背景在业务需求中,根据返回数据动态生成图片分享是很常见的场景。比如在起点读书小程序中,每本书都需要生成一个动态图片,包含:书名 查看详情

2018年年度总结

...个目标最大的感触是写博客真的是一件特别难的事,虽然自己的产出并不多,但是坚持下来了。写博客真的是很提升人的一种方式,要坚持下去)总结今年最大的收获是自己心态的成长,虽然自己并不是很优秀的人,但是从小在... 查看详情

换个姿势访问图片

...及查看大图时返回不同尺寸的图片。好处,额,闭上眼睛自己体会。我看到过一些做法是在图片保存时就生成三套缩略图提供给前端访问,这样其实是可以满足基本需求的,只是局限比较大,譬如下面这几种情况:1、系统初期... 查看详情