前端图片动画优化手段
基本知识
ASCII
大多数计算机采用ASCII码(美国标准信息交换码),它是表示所有大小写字母、数字、标点符号和控制字符的7位编码方案。统一码(Unicode)包含ASCII码,'\u0000'到'\u007F'对应全部128个ACSII字符。在JAVA中可以使用统一码。在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码,统一规定了上述常用符号用哪些二进制数来表示 。
Unicode
统一码,也叫万国码、单一码(Unicode)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。如果把各种文字编码形容为各地的方言,那么Unicode就是世界各国合作开发的一种语言。
在这种语言环境下,不会再有语言的编码冲突,在同屏下,可以显示任何语言的内容,这就是Unicode的最大好处。 就是将世界上所有的文字用2个字节统一进行编码。那样,像这样统一编码,2个字节就已经足够容纳世界上所有的语言的大部分文字了。
最出名的代表就是UTF-8
矢量图
矢量图,也称为面向对象的图像或绘图图像,在数学上定义为一系列由点连接的线。矢量文件中的图形元素称为对象。每个对象都是一个自成一体的实体,它具有颜色、形状、轮廓、大小和屏幕位置等属性。位图
矢量图是根据几何特性来绘制图形,矢量可以是一个点或一条线,矢量图只能靠软件生成,文件占用内在空间较小,因为这种类型的图像文件包含独立的分离图像,可以自由无限制的重新组合。它的特点是放大后图像不会失真,和分辨率无关,适用于图形设计、文字设计和一些标志设计、版式设计等。
位图图像(bitmap),亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的。这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块。扩大位图尺寸的效果是增大单个像素,从而使线条和形状显得参差不齐。然而,如果从稍远的位置观看它,位图图像的颜色和形状又显得是连续的。用数码相机拍摄的照片、扫描仪扫描的图片以及计算机截屏图等都属于位图。位图的特点是可以表现色彩的变化和颜色的细微过渡,产生逼真的效果,缺点是在保存时需要记录每一个像素的位置和颜色值,占用较大的存储空间。响应式图片 根据屏幕像素加载不同的图片尺寸,优点是自适应选择图片保持高清晰度,减少不必要的请求渲染时间,缺点是增加代码量和项目体积.
媒体查询
这是兼容性最好的方法,略显繁琐
.bg { background-image: url(demo-320w.jpg);
}
@media (min-device-pixel-ratio: 2){
.bg { background-image: url(demo-640w.jpg);
}
}
srcset
以逗号分隔的一个或多个字符串列表表明一系列用户代理使用的可能的图像。每一个字符串由以下组成:
- 指向图像的 URL。
- 可选地,再加一个空格之后,附加以下的其一:
- 一个宽度描述符,这是一个正整数,后面紧跟 '
w
' 符号。该整数宽度除以sizes属性给出的资源(source)大小来计算得到有效的像素密度,即换算成和x描述符等价的值。 - 一个像素密度描述符,这是一个正浮点数,后面紧跟 '
x
' 符号。
- 一个宽度描述符,这是一个正整数,后面紧跟 '
文章图片
根据不同的像素密度加载不同的图片,都不符合的情况下会使用src作为默认图,这是用于像素密度的适配
sizes
表示资源大小的、以逗号隔开的一个或多个字符串。每一个资源大小包括:
- 一个媒体条件。最后一项一定是被忽略的。
- 一个资源尺寸的值。
文章图片
表示图片在640px及以下100%宽度,超过640px但1080px及以下33%宽度,这是用于屏幕宽度的适配
下面是这两个属性的兼容图
文章图片
picture + source
如果想要更加精确控制的话可以使用这两个新标签
文章图片
media就相当于上面的sizes,这会从上往下判断直到加载符合的资源.
source还有一个type属性用于加载不同的图片类型
文章图片
下面是兼容性
文章图片
文章图片
Tinypng压缩 一般这一步应该是由Ui设计负责输给到开发,也不排除部分UI没有做这一步,我们自己也可以去做处理.
打开https://tinypng.com/选择图片上传压缩,之后再下载压缩后的图片资源回来
压缩效率
从图片可以看到根据之前图片算法不同可以压缩的体积也不一样,我随便拿了两张图片测试就达到了83%和48%.
文章图片
对比
例子 | 格式 | 处理前 | 处理后 | 新旧体积比 |
---|---|---|---|---|
图一 | jpeg | 649.8KB | 335.3KB | 52% |
图二 | jpeg | 1024KB | 176.1KB | 17% |
文章图片
跟原图相比较,肉眼看不出画质有损害
文章图片
未处理图
文章图片
处理图
文章图片
imagemin-webpack-plugin (貌似不能用了) 【前端图片动画优化手段】利用webpack插件对项目图片进行压缩处理,基本配置
import ImageminPlugin from 'imagemin-webpack-plugin'
import imageminMozjpeg from 'imagemin-mozjpeg'module.exports = {
plugins: [
new ImageminPlugin({
plugins: [
imageminMozjpeg({
quality: 65,
progressive: true // 可将图片转成渐进式渲染,用户体验较好,但是渲染耗时更久
})
]
})
]
}
对比
以前做过一个实验数据
例子 | 格式 | 处理前 | 处理后 | 新旧体积比 |
---|---|---|---|---|
图一 | jpg | 189.3KB | 162KB | 85.5% |
图二 | png | 390KKB | 94.2KB | 24.1% |
WebP最初在2010年发布,目标是减少文件大小,但达到和JPEG格式相同的图片质量,希望能够减少图片档在网络上的发送时间。2011年11月8日,Google开始让WebP支持无损压缩和透明色(alpha通道)的功能,而在2012年8月16日的参考实做libwebp 0.2.0中正式支持。根据Google较早的测试,WebP的无损压缩比网络上找到的PNG档少了45%的文件大小,即使这些PNG档在使用pngcrush和PNGOUT处理过,WebP还是可以减少28%的文件大小。从安卓4.2开始也支持,只要兼容代码写得好也足以让项目体积大大减少,safari宣布14也开始支持了
美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”
文章图片
对比
例子 | 格式 | 处理前 | 处理后 | 新旧体积比 |
---|---|---|---|---|
图一 | jpeg | 649.8KB | 254KB | 39% |
压缩后图一 | jpeg | 335.3KB | 186KB | 55.4% |
图二 | jpeg | 1024KB | 100KB | 9.7% |
压缩后图2 | jpeg | 176.1KB | 82.7KB | 46.9% |
文章图片
处理图
文章图片
文章图片
原图
文章图片
处理图
文章图片
文章图片
CDN图片处理 CDN除了缓存资源以外,还提供了很多额外处理工具,例如图片转换
文章图片
这是一种更完善的方案,花钱解决各种处理保存转换的难度和工作量,具体API大同小异.
可以轻易替代响应式图片的复杂写法
雪碧图 一张图片一个请求的方式在加载速度方面受到了严重限制。特别是在小图片的情况下请求的时间相比较本身体积得不偿失. 限制主要来自两个方面:建立连接的时间,和浏览器的并发下载数量限制。前者来自 HTTP 协议,而后者则来自浏览器的实现
将多个小图片合并成一张大图,可以用一个请求拿到所需的图片,并且合并后的体积比合并前的总体积还小,缺点是只能使用背景并且需要定位位置,也可以通过一些插件自动生成样式.
在线生成sprite-generator
文章图片
这种非常智能而且方便,但是不适用长期维护或者改动频繁的项目
webpack-spritesmith
Webpack plugin that converts set of images into a spritesheet and SASS/LESS/Stylus mixins, using spritesmith and spritesheet-templates
示例配置
//webpack.config.js
var path = require('path');
var SpritesmithPlugin = require('webpack-spritesmith');
module.exports = {
// ...
module: {
rules: [
{test: /\.styl$/, use: [
'style-loader',
'css-loader',
'stylus-loader'
]},
{test: /\.png$/, use: [
'file-loader?name=i/[hash].[ext]'
]}
]
},
resolve: {
modules: ["node_modules", "spritesmith-generated"]
},
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/ico'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl')
},
apiOptions: {
cssImageRef: "~sprite.png"
}
})
]
// ...
};
在项目开发中直接配置webpack会更加方便,更多配置可查看仓库
Data URL Data URLs,即前缀为
data:
协议的URL,其允许内容创建者向文档中嵌入小文件。Data URLs 由四个部分组成:前缀(
data:
)、指示数据类型的MIME类型、如果非文本则为可选的base64
标记、数据本身:$$ data:[
组成 | 含义 |
---|---|
data: |
前缀 |
[ |
可选的MIME type代表数据的类型 |
[;
base64] |
可选的base64标识 |
|
数据本身 |
mediatype 是个 MIME 类型的字符串,例如
"image/jpeg"
表示 JPEG 图像文件。如果被省略,则默认值为
text/plain;
charset=US-ASCII
base64
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。如果数据是文本类型,你可以直接将文本嵌入 (根据文档类型,使用合适的实体字符或转义字符)。
Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。采用Base64编码具有不可读性,需要解码后才能阅读。
Base64由于以上优点被广泛应用于计算机的各个领域,然而由于输出内容中包括两个以上“符号类”字符(+, /, =),不同的应用场景又分别研制了Base64的各种“变种”。
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。
如果是二进制数据,你可以将数据进行base64编码之后再进行嵌入。
data
data:,Hello%2C%20World!
// 简单的 text/plain 类型数据data:text/plain;
base64,SGVsbG8sIFdvcmxkIQ%3D%3D
// 上一条示例的 base64 编码版本data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E
// 一个HTML文档源代码 Hello, Worlddata:text/html,
// 一个会执行 JavaScript alert 的 HTML 文档。注意 script 标签必须封闭。
用webpack的基本入门库可以简单实现, url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
}
只适用于小图片的转换,原理就是通过增加代码量来减少资源请求,所以控制转换的体积限制尤为重要,而且本身的问题也不少
例子
文章图片
优点:
- 减少资源请求数
- 不受浏览器并发数限制没有不必要的阻塞时间
- 浏览器兼容性好
- 编码体积是原数据体积的4/3左右,且大量使用降低代码可观性
- 浏览器不会缓存Data URL图片(除非是嵌入代码里连同脚本一起缓存)
- CPU资源更多
- 内存消耗更大
- 渲染耗时更长
文章图片
与其他图像格式相比,使用 SVG 的优势在于:
- SVG 可被非常多的工具读取和修改(比如记事本)
- SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。
- SVG 是可伸缩的
- SVG 图像可在任何的分辨率下被高质量地打印
- SVG 可在图像质量不下降的情况下被放大
- SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)
- SVG 可以与 Java 技术一起运行
- SVG 是开放的标准
- SVG 文件是纯粹的 XML
创建SvgIcon组件
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
创建icons文件夹
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg组件// 注册到全局
Vue.component('svg-icon', SvgIcon)const requireAll = requireContext => requireContext.keys().map(requireContext)
// eslint-disable-next-line
const req = require.context('./svg', false, /\.svg$/)
requireAll(req)
在main.js中引入
import './icons'
使用
SVG sprite loader
Webpack loader for creating SVG sprites.
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons')],
options: {
symbolId: 'icon-[name]'
}
}
-------------------------------------------
exclude: [resolve('src/icons')],
SVG在线压缩合并工具
具体参考张鑫旭博客SVG精简压缩工具svgo简介和初体验
字体图标 随着Retina屏诞生,常规的图标展示的分辨率已经满足不了用户需求,毕竟设备像素比会导致即使屏幕差不多尺寸所看到的图片清晰度不同
上面说的所有方式终究还是图片或者会有某些损耗,而字体图标是以字体形式展示图标
优点
- 矢量绘图技术共同的优点就是支持平滑缩放
- 本身就是文字。它会受到字号、前景色、行高等参数的控制
- 字体中只有矢量数据,没有颜色数据, 字体图标必然是单色
- 本身字体内间距无法参数控制
- 工具链复杂,不是普通团队可以开发维护的,一般依赖开源社区提供使用
文章图片
应用代码有几种
unicode引用 unicode是字体在网页端最原始的应用方式,特点是:
- 兼容性最好,支持ie6+,及所有现代浏览器。
- 支持按字体的方式去动态调整图标大小,颜色等等。
- 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
注意:新版iconfont支持多色图标,这些多色图标在unicode模式下将不能使用,如果有需求建议使用symbol的引用方式第一步:拷贝项目下面生成的font-face
@font-face {font-family: 'iconfont';
src: url('iconfont.eot');
src: url('iconfont.eot?#iefix') format('embedded-opentype'),
url('iconfont.woff') format('woff'),
url('iconfont.ttf') format('truetype'),
url('iconfont.svg#iconfont') format('svg');
}
第二步:定义使用iconfont的样式
.iconfont{
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
第三步:挑选相应图标并获取字体编码,应用于页面
3
font-class引用 font-class是unicode使用方式的一种变种,主要是解决unicode书写不直观,语意不明确的问题。
与unicode使用方式相比,具有如下特点:
- 兼容性良好,支持ie8+,及所有现代浏览器。
- 相比于unicode语意明确,书写更直观。可以很容易分辨这个icon是什么。
- 因为使用class来定义图标,所以当要替换图标时,只需要修改class里面的unicode引用。
- 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
//at.alicdn.com/t/font_8d5l8fzk5b87iudi.css
第二步:挑选相应图标并获取类名,应用于页面:
symbol引用 这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。这种用法其实是做了一个svg的集合,与上面两种相比具有如下特点:
- 支持多色图标了,不再受单色限制。
- 通过一些技巧,支持像字体那样,通过
font-size
,color
来调整样式。 - 兼容性较差,支持 ie9+,及现代浏览器。
- 浏览器渲染svg的性能一般,还不如png。
//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js
第二步:加入通用css代码(引入一次就行):
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
第三步:挑选相应图标并获取类名,应用于页面:
Apng
APNG(Animated Portable Network Graphics)是一个基于PNG(Portable Network Graphics)的位图动画格式,扩展方法类似主要用于网页的GIF 89a,仍对传统PNG保留向下兼容。第1帧是标准的单幅PNG图像,因此只支持原版PNG的软件能正常显示第1帧。剩余的动画帧和帧速数据储存在符合原版PNG标准的扩展数据块里。我们回顾一下另一个常用的动态图片GIF特点
- 采用改进的LZW压缩算法处理图像数据无损压缩,最多存储8bit的索引色(256色), 适用于对色彩要求不高同时需要文件体积较小的场景
- 支持背景全透明,不支持半透明,图片边缘会有明显的粗糙锯齿
- 支持简单动画
- 支持图像渐进
- 支持24bit索引色
- 支持8位Alpha透明度
- 兼容png
- 体积更小
文章图片
因为兼容性问题,一般会搭配apng-js或者apng-canvas使用,具体原理大概为:
- 加载资源,解析数据格式
- 校验通过之后进行数据整理
- 利用
raf
在canvas上绘制每一帧重现动画
从 apng-canvas里可以看到也是利用Image标签加载
var checkNativeFeatures = oncePromise(function (resolve) {
var canvas = document.createElement("canvas");
var result = {
TypedArrays: ("ArrayBuffer" in global),
BlobURLs: ("URL" in global),
requestAnimationFrame: ("requestAnimationFrame" in global),
pageProtocol: (location.protocol == "http:" || location.protocol == "https:"),
canvas: ("getContext" in document.createElement("canvas")),
APNG: false
};
if (result.canvas) {
// see http://eligrey.com/blog/post/apng-feature-detection
var img = new Image();
img.onload = function () {
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
result.APNG = (ctx.getImageData(0, 0, 1, 1).data[3] === 0);
resolve(result);
};
// frame 1 (skipped on apng-supporting browsers): [0, 0, 0, 255]
// frame 2: [0, 0, 0, 0]
img.src = "data:image/png;
base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACGFjV" +
"EwAAAABAAAAAcMq2TYAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAGmZjVEwAAAAAAAAAAQAAAAEAAA" +
"AAAAAAAAD6A+gBAbNU+2sAAAARZmRBVAAAAAEImWNgYGBgAAAABQAB6MzFdgAAAABJRU5ErkJggg==";
} else {
resolve(result);
}
});
已知BUG
- Safari for iOS(Safari for macOS正常)预览 APNG 的时候,动图的循环次数为对应原图的 loop + 1。比如 APNG 有 10帧,loop 为 2,那么会循环总计展示 30 帧。
- 部分手机端会出现异常动效
- 因为后缀仍然使用png,如果对图片进行处理可能导致变成静态png,建议与静态png分开管理
- 部分机型使用apng-canvas会出现绘制位置大小偏离实际的问题
4可以通过重设canvas样式解决,所以解决方案如下
-------------------省略------------------------
APNG.parseURL(props.src)
.then((animation) => {
instance = animation;
const { width, height } = instance!;
gameAnimationCanvas.value!.width = width;
gameAnimationCanvas.value!.height = height;
const ctx = gameAnimationCanvas.value!.getContext('2d');
instance!.addContext(ctx);
props.autoplay && instance?.play();
})
.catch((err) => {
console.log('不支持apng该格式动画', err);
});
Svga 前端开发有时候也难免需要用到动画效果,实现方法多种多样
- canvas绘图:难以调试
- GIF:输出效果差,并且资源占用高,只能不断循环
- Apng:比GIF好点
- css3动画:需要手动调试,代码复用率低
- 序列帧:增大体积,修改麻烦
- js动画:容易被阻塞
- MP4:难以统一多端机型播放体验
- lottie:没用过,不评价
SVGA 做的事情,实际上,非常简单,Converter 会负责从 Flash 或 AE 源文件中提取所有动画元素(位图、矢量),并将其在时间轴中的每帧表现(位移、缩放、旋转、透明度)导出。 Player 会负责将这些信息还原至画布上。
SVGA 实质上做了一件非常重要的事情。她会在动画播放前,一次性地上传所有纹理到 GPU,接着,在播放的过程中,这些纹理会被重复使用。CPU 与 GPU 交换的次数大大减少,同时,纹理的数目也在可控范围。内存、CPU、GPU 占用能达到最优状态。
文章图片
优点
- 分工明确,动画设计师通过工具输出 svga 动画文件,开发工程师直接使用
- 动画文件体积更小
- 更加节省空间内存
- 跨平台,视觉一致性还原效果好
- 支持转场动画
- 支持动态替换元素
- 不支持部分AE属性
- 动画是压缩产物,不支持二次编辑、
- 不支持复杂矢量图、不支持AE自带的渐变、生成、描边、擦除等
2.3.0 - 新增音频播放支持手动加载 你可以自行创建 Player 和 Parser 并加载动画
SVGAPlayer 2.0.0 只支持以下浏览器使用
- Edge / IE 6+
- Safari / Chrome
- iOS 6.0+ / Android 4.0+
SVGAPlayer 2.0.0 同时支持以下游戏引擎使用
- CreateJS 使用指南
- LayaBox 使用指南
- 添加 Div 容器
- 加载动画
var player = new SVGA.Player('#demoCanvas');
var parser = new SVGA.Parser('#demoCanvas');
// 如果你需要支持 IE6+,那么必须把同样的选择器传给 Parser。
parser.load('rose_2.0.0.svga', function(videoItem) {
player.setVideoItem(videoItem);
player.startAnimation();
})
自动加载 为 canvas 元素添加以下属性
动画会在页面加载完成后播放
动态图像 你可以动态替换动画中的指定元素,询问你的动画设计师以获取 ImageKey。
- 用于替换的图片,宽、高必须与原图一致。
- setImage 操作必须在 startAnimation 之前执行。
player.setImage('http://yourserver.com/xxx.png', 'ImageKey');
动态文本 你可以在指定元素上添加文本,询问你的动画设计师以获取 ImageKey。
- setText 操作必须在 startAnimation 之前执行。
player.setText('Hello, World!', 'ImageKey');
player.setText({
text: 'Hello, World!,
family: 'Arial',
size: "24px",
color: "#ffe0a4",
offset: {x: 0.0, y: 0.0}
}, 'ImageKey');
// 可自定义文本样式
SVGAPlayer-Web-Lite
这是一个 SVGA 在 Web 上的播放器,它的目标是更轻量级、更高效,但同时它也放弃了对一些旧版本浏览器的兼容性支持,不支持声音播放
目标未来
- 体积 = 80k (gzip = 27kb)
- 兼容 Android 4+ / iOS 9+
- 更好的异步操作
- 多线程 (WebWorker) 解析文件数据
vue-lazyload
功能比较强大
- 轻量级、强大且易于使用
- 可作用在任何类型的图像
- 在加载图像时添加加载类
- 同时支持Vue 1.0和Vue 2.0
import Vue from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'Vue.use(VueLazyload)// or with options
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',
loading: 'dist/loading.gif',
attempt: 1
})new Vue({
el: 'body',
components: {
App
}
})
使用方法 可自定义加载和错误图
文章图片
文章图片
文章图片
// 或者
文章图片
文章图片
文章图片
预加载 一次性加载完图片再开始渲染界面
简单实现
总结 上面介绍了大致的几种实用手段
- 手机适配清晰度: 响应式加载图片
- 基本的图片压缩可以用: tinypng, webpack工具
- 零散小图: 雪碧图, Data URL, 字体图标
- 图片类型: webp, svg
- 播放动画: apng, svga
- 加载方式: 懒加载, 预加载