[译]Google HTML/CSS Style Guide 谷歌代码风格指南

译自:Google HTML/CSS Style Guide

前言

本文档定义了HTML和CSS的格式和样式规则,旨在改善协作编码,代码质量和规范基本结构。它适用于使用HTML和CSS的源代码文件,也包括全局文件。该工具免费用于混淆、压缩和编译代码,同时保持通用代码质量。

通用样式规则

协议

省略嵌入资源的协议:对于图像、媒体文件、样式表、脚本文件等,除非该资源请求无法对http:和https:同时可用,否则省略协议部分。URL地址变成相对地址,也节省了文件字节数。

<!-- 不推荐 -->
<script src="http://www.google.com/js/gweb/analytics/autotrack.js"></script>
<!-- 推荐 -->
<script src="//www.google.com/js/gweb/analytics/autotrack.js"></script>
/* 不推荐 */
.example {
  background: url(http://www.google.com/images/example);
}
/* 推荐 */
.example {
  background: url(//www.google.com/images/example);
}

通用格式化规则

缩进

使用2个空格,而不使用tab或者混用空格+tab作为缩进

<ul>
  <li>Fantastic
  <li>Great
</ul>
.example {
  color: blue;
}

大小写

所有代码只使用小写:适用于元素名,选择器,(样式和标签的)属性及属性值(text/CDATA例外,作为内容时例外)

<!-- 不推荐 -->
<A HREF="/">Home</A>
<!-- 推荐 -->
<img src="google.png" alt="Google">

尾随空格

一行中尾随的空格是多余的,可能会引起代码混乱

<!-- 不推荐 -->
<p>What?_
<!-- 推荐 -->
<p>Yes please.

通用Meta规则

编码

使用UTF-8(非BOM),在HTML中指定UTF-8编码,在CSS中则不需要特别指定因为默认就是UTF-8,更多编码和指定方式的资料可以参见Character Sets & Encodings in XHTML, HTML and CSS

<meta charset="utf-8">

注释

根据需要,在可能的情况下给代码做注释:这块实现了什么功能,它的目的是什么,为什么这种方案更好?(注释代码不是强制要求,视乎项目性质和复杂程度)

待办事项

使用”TODO”关键字高亮标识待办事项,而不用其他格式比如”@@”;使用”TODO(contact)”的形式附上联系方式(名称和邮件列表)方便联系;在冒号后加入待办事项的内容

{# TODO(john.doe): revisit centering #}
<center>Test</center>
<!-- TODO: remove optional tags -->
<ul>
  <li>Apples</li>
  <li>Oranges</li>
</ul>

HTML代码规则

文档类型

使用HTML5,首选<!DOCTYPE html>:推荐使用HTML(text/html)而非XHTML(application/xhtml+xml),原因一是浏览器基础支持缺失,二是优化空间比HTML更小

HTML合法性

使用合法的HTML标签,除非你更在意那一点点节省的文件体积;使用工具如W3C HTML验证;使用合法的HTML标签是可被衡量的规范,有助于编码者学习的技术要求和限制,并保证代码质量。

<!-- 不推荐 -->
<title>Test</title>
<article>This is only a test.
<!-- 推荐 -->
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test</title>
<article>This is only a test.</article>

语义化

符合本义地使用元素(标签),比如在头部区域使用heading元素,使用p元素表示段落,使用a元素表示链接等;语义化地使用HTML有助于网页的可访问性,复用性,和优化代码效率。

<!-- 不推荐d -->
<div onclick="goToRecommendations();">All recommendations</div>
<!-- 推荐 -->
<a href="recommendations/">All recommendations</a>

多媒体备选内容

为多媒体元素(图像、视频音频、cavas创建的动态对象)提供替代内容,对图像来说可以在alt中加上有意义的文本内容,对视频音频来说可以加上描述性的元素。提供多媒体的替代内容在网页可访问性上是重要的:一个盲人用户可以通过alt线索得知关于这个图像的信息,或者其他用户在无法理解图像内容的时候有用。对于不是在css中引用的非内容图就不要使用alt描述了。

<!-- 不推荐 -->
<img src="spreadsheet.png">
<!-- 推荐 -->
<img src="spreadsheet.png" alt="Spreadsheet screenshot.">

行为、呈现与结构分离

保持结构(标记)、呈现(样式)、和行为(脚本)严格分离,并最小化三者的相互作用。HTML文件中只包含结构代码,样式语句放在样式表文件中,行为语句放在脚本文件中,在HTML文档中去引用样式表和脚本文件。分离的重要性在于,如果你要在HTML文档中更改样式和行为,你付出的成本会更高。

<!-- 不推荐 -->
<title>HTML sucks</title>
<link rel="stylesheet" href="base.css" media="screen">
<link rel="stylesheet" href="grid.css" media="screen">
<link rel="stylesheet" href="print.css" media="print">
<h1 style="font-size: 1em;">HTML sucks</h1>
<p>I’ve read about this on a few sites but now I’m sure:
  <u>HTML is stupid!!1</u>
<center>I can’t believe there’s no way to control the styling of
  my website without doing everything all over again!</center>
 
<!-- 推荐 -->
<title>My first CSS-only redesign</title>
<link rel="stylesheet" href="default.css">
<h1>My first CSS-only redesign</h1>
<p>I’ve read about this on a few sites but today I’m actually
  doing it: separating concerns and avoiding anything in the HTML of
  my website that is presentational.
<p>It’s awesome!

转义符

在普通的符号比如 — ” 或者 ☺ 上没必要使用转义符,而对于 > < & 和  (空格)则推荐用转义符,可以避免与HTML中标签混淆

<!-- 不推荐 -->
The currency symbol for the Euro is &ldquo;&eur;&rdquo;.
<!-- 推荐 -->
The currency symbol for the Euro is “€”.

可选标签

省略可选的标签:为了代码文件的可读性和体积的优化,可以考虑省略可选的标签。HTML5 specification定义了哪些标签是可以被省略的。(由于网站开发人员的技术背景差异,应用这种方法需要一段足够的缓冲期;基于一致性和简单的原则,最好是省略所有的可选标签,而不只是一两个。

<!-- 不推荐 -->
<!DOCTYPE html>
<html>
  <head>
    <title>Spending money, spending bytes</title>
  </head>
  <body>
    <p>Sic.</p>
  </body>
</html>
 
<!-- 推荐 -->
<!DOCTYPE html>
<title>Saving money, saving bytes</title>
<p>Qed.

引用类型

省略样式表和脚本文件的引用类型,(除非你引用的不是CSS或者JavaScript)。HTML5默认使用text/css和text/javascript,因此声明引用类型不是必要的,这对于较老的浏览器也适用。

<!-- 不推荐 -->
<link rel="stylesheet" href="//www.google.com/css/maia.css" type="text/css">
<!-- 推荐 -->
<link rel="stylesheet" href="//www.google.com/css/maia.css">
<!-- 不推荐 -->
<script src="//www.google.com/js/gweb/analytics/autotrack.js" type="text/javascript"></script>
<!-- 推荐 -->
<script src="//www.google.com/js/gweb/analytics/autotrack.js"></script>

HTML格式化规则

通用格式化

为下一个block元素、list元素或者table元素使用另起一行,并缩进此类的每个子元素。如果因为list元素中的空格占位问题而把list元素放置在同一行中也是可以接受的,对此语法检查器应该警告而不是提示错误。

<blockquote>
  <p><em>Space</em>, the final frontier.</p>
</blockquote>
<ul>
  <li>Moe
  <li>Larry
  <li>Curly
</ul>
<table>
  <thead>
    <tr>
      <th scope="col">Income
      <th scope="col">Taxes
  <tbody>
    <tr>
      <td>$ 5.00
      <td>$ 4.50
</table>

CSS代码规则

CSS验证

尽量使用CSS验证,除非因为修正浏览器解析差异而编写私有属性,否则都应该使用合法的CSS代码。可以使用W3C CSS validator测试你的CSS。使用合法的CSS同样是一个可被衡量的规则,它能标记出那些不起作用和可被删除的代码,并确保CSS的正确使用。

ID和class命名

使用有意义的或通用的ID和class命名:ID和class的命名应反映该元素的功能或使用通用名称,而不要用抽象的晦涩的命名。反映元素的使用目的是首选;使用通用名称代表该元素不表特定意义,与其同级元素无异,通常是用于辅助命名;使用功能性或通用的名称可以更适用于文档或模版变化的情况。

/* 不推荐: 无意义 */
#yee-1901 {}
/* 不推荐: 与样式相关 */
.button-green {}
.clear {}
 
/* 推荐: 特殊性 */
#gallery {}
#login {}
.video {}
 
/* 推荐: 通用性 */
.aux {}
.alt {}

ID和class命名风格

越简短越好,只要足够表达涵义。这样既有助于理解,也能提高代码效率。

/* 不推荐 */
#navigation {}
.atr {}
/* 推荐 */
#nav {}
.author {}

类型选择器

避免同时使用标签、ID和class作为定位一个元素选择器;从性能上考虑也应尽量减少选择器的层级。

/* 不推荐 */
ul#example {}
div.error {}
/* 推荐 */
#example {}
.error {}

属性连写

尽量使用属性连写:CSS语法提供了大量支持连写的属性(比如font),即使只有一个值需要表达的情况,连写也能提高代码效率和可读性。

/* 不推荐 */
border-top-style: none;
font-family: palatino, georgia, serif;
font-size: 100%;
line-height: 1.6;
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
 
/* 推荐 */
border-top: 0;
font: 100%/1.6 palatino, georgia, serif;
padding: 0 1em 2em;

0与单位

属性值为0的情况下请省略单位,除非被要求加上。

margin: 0;
padding: 0;

小数点前的0

小数点前的0请省略。

font-size: .8em;

URL中的引号

URL中的引号(”"或”)请省略。

@import url(//www.google.com/css/go.css);

十六进制

可以的情况下用3个字符来表达6个十六进制字符的信息。

/* 不推荐 */
color: #eebbcc;
/* 推荐 */
color: #ebc;

前缀

对ID和class使用带前缀的命名(短破折号连接),在大型项目中,可以防止命名冲突,配合查找快速定位,便于维护。

.adw-help {} /* AdWords */
#maia-note {} /* Maia */

分隔符

对ID和class使用分隔符连接单词(分隔符只能有一种,如果你选择了短破折号就不要再用下划线或者其他连字符)以便于理解和扫描。

/* 不推荐: 没有分离单词demo和image */
.demoimage {}
/* 不推荐: 应使用短破折号 */
.error_status {}
/* 推荐 */
#video-id {}
.ads-sample {}

Hacks

请不用动不动就使用浏览器检测和CSS Hacks,先试试别的解决方法吧!考虑到代码高效率和易管理,虽然这两种方法能快速解决浏览器解析差异,但应被视为最后的手段。在长期的项目中,允许使用hack只会带来更多的hack,你越是使用它,你越是会依赖它!

CSS格式化规则

声明顺序

按字母序排列依次声明样式属性有助于代码一致性和易记性,如果是带前缀的多个私有属性则忽略该原则,但多个不同前缀的私有属性又需要遵循(-moz-应该在-webkit-前面)

background: fuchsia;
border: 1px solid;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
color: black;
text-align: center;
text-indent: 2em;

整块内容缩进

所有整块的内容都应该缩进,包括规则块和样式声明规则,利于清晰反映层次提高可读性。

@media screen, projection {
  html {
    background: #fff;
    color: #444;
  }
}

声明结束符

每个声明结束都应该带一个分号,不管是不是最后一个声明

/* 不推荐 */
.test {
  display: block;
  height: 100px
}
/* 推荐 */
.test {
  display: block;
  height: 100px;
}

属性名分隔

每个属性与属性值之间用一个空格分隔,位于冒号之后。

/* 不推荐 */
h3 {
  font-weight:bold;
}
/* 推荐 */
h3 {
  font-weight: bold;
}

选择器和声明分离

选择器和声明应各占一行,多个选择器的情况每个选择器独占一行

/* 不推荐 */
a:focus, a:active {
  position: relative; top: 1px;
}
/* 推荐 */
h1,
h2,
h3 {
  font-weight: normal;
  line-height: 1.2;
}

规则分离

每个声明里的规则都另起一行

html {
  background: #fff;
}
 
body {
  margin: auto;
  width: 50%;
}

CSS的Meta规则

区块注释

对一个代码区块注释(可选),将样式语句分区块并在新行中对其注释。

/* Header */
 
#adw-header {}
 
/* Footer */
 
#adw-footer {}
 
/* Gallery */
 
.adw-gallery {}

结语

如果你正在编辑一份代码,不妨花几分钟时间审视一下你周围代码的风格,把你的代码风格与它们保持一致。

有一份共同的代码风格指引,可以使代码阅读者专注于你代码的内容而不是你怎样组织你的代码。我们在这里向人们公布我们的全局代码风格规则,但其实局部代码风格也是很重要的。如果你添加到文件里的代码看起来与周围的代码风格大相径庭,会使下一个阅读者无所适从,请避免这种情况!

使用Chrome Frame技术开发者小指南

Google Chrome Frame,谷歌浏览器内嵌框架(简称GCF),是一个使你机器上的Internet Explorer系列浏览器鸟枪换炮,用上webkit内核的Chrome引擎,但IE浏览器外观上还是IE的外观的免费插件。

不相信吗?使用用IE浏览器打开GCF安装页,按照提示安装后,重启IE,再打开gcf:about:version,现在你会看到chrome浏览器的内核信息,说明Chrome内核已经植根于你的IE浏览器上了。

但并非你的IE访问任意网页就会自动的使用Chrome内核来解析,需要两种方式:一是在网址前面加上”gcf:“,比如gcf:http://gmail.com来访问;二是在网页的meta信息中加入一句:

<meta http-equiv="X-UA-Compatible" content="chrome=1">

这里chrome=1代表所有版本的IE浏览器都使用Chrome内核解析网页,chrome属性还有其他的值,比如chrome=IE7,代表IE7或以下版本的浏览器才使用chrome内核,chrome=IE6chrome=IE8等依此类推。

作为一个网页开发者,你会不会想到有了这个插件,以后就可以大胆使用各种CSS3样式,只要Chrome下显示正确,IE下也能显示正确了!没错!但前提是用户会安装这个插件吗?如何提供一个友好的引导安装界面呢,Google帮我们解决了这个问题。

    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
    <div id="prompt"></div>
    <script>
     window.attachEvent("onload", function() {
       CFInstall.check({
         mode: "overlay", 
         node: "prompt"
       });
     });
    </script>

在body标签中加入这段js代码,可以使得IE打开该网页时出现友好的GCF安装引导iframe框。这段代码不需要存在于

<!--[if IE]>...<![endif]-->

之中,js中已经做了浏览器的判断。

CFInstall.min.js是官方提供的文件,CFInstall.check()方法有许多可选项,其中包括:

  • mode: “inline” 默认值,GCF安装引导的iframe结构将存在于node选项指定id的元素中最前面位置,属于文档流的一部分
  • mode: “overlay” 该iframe以弹出层显示,弹出层将会在页面可视范围内居中
  • mode: “popup” 该iframe以新开窗口/选项卡显示,类似于target:_blank的效果
  • node: “” 指定iframe结构的dom结点位置,在mode:”inline”下有效
  • url: “” 点击安装按钮跳转到的链接地址,默认为GCF安装文件地址
  • destination: “” GCF安装完成后页面跳转到的链接地址
  • className: “” 在mode:”inline”下对iframe指定新的class名,美化iframe界面时很有用,默认的class为chromeFrameInstallDefaultStyle

OK,了解了这么多,相信你已经跃跃欲试了,我在我的博客上开启了chrome=IE8并加上了GCF的友好安装指引,欢迎访问比较IE下和Chrome下的渲染差异。

更多GCF的参考资料:

【译】利于Web开发的编码工具和JS库

原文:《Useful Coding Tools and JavaScript Libraries For Web Developers》,本文为选译,译者筛选出了其中18个工具/js库,翻译于2011年11月02日(据说是对称日)。

Livereload:Web开发的仙境
Web开发既有愉快的有创意的工作,也有单调乏味的任务。可能最烦的就是开发和调试过程中每次更改页面就得在浏览器中重新载入一次。Livereload是一个可以监视你文件系统中变化的桌面应用程序,只要你保存了一个按照需要被预处理的文件,就会刷新浏览器。而且每次你修改CSS文件或者图像文件,浏览器都会即时更新而无需重载整个页面。这工具支持CoffeeScript, SASS/SCSS, LESS, Stylus, HAML 和 Jade, 并将它们都内置了。目前只运行在Mac平台上。

The Web Developer’s Wonderland

Ender: 大型JavaScript库的终结
Ender允许你搜索、安装、管理和编译前端JavaScript包以及其依赖。本质上讲,它是一个命令行工具,可以让你合并和混合所有流行和小型的JavaScript库,从而创建你自己的个人开发库。如果其中一个库坏了或者被禁用了,Ender会帮你快速找到另一个来替代。如果你需要一个特殊版本的软件包,它同样可以帮到你。发布页包含详细文档,使用指引和一些视频教程。不浪费你的带宽!

Ender: the end of monolithic JS libraries

Tilt:以3D视角检视DOM的Firefox扩展
你有多少时间是花费在用Firebug遍历DOM,探寻节点之间的层级关系,分析代码的结构并试图操纵它与令人讨厌的(或许你不那么讨厌)的JavaScript上呢?好吧,也许你也想要尝试不同的方法来做DOM检查。Mozilla的新工具,Tilt这个Firefox扩展让你构想一下以3D形式展现任何Web页面的DOM树。因为DOM本质上代表的是一种文档树,该工具的开发者决定去通过创建树上(像巢一样放置)的元素节点,每一个节点都有对应的深度,并配合网页的质感来呈现。

Tilt Firefox Extension: DOM Inspection in 3D

Mou-Markdown,Mac OS X上的web开发者编辑器
目前我们可用的Markdown编辑器几乎都是面向一般编辑者的,Mou是与众不同的:它只面向web开发者。语法高亮显示,实时预览,全屏模式,自动保存,有力动作,自动匹配,渐进式搜索,自定制主题,HTML输出,增强的CJK字符支持。这正是你想要的应用程序。

Mou - Markdown editor for web developers, on Mac OS X

Bootstrap 启动开发工具包
Bootstrap是一个工具包,包含了基本的CSS和HTML用于排版,表格,网格,导航,错误消息,组件模块,按钮和表单。它基于LESS框架而建立。它配备的是940px网格(如果不算上两边的margin值就是960px),你也可以自己创建一个。Bootstrap允许你创建fixed或者流体的布局,带有许多元素可以用或者被重新设计来适合你的网站。当然,这个工具包是可供免费使用的。

Kickstart Your Website and App Development

Colour书签
拖拽Colour书签链接到你的浏览器工具栏上,就可以开启你当前访问的网站的取色器。然后就很简单了:复制,粘贴并使用你选中的颜色。

Colour Bookmark

Leaflet:基于JavaScript的开源互动地图
这个库提供了一系列的地图图层,包括纹理,标记,弹窗,图像叠加层和GeoJSON。它支持移动端和桌面端的浏览器上平移,移动端浏览器上的双击缩放(在iOS上还有多点触控缩放)等等。在iOS上,硬件加速是启用的,并且有一个模块化的结构使你的库文件更小运行起来更快。这个项目是开源的,并且可以在GitHub上进一步开发和进行分支。

Leaflet

Weinre
weinre是一个远程Web检视器,本质上是一个网页调试工具,除了它是为远程工作而设计这一点,其他就跟(基于Firefox的)FireBug差不多。特别的地方在于,它可以使你在移动终端例如手机上调试Web页面。

Weinre

Aardwolf:远程JavaScript调试器
手机浏览器正变得越来越强大,你可以做几乎所有你在桌面浏览器上同样的事情。对于开发者来说一个主要关注的问题就是缺少开发工具。原因是很明显的 — 真实的情况需要展示给调试者,非开发者一个友好的界面。解决这个问题正是远程调试。你可以使用JSConsole来达到这个目的,但当涉及到JavaScript调试,Aardwolf是更好的选择。Aardwolf是一个JavaScript调试工具,面向iPhone / Android / WindowsPhone 7 / BlackBerry OS 6+平台.

Varun's ScratchPad: JavaScript Remote Debugging

IE Vms
微软提供的虚拟机磁盘映像,以方便的在多个IE版本中测试网站,而不用管你的主机是什么操作系统。但之前如果没有微软的VitrualPC,设置这些虚拟机是很困难的。ievms脚本的目的就是推动实现在基于Linux或OS X的VirtualBox上也能使用IE多版本虚拟机。只要一个命令,你就能同时拥有分别跑在不同虚拟机上的IE7, IE8 和 IE9。

IE Vms

WhatFont
这个工具可以使你轻松获取你当前鼠标悬停的文字CSS排版细节。

WhatFont

WordPress TextMate Bundle
WordPress TextMate Bundle是只为了一个目的:大量减少花费在扩展Wordpress内核上的时间,并改善每天我们遇到的小事情。该插件的功能包括Wordpress函数自动完成,常用代码片段,和Wordpress组件模版。我们甚至悄悄想实现Carrington模版框架函数的自动完成。我们一直在改善,因为我们不断找到想要加入这个插件的功能(从WordPress MU与WordPress 3.0 code base consolidation合并)。

WordPress TextMate Bundle

cubic-bezier previewer(三次贝塞尔曲线预览)
不管你看过多少次别人修改参数,如果你自己没有在2D平面上绘制过它的话,是无法理解三次贝塞尔曲线运动是怎么一回事。Lea Verou寻找一个工具可以用来展示贝塞尔曲线是如何形成的。找到很多,但它们都是有坐标限制的,只在0-1范围内。Lea于是创建了自己的三次贝塞尔曲线生成器。

cubic-bezier.com

Patternizer – 条纹图案生成工具
使用Patternizer可以很容易在几分钟内做出一些令人惊讶的事情。
它所做的工作就是帮你创造复杂的模式,让你专注于创意和发挥。模式可以保存也可以与他人共享,允许多人协作和混合。你可以用全球任何设备进行访问。

Patternizer - Stripe Pattern Generator Tool

Chainvas
chainvas是一个轻量级模块化的JavaScript开发包,可以对本身不是链接的API添加链接,比如Canvas API, DOM 和其它。

Chainvas

Comparison Table Generator
这个表格生成器可以让你轻松创建漂亮的HTML/CSS动态对照表格。

Compare Ninja Table Generator

DropKick.js
一个自定制下拉框,优雅降级的jQuery插件。如果用户禁用了JavaScript,下拉框也会使用常规的<select>正常显示,它用于IE7以上的浏览器。

DropKick.js

-prefixfree
-prefix-free可以使你到处使用不带前缀的CSS属性。它在后台工作,帮你自动对任意CSS代码加上当前浏览器的前缀,如果需要的话。

Prefixfree

【译】CSS 3D变换概述与示例

原文:David DeSandroAn Introduction to CSS 3-D Transforms》 翻译于2011年10月30日

各位亲爱的童鞋们,虽然现在已经是二十一世纪的第二个十年了,但我们还是像三十年前一样面对同样的2D界面。可以确定的是,苹果公司已经新推出了一些基于OSX 10.7的拥有3D效果的应用,微软也有Flip 3D这样的技术了。现在是2011年啊亲们,我们的3D虚拟现实在哪里呢?现在是时候像 Metaverse on super-sonic motorbikes 一样冲破枷锁了.

诚然,渲染复杂3D环境的能力已存在多年。在Web上,有以下几种解决方案: Flash; 使用<canvas>three.js; 以及, WebGL. 终于,我们这些苦逼的前端开发者们有了自己的3D神器: CSS 3D transforms(CSS的3D变换)!

理由

就像一颗璀璨的宝石,3D变换可以让人眼花缭乱,像是一个华丽的奇观。但我们正如一个Liberace的裁缝,在把宝石缝到衣服上之前,得想想穿这件衣服的人是不是真的会变得很好看。

实际上这里提到的3D变换对客户端程序没太大帮助。 CSS是写在样式文档里头,而不是在浏览器环境上的。我不能从一个拥有牛逼的旋转视角功能的注册页(尽管这只是对一个web页的建议)找到任何好处。不过,通过合理的过渡,还是有很多场合可以用3D变换来使我们的页面交互看起来更上流。

举个栗子吧,iPhone上有个天气的应用,就使用了两个浏览页:一个详细页和一个选项页。在这两个页面中切换是通过3D翻转来过渡的。这就告知了用户有且只有两个浏览页,并且它们存在于同个平面的两侧。

iPhone Weather App 3D flip transition
使用3D过渡使详细页翻转到选项页的效果

此外,考虑一下幻灯片滑动的展示,当你看到最后一张的时候,有什么线索可以提示你从第一张重新开始一次新的循环呢?3D变换有一个很好的方法,将幻灯片放置在一个三维空间的圆转盘上,这样你可以就很自然的从最后一张过渡到第一张幻灯片上。

3D变换不仅只是带来视觉上的享受,我们同样可以用它解决一些难题以及获得更直观的体验。

目前支持情况

CSS 3D变换模块 已经推出一年多了。目前只有Safari浏览器支持这个规范,包括Mac OS X上的Safari和iOS上的Safari。

其他浏览器支持的情况也不尽相同。Mozilla 团队已经在实施这个模块上 取得了初步的进展Mike Taylor告诉我们Opera团队也对CSS变换密切关注着,并迫不及待的希望规范尽快出炉。还有我们最好的朋友Internet Explorer在我们可以对3D特性高谈阔论之前,还依旧需要在2D变换上亦步亦趋地跟进。

情况还可以更复杂点,Safari有个Webkit内核的表亲Chrome目前已经支持3D变换的样式声明,但使用2D空间来渲染页面。 Chrome团队成员 Paul Irish说即将迎来3D变换,很可能就在下一个8.0版本。(译者注:亲现在都快版本号17了喂)

(译者注:截止到翻译日,浏览器支持的情况与原文有较大出入,现在包括IE在内的现代浏览器最新版本都支持这个属性,具体参照caniuse.com;另外尽管已经有许多浏览器支持这个属性,本文中的示例都只是当时针对webkit内核编写的,所以请使用webkit内核的浏览器打开)

这会让使得我们激动的3D变换实施起来遭遇或多或少的挑战。我直接这样跟你说吧:无视维度差异将会使得体验降级不太优雅。除非变换相对简单并且在非3D支持的浏览器也能“hold”住,否则你还是去设计别的思路吧。另一个阻碍我们的是,我们设计的web网页得经得起时间的考验。我们已经准备制定多个解决方案了。

这里是我文章中提到Modernizr(Modernizr 是一个利用 JS 和 CSS 来检测浏览器所支持功能的小工具)的一处,不过估计会被你们忽略掉,因为大家在这类文章中看到过太多了。你至少可以在你现在用的浏览器尝试一下。重要的是,它或许是最好的方式去测试CSS 3D变换的支持情况,用起来吧!

尽管应用3D变换困难重重,但果断尝试才是正确的举措。CSS 3D变换是推出 CSS 2D变换Animation模块的Apple团队所开发的。这份规范同样有来自 Mozilla 和 Opera 各自团队的贡献。现在3维变换的支持可以使厂商在浏览器的角逐中占据有利位置。

选择在你手上呢,亲们。你可以找各种借口或者是鄙视3D变换因为这孩子目前只受Apple粉丝待见,实施起来太困难了。否则,带上这份tip of the fedora to Mr Andy Clarke,你可以迎难而上,马上着手进行最华丽视觉体验的设计。

因此,让我用永恒擎天柱的话来跟你说…

Transform and roll out.

Let’s get coding.让我们开始编码吧。

视角

元素需要perspective(视角)属性才能激活3D空间状态。有两种方式可以实现:使用transform属性,perspective作为函数值:

-webkit-transform: perspective(600);

或者直接使用 perspective 属性:

-webkit-perspective: 600;

例子: Perspective 1.

Perspective example
左边红色的元素使用的是 transform: perspective() 函数表示;右边蓝色的元素则使用 perspective 属性

这两种格式都会触发3维空间,但它们也有不同点。首先,函数表示对于在单个元素上直接应用3D变换是很方便的,(正如之前的例子上同时结合了一个rotateY的变换)。但当要应用到多个元素时,应用3D变换的元素看起来可能会跟预期不符了。在不同位置的元素上使用transform,每个元素将有它自己独立的原点。要解决这个问题,在它们的父元素上使用perspective属性,这样每个子元素都会存在于同一个3维空间。

例子: Perspective 2.

Perspective example
左边的每个红色盒子都有它自己的在父容器中的原点;右边蓝色的盒子则共享父容器的原点

perspective的值决定3D效果的强度。想象自己是一个物体前方的观察者,值越大,你与物体的距离越远,所以视觉效果也就越弱。perspective: 2000;只产生了微妙的3D效果,就像我们在远处看这个物体一样。perspective: 100;则产生强烈的3D效果,就像一个小昆虫在观察一个大的物体。

默认状态下,3D空间的原点是在它的中心点。你可以使用perspective-origin属性来改变原点的位置。

-webkit-perspective-origin: 25% 75%;

例子: Perspective 3.

Intense perspective value, with vanishing point modified

3D变换函数

作为一个web设计者,你可能已经在二维空间上处理得得心应手了,X和Y坐标,水平定位和垂直定位。在用perspective初始化3维空间后,我们现在就可以在第三个维度Z上来变换元素了。

3D变换使用跟2D变换一样的transform属性。如果你对2D变换很熟悉,你会发现它跟基本的3D变换函数几乎一样。

  • rotateX(angle)(X轴旋转)
  • rotateY(angle)
  • rotateZ(angle)
  • translateZ(tz)(Z轴平移)
  • scaleZ(sz)(Z轴缩放)

正如translateX()通过横向的X轴来定位元素,translateZ()则通过Z轴来定位,也就是3维空间的前-后方向。正值使得元素与观察者更近,负值则是更远。

rotate函数通过指定的维度翻转元素。起初你可能会觉得跟你的直觉不符,你可能会想象rotateX将物体从左至右旋转。恰好相反的是,使用rotateX(45deg)将使得物体绕着横向的X轴旋转,因此该物体的头部将一定角度地远离观察者而底部则变得更贴近观察者了。

例子: Transforms 1.

CSS 3D transform functions
3-D rotate()translate()函数在各个维度上的展示

transform函数有一些缩写的方式,要求所有3个维度都有确定值:

  • translate3d(tx,ty,tz)
  • scale3d(sx,sy,sz)
  • rotate3d(rx,ry,rz,angle)

Pro-tip:这些foo3d()变换函数还有一个好处,在Safari下可以触发硬件加速。Dean Jackson, CSS 3D变换规范的作者也是WebKit引擎的主要贡献者,在给Thomas Fuchs的邮件中写到:

从本质上讲,任何3D变换的操作都应当触发硬件加速,即使实际上进行的只是2D变换,或者什么都不做(如translate3d(0,0,0)).请注意这只是目前的情况,可能会在未来得到改变(这也是为什么我们不鼓励这种行为的原因)。硬件加速在某些情况下非常有帮助,可以显著提高渲染的性能。

为了简单起见,我的demo只使用基本的变换函数,假如你正在为iOS平台或者只针对Safari编写样式,确保你使用的是foo3d() 的函数形式可以获得最佳的渲染性能。

卡片翻转

我们已经有开始绘制3D物体的所有必备条件了,让我们从一个简单的例子开始:翻转一张卡片。

你需要一些基本的标签:

<section class="container">
  <div id="card">
    <figure class="front">1</figure>
    <figure class="back">2</figure>
  </div>
</section>

.container将装载一个3D空间,#card包裹3D实体。卡片的每一面都有自己的类名.front.back。即使是这样简单的物体,我要求使用相同的3D变换模式,建立一个3D空间元素和具体对象元素分离的模式,既简单易懂,又利于编写样式。

我们已经准备好一些3D样式了,首先对于开启3D空间来说perspective是必须的,同时要带有任何大小或者定位的样式。

.container {
  width: 200px;
  height: 260px;
  position: relative;
  -webkit-perspective: 800;
}

现在#card元素可以在它父级的3D空间中变换了。我们结合绝对定位和相对定位使得该物体脱离文档流。再加上一个width: 100%;height: 100%;属性,可以确保物体的transform-origin(变换原点)是在整个.container的中心点,过一会儿再改变transform-origin的值。

让我们加一些CSS3的过渡样式使得变换看起来生效了。

#card {
  width: 100%;
  height: 100%;
  position: absolute;
  -webkit-transform-style: preserve-3d;
  -webkit-transition: -webkit-transform 1s;
}

.containerperspective只会应用在它的直接后代,也就是#card元素。父元素可以通过transform-style: preserve-3d来使它一连串的后代元素继承到它的视角,并处在同一个3D空间中。没有3D属性transform-style的话,卡片将会是平坦的(而非3D化),其父级与自身背面的旋转将是无效的。

为了在3D空间定位卡片的面,需要先在2D空间使用position: absolute重置卡片的位置。要隐藏背对观察者的那一面,需要使用backface-visibility: hidden这句样式。

#card figure {
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  -webkit-backface-visibility: hidden;
}

要翻转.back那一面,我们加上基本的3D变换样式rotateY(180deg)

#card .front {
  background: red;
}
#card .back {
  background: blue;
  -webkit-transform: rotateY(180deg);
}

卡片在空间中是有两面的,#card在被翻转时需要一个相应的样式。

#card.flipped {
  -webkit-transform: rotateY(180deg);
}

现在我们有一个待命的3D物体了。要翻转卡片,我们切换flipped这个类名。但拥有.flipped类名是,#card将翻转180度,从而显露出.back那一面。

例子: Card 1.

3D card flip transition
在三维空间中翻转卡片

滑动翻转

再看起初我们提到的天气应用的3D过渡,你会注意到它跟我们上一个例子是不一样的效果。目光跟随着卡片的右边缘,你发现它的边角留在容器内,而不是在横向中心轴上作为右边缘的支点。而过渡并不只是一个水平移动由右至左边缘的旋转。只要修改两三行CSS,我们就能重现这个过渡效果。

这个旋转要的支点是在卡片的右侧。默认情况下一个元素的transform-origin值是它水平垂直的中心(50% 50%或者center center)。我们来把它改到右边:

#card { -webkit-transform-origin: right center; }

翻转需要translateX的水平运动。我们设置旋转到-180deg从而使它往右翻转。

#card.flipped {
  -webkit-transform: translateX(-100%) rotateY(-180deg);
}

