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

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

不知道你又没有遇到过这样的烦恼:“设计师给你的精致的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: \n" + new String(svg_code, "utf-8"));
            System.out.println("save path  : \n" + 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!\n‘
                + ‘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}‘, ‘[email protected]{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操作方面给予的技术支持

參考资料

时间: 2024-10-12 23:33:42

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

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

图片处理在前端开发过程中占据了不少的时间,很是累人.在本文中我们不讨论如何提高切图的效率,我们讨论另一个问题:如何处理设计稿中的一些简单图形.不知道你又没有遇到过这种烦恼:"设计师给你的精致的PSD中有一个简单图形,就是那用用多边形.圆形和线条组成的图形.这个图形用css3实现不了,或者能实现,但为了兼容某些浏览器不能用css3来实现,只能切图.好的,你很快切完了并在样式中引用了.但没过多久需求上要求改下图片的颜色,而这是你已经将导出图片的那个PSD删除了,你还得重新在设计稿中把这个图形抠出来&

WEB前端开发中的图片压缩

web前端开发中,图片的重要性不言而喻,而由于一些图片的大小加上现在国内的网速不给力等种种原因,我们非常有必要对网站使用的图片进行压缩,压缩图片必然会带来图片质量的损失,我们要尽可能的在质量降低很小的情况下压缩图片,以便让网站更快的加载,提高用户体验度. 我在工作中,压缩图片用到了三个方法,分享给大家: 一.使用windows自带的画图工具 1.使用画图工具打开想要压缩的图片:2.什么都不用做,直接另存为你想要的图片格式,你会发现它比源文件小了很多,而且质量看不出来损失. 这个方法简单,快捷,压

【前端福利】用grunt搭建自己主动化的web前端开发环境-完整教程

jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt,你没有理由不学.不用! 1. 前言 各位web前端开发者.假设你如今还不知道grunt或者听说过.可是不会熟练使用grunt,那你就真的真的真的out了(三个"真的"反复.表示重点). 至于grunt的作用,这里不具体说了.总之你假设做web前端开发,你一定要用grunt.另一点,它全然免费,没有盗版.既强大又免费的东西.为何不用? 当然了,你假设你能找到更好的替代grunt的其它工具

关于前端开发中的“收口”思想

什么是收口 所谓条条大路通罗马,但如果让我来设计通向罗马的各种大路,我至少会做两件事情: ① 让罗马只有一个入口 ② 让罗马只有一个出口 这样做的好处是,无论你路从哪来,我可以统一在入口处给你打上各种标志,我也可以在你离开罗马时给你留点纪念.当然罗马自然不只一个出口入口,但是每个出口入口一定有一套相同的规定,否则就会出问题. 具体到当今的工作场景,高速公路又是一个收口的好例子,进入高速公路时候得经过收费站做点标志,离开时候也会做点操作,如果没有这种收口,不论是缴费工作,流量统计或者其他都是无法统

【名词】在前端开发中的“轮子”

最近在随便刷一些论坛,文档,常见一个名词"轮子". 自我理解:已封装完成的库.换个名词,组件.例如:图片轮播组建:swiper等,菜单栏组件. 在前端开发中,我们需要在满足条件下对于平台进行兼容,众所周知的IE系列,那么如果有一份已经写好的能够兼容各个浏览器的代码,进行封装以后直接引入使用显然会事半功倍. 作为练习,对于市场上主流的"轮子"进行仿造可以增加经验,也可以自己造轮子,只有自己动手做了才能够知道其中的知识点.

总结前端开发中的一些特殊规范

前端日子工作太忙没时间发随笔,现在来总结一些前端开发中的特殊规范(常规的规范就不赘述了),希望能让各位收益,也欢迎提出异议. 一. 文件系统 一个有条理的文件系统可以为后期的维护提供便利,起码寻找某个页面的某张图片时不用对着url地址顺藤摸瓜找半天,如果能做到不看url也能准确猜中某页面文件的所在地,那这个文件系统便是合格的. 先来看一个不合格的文件存放方式: 如上图,该目录下共有2个css文件夹.2个js文件夹以及3个存放图片的文件夹(“dyp2p”文件夹里也是放置图片的),同时还有许多人经常

前端开发中的Error以及异常捕获

本文首发于公众号:符合预期的CoyPan 写在前面 在前端项目中,由于JavaScript本身是一个弱类型语言,加上浏览器环境的复杂性,网络问题等等,很容易发生错误.做好网页错误监控,不断优化代码,提高代码健壮性是一项很重要的工作.本文将从Error开始,讲到如何捕获页面中的异常.文章较长,细节较多,请耐心观看. 前端开发中的Error JavaScript中的Error JavaScript中,Error是一个构造函数,通过它创建一个错误对象.当运行时错误产生时,Error的实例对象会被抛出.

五年干货分享!前端开发中最常用的JS代码片段

很多网友私信我,说学到js就开始卡壳了,甚至初略的看了一下js,就跳过开始学习框架之类的.这里要提醒你,js是前端的重中之重,如果你忽视了,后果不堪设想! 学好,并熟练的运用这门编程语言真的很难吗?本篇文章为大家总结了一些前端开发中最常用的JS代码片段,希望能对大家的学习以及工作上都能有所帮助,有所收获. HTML5 DOM 选择器 javascript 代码 // querySelector() 返回匹配到的第一个元素 var item = document.querySelector('.i

总结:前端开发中让元素水平垂直居中的方法

前端开发中,我们经常需要对元素进行水平垂直居中.为此,小编特地总结了让元素居中的方法. 水平居中text-align:center; 这个是没有浮动的情况下,我们可以先将要居中的块级元素设为inline/inline-block,然后在其父元素上加上属性text-align:center;即可.如果要居中的块级元素直接是内联元素(span.img.a等),直接在其父级元素上加上属性text-align:center;即可: .way { border: 1px solid red; width: