在项目的开发过程中,如果一个页面有很多的小图标展现。浏览器展示页面时会向后台服务器发送很多的请求获取对应的图片,这样既浪费资源,也使得页面的加载变得很慢,影响客户的体验。此时我们可以采用将这些小图标放置在一张图片上,通过从指定的background-position开始读取展示我们的雪碧图就可以解决这样的问题。这张大图我们就叫做雪碧图。
可是如何将这些图合成一张图片,而且便于我们维护,却是一个值得考虑的问题。一般情况下,我们可以使用常用的图像处理工具来完成雪碧图的制作,但这样的操作方式,费时费力,还有可能出现一些图标的位置偏差。对于图像处理工具不熟悉的开发人员可能还需要一直去麻烦UI的同事。作为JAVA开发人员的我们既然工作中发现了这样的需求肯定是要想办法去解决的,而且越是能通过程序处理的方式越好。
通过论坛的一翻查找,发现少有能提供完整的直接从原始图标读取到生成雪碧图并配套雪碧CSS和原始资源CSS的解决方案。因为项目的原因有些地方还是需要使用原始的资源资源,有些要使用雪碧资源减少请求的次数减少资源占用提升客户的浏览体验。接下来我们将一起来实现这一自动生成的功能。(文章末尾会提供该完整demo的下载地址,如果需要可以下载。)
概述及准备
本项目环境情况为maven3.0.5,jdk1.7,如果不是采用maven作为自己的包管理项目。下载demo后需要将源码部分拷贝到自己的一个项目中,并下载对应的jar包加入到自己的项目中去。
制作的步骤
在最后一个生成雪碧图及其样式CSS文件的过程中需要基于一个传统的CSS样式文件做特殊的标记来完成,不过这些都可以通过代码来控制,不用自己手动去维护的。
过程中生成的文件
为了便于大家理解这样一个图标转变的过程这里给出在不同阶段文件的变化情况,直观的了解这中间我们需要做的事情。这里我项目中使用的图标名称是全局唯一的而且是采用英文和横线搭配的方式。这样方便我们在页面中使用class='h-f-icon-xtgl’的方式进行调用。
通过此图我们可以大致知道,过程主要分为两个步骤:
- 第一步是根据原始的图标生成对应的图标样式CSS文件,该文件是使用freemarker模板解析的方式生成的,resources.css内容如下所示。
我们会发现css文件中多了一些类似注释的信息,这是我们后边第二步生成雪碧图需要用到的标注信息。顶部是雪碧图的分类id(sprite)以及id对应的雪碧图url。background-image行末尾的标记是用来标记当前使用的图标将会被加入都哪一张雪碧图中。
这种的标记就我个人目前使用来说主要有两个用途:
其一是对图标进行归类,不同类型的图标放置在不同的雪碧图中,方便管理;
其二是可以控制雪碧图中图标的数量,我们可以将1000个图标每100个放置在一张雪碧图中,而不用一千张放一起。
试想一下我只需要访问50kb的图标结果我需要下载一张1MB的图片是不是很奇怪呀??虽然解决了多图标不用多次请求服务端的问题,但也不能一个项目图标就放一起一次性请求吧?这样页面的图标展现就呵呵啦~~,所以虽然雪碧图能带来方便,但是到底怎么给图标归类还是要自己根据项目区衡量一下的,不过这些都可以通过代码来实现也是很简单了吧。
- 第二步是根据图标文件和resources.css生成雪碧图和雪碧的css。这里使用了org.carrot2.labs.smartsprites这个类库来完成。生成的雪碧CSS文件内容如下:
这里我配置了ie6的兼容所以会有-background-image: …这样的一行,并且雪碧图也多了一张sprite-1-icon-ie6.png。所以如果在生成雪碧图时如果没有配置兼容IE6的话就不会出现这些内容。
项目依赖包
这里需要使用的依赖包有,如下依赖是maven中的加入方式。如果项目不是采用maven管理包依赖,请下载对应的jar包并加入到项目中。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<guava.version>11.0.2</guava.version>
<smartsprites.version>0.2.10</smartsprites.version>
<spring.version>4.1.5.RELEASE</spring.version>
<freemarker.version>2.3.20</freemarker.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.carrotsearch</groupId>
<artifactId>smartsprites</artifactId>
<version>${smartsprites.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
</dependencies>
生成原始资源的样式CSS文件
这里我们需要从图片资源目录下读取png文件并生成图标引用的样式CSS文件。这一步需要对freemarker有一定的了解,可以进行自定义很多东西。如果不熟悉也没有关系,文末有项目的完整源码,可以先用后边再自己慢慢的去修改。(作为程序员大家的学习能力应该还是很棒的)
- 模板信息:
<#list sprites as sprite>
/** sprite: ${sprite}; sprite-image: url('${sprite}-icon.png'); */
</#list>
<#list classList as class>
.${class.className} {
background-image: url(${class.iconPath}) !important; /** sprite-ref: ${class.spriteId}; */
}
</#list>
- Icon类:
public class Icon {
private String className;//图片引用的样式名
private String iconPath;//图片的路径
private String spriteId;//图片所在的雪碧图的名称
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getIconPath() {
return iconPath;
}
public void setIconPath(String iconPath) {
this.iconPath = iconPath;
}
public String getSpriteId() {
return spriteId;
}
public void setSpriteId(String spriteId) {
this.spriteId = spriteId;
}
}
- 主要代码:
private void makeCssFromPng() throws Exception {
final String pngDir = "C:\\Users\\Administrator\\Desktop\\图标临时文件\\图片资源";
final String resourcesCssName = "resources.css";
final String markDownTemplate = "classpath:templates/menu-css.ftl";
final String outResourceCssPath = "C:\\Users\\Administrator\\Desktop\\图标临时文件";
final String UTF8 = "UTF-8";
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
configuration.setObjectWrapper(new DefaultObjectWrapper());
File css = new File(outResourceCssPath + File.separator + resourcesCssName);
Map<String, Object> dataMap = new HashMap<>();
//雪碧图的分类,这里用来确定有多少张雪碧图
List<String> spriteNameList = new ArrayList<>();
spriteNameList.add("sprite-1");
//png资源图标的对象集合准备
List<File> pngFileList = getPngFile(pngDir);
List<Icon> icons = Lists.transform(pngFileList, new Function<File, Icon>() {
@Override
public Icon apply(@Nullable File inputFile) {
String fileName = StringUtils.substringBeforeLast(inputFile.getName(), ".");
Icon icon = new Icon();
icon.setClassName(fileName);
String path = StringUtils.substringAfter(inputFile.getAbsolutePath(), outResourceCssPath);
icon.setIconPath(StringUtils.replace(path.substring(1), File.separator, "/"));
icon.setSpriteId("sprite-1");
return icon;
}
});
dataMap.put("sprites", spriteNameList);
dataMap.put("classList", icons);
String templateStr = IOUtils.toString(getResource(markDownTemplate).getInputStream(), UTF8);
Template template = new Template(null, templateStr, configuration);
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap);
FileUtils.writeStringToFile(css, content, UTF8);
}
生成雪碧图及其雪碧图的样式CSS文件
这一步比较简单我们只需要做一些简单的配置就可以完成雪碧图的制作了。
- 主要的代码
rivate void makeSprites() throws Exception{
//http://csssprites.org
String rootDir = "C:\\Users\\Administrator\\Desktop\\图标临时文件";
List<String> cssFiles = new ArrayList<>();
String outputDir = "C:\\Users\\Administrator\\Desktop\\图标临时文件\\雪碧";//处理后的css文件和雪碧图文件放置的位置
String documentRootDir = "";//当资源原始css中使用/开始的路径时,在解析时会添加该路径到css的解析中
//(如果使用的是相对于当前工作目录rootDir的路径下的目录则该属性值应当设置为空)
Message.MessageLevel logLevel = Message.MessageLevel.WARN;//日志的级别有INFO、WARN、IE6NOTICE
String cssFileSuffix = "_SUFFIX";//在生成好的雪碧图css文件的末尾追加的后缀
SmartSpritesParameters.PngDepth spritePngDepth = SmartSpritesParameters.PngDepth.AUTO;//生成的雪碧图的颜色深度
//(AUTO:自动根据资源图片来确定;DIRECT:PNG24格式保存,颜色更加丰富;INDEXED:以PNG8格式保存,色彩不够丰富)
boolean spritePngIe6 = true;//兼容旧版本的IE,会生成独立的雪碧文件及对应的css
String cssEncoding = "UTF-8";//CSS文件的编码(https://docs.oracle.com/javase/1.5.0/docs/guide/intl/encoding.doc.html)
SmartSpritesParameters params = new SmartSpritesParameters(
rootDir,
cssFiles,
outputDir,
documentRootDir,
logLevel,
cssFileSuffix,
spritePngDepth,
spritePngIe6,
cssEncoding
);
MessageLog messageLog = new MessageLog(new PrintStreamMessageSink(
System.out, params.getLogLevel()));
SpriteBuilder spriteBuilder = new SpriteBuilder(params, messageLog);
spriteBuilder.buildSprites();
}