例子: Card 2.

3D card slide-flip transition
创建一个右边缘的滑动翻转

立方体

创建3D卡片是我们开始学习3D变换很好的方式。一旦你掌握了,你会急不可耐的更进一步创建一些真实的3D物体:比如棱镜。让我们先从做一个立方体开始吧。

为立方体设置的标签跟卡片很像。这次我们需要六个子元素来代表立方体的六个面:

<section class="container">
  <div id="cube">
    <figure class="front">1</figure>
    <figure class="back">2</figure>
    <figure class="right">3</figure>
    <figure class="left">4</figure>
    <figure class="top">5</figure>
    <figure class="bottom">6</figure>
  </div>
</section>

给另一个容器中的六个面设置位置和尺寸样式。

.container {
  width: 200px;
  height: 200px;
  position: relative;
  -webkit-perspective: 1000;
}
#cube {
  width: 100%;
  height: 100%;
  position: absolute;
  -webkit-transform-style: preserve-3d;
}
#cube figure {
  width: 196px;
  height: 196px;
  display: block;
  position: absolute;
  border: 2px solid black;
}

对于卡片来说我们只要旋转它的背面就可以了,但立方体要求六个面中有五个面是旋转的。1和2面是前和后,3和4面是两侧,5和6面是顶部和底部。

