Codrop是我很喜欢的一个Web前端开发教程站,页面精美讲解详细,今天翻译一篇跟我最近感兴趣的SVG相关的文章,也顺便体验用Markdown写作的简单顺畅感。
参考原文 INTERACTIVE INFOGRAPHIC WITH SVG AND CSS ANIMATIONS
翻译模式开挂
和以PNG,JPG,GIF等为代表的位图比起来,SVG(可伸缩矢量图)的最大好处就是在任何分辨率及缩放下都不会变形,当需要大幅面的显示时,SVG比起位图的占用空间可谓是沧海一栗,可以显著减小文件大小,减少下载时间。
但是SVG最给力的还是它使用XML来描述,和HTML相似,因此我们可以用与操作DOM相似的方法(CSS,Javascript)来操作它,这可是前端攻城狮们的看家本领啊。 借现代浏览器助力(IE乖乖,出门右转吃糖糖去吧,进化到9以后再回来听大人说话),接下来我们就用SVG来做个交互信息图。
翠花,上SVG
生成SVG的方法真是汗牛充栋,哪怕用手写代码也能哼哧哼哧出来,不过咱是攻城狮,不是攻城狗,累死累活太丑陋,咱们要时刻保持优雅。美工们熟悉的Adobe Illustrator
就能导出SVG,不过如果你差钱,这里有个叫做Inkscape
的免费软件也相当不错,而且它处理SVG还更加专业,更适合我们。 用什么软件都行,但它必须能对图层分组和自定义组名(用作SVG元素的id
属性值),这样我们才能建立SVG的层级结构以方便之后的CSS/Javascript操作。以上提到的两个软件都可以在对象(Object)
->分组(Group)
菜单下实现该功能。 例如在Illustrator
中,双击图层名就可以编辑名称,之后在转化到SVG之后会成为分组id值,所以命名时要避免特殊字符,两个图层也不要重名。 在Inkscape
中,使用Object
> Object Properties
指定组名。也可以用Edit
> XML Editor
来编辑细节,这里面功能更多,除了id
以外,还可以指定class
属性。
在本文例子中,保存好SVG文件后用文本编辑器打开,就会看到下面这些有条理的好孩子们:
<g id="background">
<g id="bg-lines-left"> <!-- left background lines --> </g>
<g id="bg-lines-right"> <!-- right background lines --> </g>
</g>
<g id="logo"> <!-- logo graphics --> </g>
<g id="quote">
<g id="quote-left-brace"> <!-- left quote brace --> </g>
<g id="quote-right-brace"> <!-- right quote brace --> </g>
<g id="quote-text"> <!-- quote text --> </g>
</g>
<g id="timeline">
<g id="coffee">
<rect id="coffee-bar" />
<polyline id="coffee-arrow" />
<g id="coffee-time"> <!-- time text --> </g>
<g id="coffee-badge">
<circle id="coffee-circle" />
<g id="coffee-title"> <!-- title text --> </g>
<g id="coffee-details"> <!-- icon, description --> </g>
</g>
</g>
<g id="design">
<rect id="design-bar" />
<polyline id="design-arrow" />
<g id="design-time"> <!-- time text --> </g>
<g id="design-badge">
<circle id="design-circle" />
<g id="design-title"> <!-- title text --> </g>
<g id="design-details"> <!-- icon, description --> </g>
</g>
</g>
<g id="build">
<rect id="build-bar" />
<polyline id="build-arrow" />
<g id="build-time"> <!-- time text --> </g>
<g id="build-badge">
<circle id="build-circle" />
<g id="build-title"> <!-- title text --> </g>
<g id="build-details"> <!-- icon, description --> </g>
</g>
</g>
<g id="complain">
<rect id="complain-bar" />
<polyline id="complain-arrow" />
<g id="complain-time"> <!-- time text --> </g>
<g id="complain-badge">
<circle id="complain-circle" />
<g id="complain-title"> <!-- title text --> </g>
<g id="complain-details"> <!-- icon, description --> </g>
</g>
</g>
<g id="beer">
<rect id="beer-bar" />
<polyline id="beer-arrow" />
<g id="beer-time"> <!-- time text --> </g>
<g id="beer-badge">
<circle id="beer-circle" />
<g id="beer-title"> <!-- title text --> </g>
<g id="beer-details"> <!-- icon, description --> </g>
</g>
</g>
</g>
以上只给出了SVG文件的骨架,具体内容请看源码。
看着跟HTML有点像吧?它们都使用了XML语法,每一个<g>
标签代表一个分组,亦可以嵌套到别的分组里。SVG不强制每个元素拥有id
属性,不过我们给它们指定id
才方便之后的JavaScript/CSS操作。具体标签含义可以查看亲爱的W3CSCHOOL提供的SVG的教程。
用Javascript将SVG装入HTML页面
The HTML
SVG嵌入旧的HTML有好几种做法,一是用<img>
标签,二是用<embed>
标签,你甚至都可以用CSS的background-image
属性,但是这样显示出的SVG都只是图片,我们无法进入它的内部进行操控。在本文中,要操作SVG内的DOM,我们需要要好好利用HTML5的行内SVG支持特性,直接把它载入HTML文档就行,载入过程可以借助jQuery。
先在HTML里放一个占位的div当做容器:
<div id="stage">
哟哟切克闹~~我是提示文字,当浏览器不支持SVG时候我就会显示,支持的话我就会被SVG覆盖掉了(PД`q。)·。'゜
</div>
JavaScript部分
用jquery.load将SVG文件载入id为stage
的div当中,成功以后给#stage
加上svgLoaded
类名,这样就可以开始我们用CSS定义的动画了(CSS在后面给出)。
$(function(){
$("#stage").load('interactive.svg',function(response){
$(this).addClass("svgLoaded");
if(!response){
// 没有response说明SVG载入失败
//
}
});
});
注意: 如果以本地文件形式打开,出于安全原因(同源策略),Chrome会阻止你读取本地文件(其他浏览器估计也不会允许)。要想本文源码正确工作,请确定你的脚本运行在localhost或是服务器端。
CSS 部分
在本文里所有的示例CSS都没有添加浏览器特性前缀(-webkit-,-moz-这些),但是最终源码的CSS会完整包含。
先指定SVG的容器div,默认情况下SVG会自动撑满它的容器。如果不设定容器大小的话,也许会有不可控状况发生。
#stage {
width: 1024px;
height: 1386px;
}
用CSS为SVG添加样式,设定变形原点
本文的SVG元素动画技术的关键在于设定transform-origin
(变形原点)值,因为默认情况下SVG内元素的transform-origin
都在SVG的原点,即(0px,0px),若要元素正确地变形(旋转,缩放,平移等)则需要将它们的变形原点放到与它们相应的地方(一般来说是圆心)。
咔嘭咔嘭: 原文用了个很笨的方法,一个一个的从
Inkscape
里复制粘贴每个元素的变形原点数据。
#coffee {
transform-origin: 517px 484px;
}
#coffee-badge {
transform-origin: 445px 488px;
}
#coffee-title {
transform-origin: 310px 396px;
}
#coffee-details {
transform-origin: 311px 489px;
}
#design {
transform-origin: 514px 603px;
}
#design-badge {
transform-origin: 580px 606px;
}
#design-title {
transform-origin: 712px 513px;
}
#design-details {
transform-origin: 710px 620px;
}
#build {
transform-origin: 511px 769px;
}
#build-badge {
transform-origin: 445px 775px;
}
#build-title {
transform-origin: 312px 680px;
}
#build-details {
transform-origin: 310px 790px;
}
#complain {
transform-origin: 512px 1002px;
}
#complain-badge {
transform-origin: 586px 1000px;
}
#complain-title {
transform-origin: 718px 921px;
}
#complain-details {
transform-origin: 717px 1021px;
}
#beer {
transform-origin: 513px 1199px;
}
#beer-badge {
transform-origin: 444px 1193px;
}
#beer-title {
transform-origin: 313px 1097px;
}
#beer-details {
transform-origin: 316px 1202px;
}
Apply some initial transformations
在我们用Inkscape
创建的SVG文件里,给#timeline
组中一些元素设立了'hover(鼠标悬停)'效果,接下来我们要用CSS3属性选择器强大的后缀选择功能(功能强大咱就能少写几行字了)来指定元素们在正常情况下的样子。
[id$=badge] { /* id以'badge'结束的所有元素 */
transform: scale(0.5, 0.5);
}
[id$=title] { /* id以'title'结束的所有元素 */
transform: scale(1.8) translate(0px, 48px);
}
[id$=details] { /* id以'details'结束的所有元素 */
transform: scale(0, 0);
}
在Inkscape中可以指定class
属性(即类名),所以你也可以用.someEle
来选择类名为'someEle'的元素。 但是如果你把SVG文件导入Illustrator,再次保存时它会去掉它不认识的class
属性,造成信息丢失。所以说编辑SVG还是Inkscape比较保险。
加入:hover鼠标悬停效果
现在我们要给在id为'timeline'的组内的那些id以'badge','details'或者'tittle'结尾的元素加上点效果,我们添上了transition
属性实现一点动画渐变效果。
#timeline > g:hover [id$=badge], #timeline > g:hover [id$=details] {
transform: scale(1, 1);
}
#timeline > g:hover [id$=title] {
transform: scale(1) translate(0px, 0px);
}
[id$=badge], [id$=title], [id$=details] {
transition: transform 0.25s ease-in-out;
}
开场动画来一个
要实现一个流逼流畅又丰富的动画,要敲的代码还真不算少,定制CSS动画的方法之一就是制定keyframes
(动画关键帧)。
以下使用了translateX,所以如果你的浏览器不支持CSS的3D变换的话,那就没得玩了。
@keyframes left-brace-intro {
0% {
transform: translateX(220px);
opacity: 0;
}
50% {
opacity: 1;
transform: translateX(220px);
}
100% {
transform: translateX(0px);
}
}
@keyframes right-brace-intro {
0% {
transform: translateX(-220px);
opacity: 0;
}
50% {
opacity: 1;
transform: translateX(-220px);
}
100% {
transform: translateX(0px);
}
}
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes grow-y {
0% {
transform: scaleY(0);
}
100% {
transform: scaleY(1);
}
}
@keyframes grow-x {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
@keyframes grow {
0% {
transform: scale(0, 0);
}
100% {
transform: scale(1, 1);
}
}
动画keyframe(关键帧)定义好了以后,我们只要动用CSS选择器,给元素的animation
属性指定动画名和相应设置。这里的animation-fill-mode: backwards
表示在动画结束后元素会回到初始(也就是keyframes中的0%)状态。
.svgLoaded #logo {
animation: fade-in 0.5s ease-in-out;
//简写方法,分别对应animation-name,animation-duration,animation-timing-function
}
.svgLoaded #quote-text {
animation: fade-in 0.5s ease-in-out 0.75s;
animation-fill-mode: backwards;
}
.svgLoaded #quote-left-brace {
animation: left-brace-intro 1s ease-in-out 0.25s;
animation-fill-mode: backwards;
}
.svgLoaded #quote-right-brace {
animation: right-brace-intro 1s ease-in-out 0.25s;
animation-fill-mode: backwards;
}
.svgLoaded #background {
animation: grow-y 0.5s ease-in-out 1.25s;
transform-origin: 512px 300px;
animation-fill-mode: backwards;
}
.svgLoaded #background > g {
animation: grow-x 0.25s ease-in-out 1.75s;
animation-fill-mode: backwards;
}
.svgLoaded #background > g:last-of-type {
transform-origin: 458px 877px;
}
.svgLoaded #background > g:first-of-type {
transform-origin: 563px 877px;
}
.svgLoaded #coffee, .svgLoaded #design, .svgLoaded #build, .svgLoaded #complain, .svgLoaded #beer {
animation: grow 0.25s ease-in-out;
animation-fill-mode: backwards;
}
.svgLoaded #coffee {
animation-delay: 2s;
}
.svgLoaded #design {
animation-delay: 2.25s;
}
.svgLoaded #build {
animation-delay: 2.5s;
}
.svgLoaded #complain {
animation-delay: 2.75s;
}
.svgLoaded #beer {
animation-delay: 3s;
}
WEB自定义字体
本例中我们使用了非WEB标准字体LeagueGothic
,导出成SVG文件后,会用font-family
标出:
<!-- .SVG文件部分内容. -->
<text font-family="'LeagueGothic'" font-size="28">12PM</text>
<!-- ... -->
在CSS中记住要用@font-face
定义这种字体以及字体文件所在位置:
<!-- .CSS文件部分内容. -->
@font-face {
font-family: 'LeagueGothic';
url("../fonts/league-gothic/league-gothic.eot.woff") format("woff");
}
该说的都说了,大家来这里看看效果,如果看不到图片或者没有动态效果,请确定你的浏览器支持SVG显示以及CSS3的3D变换效果,在这里给chrome做个广告,你绝对值得拥有居家旅行杀人放火必备浏览器。
想自己琢磨多点的,可以先把源码抱回去。
咔嘭呛后话
本文中用CSS3实现变换和动画,其实SVG本身也支持变形动画,效果也挺不错。具体可以看微软的教程。
再推荐一个我最近很喜欢的开源Javascript项目Raphael,作者是Adobe的JS大牛Dmitry Baranovskiy,Raphael
封装了强大的SVG绘画和动画实现,API清晰,容易扩展,可以用来做互动图表和游戏,官网有几个不错的demo,你也可以去它的github仓库看看相关项目和资料。