#cube .front { -webkit-transform: rotateY(0deg); }
#cube .back { -webkit-transform: rotateX(180deg); }
#cube .right { -webkit-transform: rotateY(90deg); }
#cube .left { -webkit-transform: rotateY(-90deg); }
#cube .top { -webkit-transform: rotateX(90deg); }
#cube .bottom { -webkit-transform: rotateX(-90deg); }

第一维的#cube .front我们可以去掉这句样式声明,它对变换没有影响,但保留它使得我们的代码保持一致。

现在每一个面都在旋转了,只有前面是可见的。四个侧面都与观察者垂直,似乎它们是不可见的。要绕着它们位置的中心点平移才能使它们对应的面也展现出来,立方体的每一个边都是200px宽,所以也就是一半的尺寸,100px

#cube .front { -webkit-transform: rotateY(0deg) translateZ(100px); }
#cube .back { -webkit-transform: rotateX(180deg) translateZ(100px); }
#cube .right { -webkit-transform: rotateY(90deg) translateZ(100px); }
#cube .left { -webkit-transform: rotateY(-90deg) translateZ(100px); }
#cube .top { -webkit-transform: rotateX(90deg) translateZ(100px); }
#cube .bottom { -webkit-transform: rotateX(-90deg) translateZ(100px); }

注意到translateZ(Z轴平移)函数值紧跟在rotate后面。这个变换函数的指令是重要的。花点时间消化一下,每一面都是先在它的方向上翻转,再对着一个独立的向量做平移。

我们已经有一个看起来可用的立方体了,但我们还没有真正完成。

回到Z轴的原点

从用户角度考虑,3D变换中不动的那一面是不应当发生扭曲的。但一旦我们移动Z轴的原点,失真就会无可避免了。

为了使得3D变换保持正常,Safari先合并所有的元素,在应用变换。因此无论如何都要保持文本抗锯齿是应用在变换之前的。先应用变化的话,就可能会出现明显的杂边。

例子: Transforms 2.

Using 3D transforms can pixelate text

回头看Perspective 3 demo,注意到不管perspective 的值多么小,或者不管transform-origin的位置在哪里,1面总是会回到它最初的位置,就好像那些杂七杂八的3D变换属性完全是坑爹的嘛。

为了解决这个失真并完美重现一个#cube立方体,我们可以把这个3D物体向后推,使得它向前的那一面是基于Z轴原点的反方向上。

#cube { -webkit-transform: translateZ(-100px); }

例子: Cube 1.

CSS 3D cube object
恢复到Z轴上原来的位置

旋转立方体

为了显露出所有的面,我们需要加上旋转立方体的样式。transform的值是相应变换面的负值,在#box上切换必要的类名来应用适当的变换。

#cube.show-front { -webkit-transform: translateZ(-100px) rotateY(0deg); }
#cube.show-back { -webkit-transform: translateZ(-100px) rotateX(-180deg); }
#cube.show-right { -webkit-transform: translateZ(-100px) rotateY(-90deg); }
#cube.show-left { -webkit-transform: translateZ(-100px) rotateY(90deg); }
#cube.show-top { -webkit-transform: translateZ(-100px) rotateX(-90deg); }
#cube.show-bottom { -webkit-transform: translateZ(-100px) rotateX(90deg); }

注意到命令中变换函数是如何被反置的。首先我们把物体用translateZ向后推,然后我们旋转它。

整理一下,我们可以添加一个不同状态间的transition过渡动画。

#cube { -webkit-transition: -webkit-transform 1s; }

例子: Cube 2.

CSS 3D cube object changing sides
带有CSS transition过渡动画的立方体翻转

长方体

立方体是容易实现的,因为我只需要测量一次。面对一个不规则的长方体又该怎么处理呢?让我们创建一个300px宽,200px高,100px深的长方体试试。

标签同样与#cube立方体相似,但我们把id改为#box。容器的样式几乎保持相同:

.container {
  width: 300px;
  height: 200px;
  position: relative;
  -webkit-perspective: 1000;
}
#box {
  width: 100%;
  height: 100%;
  position: absolute;
  -webkit-transform-style: preserve-3d;
}

现在定位到每一面上,每一面都有它自己的尺寸。较小的那一面需要定位到容器的中心以便翻转和移出。较薄的左面和右面定位值为left: 100px=((300−100)/2),较大的顶面和底面定位值为top: 50px=((200−100)/2).

#box figure {
  display: block;
  position: absolute;
  border: 2px solid black;
}
#box .front,
#box .back {
  width: 296px;
  height: 196px;
}
#box .right,
#box .left {
  width: 96px;
  height: 196px;
  left: 100px;
}
#box .top,
#box .bottom {
  width: 296px;
  height: 96px;
  top: 50px;
}

旋转值可以全部跟立方体的一致,但对于长方体来说,平移值是不同的。由于#box是100px深的,前面和后面的平移值都是50px,同理左面和右面的平移值是150px,上面和下面的平移值是100px:

#box .front { -webkit-transform: rotateY(0deg) translateZ(50px); }
#box .back { -webkit-transform: rotateX(180deg) translateZ(50px); }
#box .right { -webkit-transform: rotateY(90deg) translateZ(150px); }
#box .left { -webkit-transform: rotateY(-90deg) translateZ(150px); }
#box .top { -webkit-transform: rotateX(90deg) translateZ(100px); }
#box .bottom { -webkit-transform: rotateX(-90deg) translateZ(100px); }

例子: Box 1.

3D CSS box object

像立方体一样,要显露出各个面,#box需要有一个样式反置某一面的变换。反置状态下,translateZrotate的值都设为当前面的对立面的值。

#box.show-front { -webkit-transform: translateZ(-50px) rotateY(0deg); }
#box.show-back { -webkit-transform: translateZ(-50px) rotateX(-180deg); }
#box.show-right { -webkit-transform: translateZ(-150px) rotateY(-90deg); }
#box.show-left { -webkit-transform: translateZ(-150px) rotateY(90deg); }
#box.show-top { -webkit-transform: translateZ(-100px) rotateX(-90deg); }
#box.show-bottom { -webkit-transform: translateZ(-100px) rotateX(90deg); }

例子: Box 2.

3D CSS box object rotating
带有CSStransition过渡效果的长方体翻转

立体圆盘

前端开发者们要创建一个圆盘来展示内容,有无数多种方式可以用。现在我们的浏览器支持3D了,为什么不试试创建一个实际的3D圆盘呢?

准备圆盘的标签同样类似于之前的例子。我们先假设圆盘有9个面。

<div class="container">
  <div id="carousel">
    <figure>1</figure>
    <figure>2</figure>
    <figure>3</figure>
    <figure>4</figure>
    <figure>5</figure>
    <figure>6</figure>
    <figure>7</figure>
    <figure>8</figure>
    <figure>9</figure>
  </div>
</div>

现在,应用上基本的layout样式。我们开始给#carousel的每一个面之间留10px的间隙,以及加上left: 10px;top: 10px;。这样每一面的布局宽度就是210px。

.container {
  width: 210px;
  height: 140px;
  position: relative;
  -webkit-perspective: 1000;
}
#carousel {
  width: 100%;
  height: 100%;
  position: absolute;
  -webkit-transform-style: preserve-3d;
}
#carousel figure {
  display: block;
  position: absolute;
  width: 186px;
  height: 116px;
  left: 10px;
  top: 10px;
  border: 2px solid black;
}

下一步,旋转每一面,这个#carousel有九个面。每一面都是相同的,所以每一面需要旋转的度数相差为40 degrees=(360/9)。

#carousel figure:nth-child(1) { -webkit-transform: rotateY(0deg); }
#carousel figure:nth-child(2) { -webkit-transform: rotateY(40deg); }
#carousel figure:nth-child(3) { -webkit-transform: rotateY(80deg); }
#carousel figure:nth-child(4) { -webkit-transform: rotateY(120deg); }
#carousel figure:nth-child(5) { -webkit-transform: rotateY(160deg); }
#carousel figure:nth-child(6) { -webkit-transform: rotateY(200deg); }
#carousel figure:nth-child(7) { -webkit-transform: rotateY(240deg); }
#carousel figure:nth-child(8) { -webkit-transform: rotateY(280deg); }
#carousel figure:nth-child(9) { -webkit-transform: rotateY(320deg); }

现在要向外平移了。回想我们创建立方体和长方体的时候translate的值都是非常好计算的,要么跟长宽高一致要么是一半。对于圆盘来说没有一个提到的值可以直接用的,因此不得不稍微计算一下。

Geometric diagram of carousel

绘制一个圆盘的过程中,可以看出我们只需要知道两个事情:每个面的宽度是210px,以及每一面占位40度。我们把其中一个分割成两半,就得到了一个严格的直角三角形。

列一个简单的方程式中可以解出r的值:

Trigonometric calculation

已经算出来,这一面需要在3D空间中平移288px的距离

#carousel figure:nth-child(1) { -webkit-transform: rotateY(0deg) translateZ(288px); }
#carousel figure:nth-child(2) { -webkit-transform: rotateY(40deg) translateZ(288px); }
#carousel figure:nth-child(3) { -webkit-transform: rotateY(80deg) translateZ(288px); }
#carousel figure:nth-child(4) { -webkit-transform: rotateY(120deg) translateZ(288px); }
#carousel figure:nth-child(5) { -webkit-transform: rotateY(160deg) translateZ(288px); }
#carousel figure:nth-child(6) { -webkit-transform: rotateY(200deg) translateZ(288px); }
#carousel figure:nth-child(7) { -webkit-transform: rotateY(240deg) translateZ(288px); }
#carousel figure:nth-child(8) { -webkit-transform: rotateY(280deg) translateZ(288px); }
#carousel figure:nth-child(9) { -webkit-transform: rotateY(320deg) translateZ(288px); }

如果你决定把每一面的宽度和圆盘的面数做成可变的,我们需要把这两个常量换成变量,代入同样的等式中去求出translateZ对应的值。在JavaScript语法中,应该是下面这样的:

var tz = Math.round( ( panelSize / 2 ) /
  Math.tan( ( ( Math.PI * 2 ) / numberOfPanels ) / 2 ) );
// or simplified to
var tz = Math.round( ( panelSize / 2 ) /
  Math.tan( Math.PI / numberOfPanels ) );

正如之前的3D物体,需要应用反置的变换来展示任意一面。当要展示第五面时样式就是:

-webkit-transform: translateZ(-288px) rotateY(-160deg);

例子: Carousel 1.

3-D CSS carousel

现在,你可能有两个想法:

  1. 很蛋疼的为每一面写出对应的样式属性值。
  2. 我才不这么2呢,为什么不交给机器去帮我做?

完全正确!3D对象的特性适合编写脚本实现。我们可以把所有简单变换样式的值通过脚本来计算,操作正确的话会比直接赋值更加灵活。

例子: Carousel 2.

结论

3D变换改变了我们在web设计这块空白画布上的思维方式,更妙的是它改变了画布本身,开辟了平面设计上的深度。

我希望你看完本文至少能亲自演示一次demo,并感到兴奋无比。我们web设计者,看到border-radius,box-shadow以及背景渐变的出现都忍不住欢欣鼓舞,现在有一个让我们实施3D变换的不可思议的工具,有什么理由不High起来呢亲们。3D变换应当像其他CSS3属性一样获得我们的热情投入,研究和实验。现在是一个最好的时机去思考如何让3D变换提升我们作品的工艺。啊啊啊我好High啊我不能呼吸了靠。。。。。。

期待你华丽的转身!