通常情况下,GPUs处理精细的3D建模或者CAD图表,但这种情况下,我们想要原始的制图(divs, 背景,下落式阴影的文字,图像等等...) 能通过GPU平滑地展现出来并且有流畅的动画。不幸的是,大多数前端开发者没有考虑动画处理的机制并将其装载在第三方框架,但是这些核心的CSS3特性应该被掩盖吗?让我来给你们一些关于为什么关心这件事是十分重要的理由:
1. 内存分配和计算压力- 如果你将所有元素都合成在DOM里,仅仅是为了硬件加速,在你的代码基础上继续工作的另一个人可能会想狠狠揍你一顿。
2. 电源消耗- 显然地,当硬件开始工作,电源也随之开始消耗。当进行移动端开发时,开发者开发移动应用必须要考虑设备多样化的约束。普遍流行的情况是浏览器开发商开始使其产品能适应多样的设备硬件。
3. 冲突- 我曾经历过小故障:将硬件加速应用到一部分能够加速的页面。值得确信的是如果你有重复的加速区域是非常重要的。
为了尽可能地使户交互平滑并且接近真实,我们必须使浏览器为我们工作。理想的情况是,我们想要移动设备的CPU建立初始化动画,然后使GPU仅仅负责动画处理过程中合成不同的层。这就是translate3d, scale3d, translateZ做的事- 他们给了动画元素到他们各自的层,因此允许设备能平滑渲染。如果想要了解更多加速合成,WebKit工作原理,Ariya Hidayat 在他的博客里提供了许多信息。
让我们看看开发移动WEB应用时最常用的三种用户交互方法:滑动、翻转、旋转效果。
你可以在这个链接查看代码的实际效果: http://slidfast.appspot.com/slide-flip-rotate.html (注意: 这个演示是为移动设备建立的,所以请启动模拟器,或者使用手机、平板电脑,或把你的浏览器窗口减小到约1024px或更小).
首先,我们将剖析滑动、翻转、旋转过渡,及如何使其加速。请注意每个动画是如何只需三、四行CSS和JavaScript即可实现的。
在这三种常用效果中最常用的是滑动,滑动页面变换模拟了移动应用的自然感觉。滑动转换用来向视图区域带来一个新的内容。
要实现滑动效果,首先我们要声明元素标签:
<div id="home-page" class="page"> <h1>Home Page</h1> </div> <div id="products-page" class="page stage-right"> <h1>Products Page</h1> </div> <div id="about-page" class="page stage-left"> <h1>About Page</h1> </div>
注意我们是如何让页面向左或向右演出的。本质上,它可以是任何方向,但水平是最常见的。
我们现在只需几行CSS就可以产生有硬件加速的动画。我们交换页面上的div元素的class时,动画就会实际发生。
.page { position: absolute; width: 100%; height: 100%; /*activate the GPU for compositing each page */ -webkit-transform: translate3d(0, 0, 0); }
translate3d(0,0,0)作为“银弹”方法而闻名。
当用户点击一个导航元素,我们执行下面的JavaScript来交换class。没有第三方框架被使用,这是纯JavaScript!
function getElement(id) { return document.getElementById(id); } function slideTo(id) { //1.) the page we are bringing into focus dictates how // the current page will exit. So let's see what classes // our incoming page is using. We know it will have stage[right|left|etc...] var classes = getElement(id).className.split(' '); //2.) decide if the incoming page is assigned to right or left // (-1 if no match) var stageType = classes.indexOf('stage-left'); //3.) on initial page load focusPage is null, so we need // to set the default page which we're currently seeing. if (FOCUS_PAGE == null) { // use home page FOCUS_PAGE = getElement('home-page'); } //4.) decide how this focused page should exit. if (stageType > 0) { FOCUS_PAGE.className = 'page transition stage-right'; } else { FOCUS_PAGE.className = 'page transition stage-left'; } //5. refresh/set the global variable FOCUS_PAGE = getElement(id); //6. Bring in the new page. FOCUS_PAGE.className = 'page transition stage-center'; }
stage-left或stage-right成为stage-center,会推动页面滑入视图中心。我们完全依靠CSS3完成繁重的工作。
.stage-left { left: -480px; } .stage-right { left: 480px; } .stage-center { top: 0; left: 0; }
接下来,让我们看看处理移动设备检测与适应的CSS。我们可以定位每种设备和每种分辨率(参考 媒体查询解析)。我在演示中使用的只是几个简单的例子来覆盖移动设备上大多数的竖立和横放视图。这对应用每种设备本身的硬件加速功能也很有用。比如,因为Webkit的桌面版本加速了所有转换元素(不管是二维还是三维),所以在这个水平上建立媒体查询和排除加速很有意义。注意,在Android Froyo 2.2+以下,硬件加速技巧不会提供任何速度的改进。所有合成都是在软件内部实现的。
/* iOS/android phone landscape screen width*/ @media screen and (max-device-width: 480px) and (orientation:landscape) { .stage-left { left: -480px; } .stage-right { left: 480px; } .page { width: 480px; } }
在移动设备上,翻转实际上以把页面击飞(译者注:如果你熟悉棒球,很容易想像)而闻名。在这里我们用一些简单的 JavaScript 在iOS 和 Android (基于WebKit)设备上来处理这个事件。
在这个地址可查看实际执行效果http://slidfast.appspot.com/slide-flip-rotate.html.
当处理触摸事件和转换效果时,你要做的第一件事就是获得元素当前位置的句柄。在WebKitCSSMatrix上可以看到更多信息。
function pageMove(event) { // get position after transform var curTransform = new WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform); var pagePosition = curTransform.m41; }
由于我们为页面翻转使用的是CSS3的ease-out转换,usualelement.offsetleft不会工作。
下一步我们要找出用户翻转的是哪个方向,并对事件(页面导航)设定一个发生的阈值。
if (pagePosition >= 0) { //moving current page to the right //so means we're flipping backwards if ((pagePosition > pageFlipThreshold) || (swipeTime < swipeThreshold)) { //user wants to go backward slideDirection = 'right'; } else { slideDirection = null; } } else { //current page is sliding to the left if ((swipeTime < swipeThreshold) || (pagePosition < pageFlipThreshold)) { //user wants to go forward slideDirection = 'left'; } else { slideDirection = null; } }
你会注意到我们测量击打时间是毫秒级的。这允许导航事件在用户快速点击屏幕来翻页时也会发生。
为了定位页面和当手指正触摸屏幕时使动画看起来自然,我们在每次事件触发后都使用CSS3转换。
function positionPage(end) { page.style.webkitTransform = 'translate3d('+ currentPos + 'px, 0, 0)'; if (end) { page.style.WebkitTransition = 'all .4s ease-out'; //page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)' } else { page.style.WebkitTransition = 'all .2s ease-out'; } page.style.WebkitUserSelect = 'none'; }
我想玩弄一下三次曲线来让转换带有最好的自然感觉,但ease-out已经玩了这个花样。
最后,为让导航发生,我们必须调用我们之前在上一个演示里定义的slideTo()方法。
track.ontouchend = function(event) { pageMove(event); if (slideDirection == 'left') { slideTo('products-page'); } else if (slideDirection == 'right') { slideTo('home-page'); } }
接下来,让我们来看看在本演示使用的旋转动画。在任何时候,你可以旋转页面将看到180度旋转后反面的“联系人”菜单选项。 同样的,只需要几行CSS和一些JavaScript指定一个点击时的transition class。注:旋转过渡则无法正确的在大多数版本的Android上呈现,因为它缺乏3D CSS transform 的支持。不幸的是,Android提供了“侧手翻”页面旋转特性,来替代翻转。我们建议在android得到支持之前使用transition来进行翻转。
正面与背面的基本结构:
<div id="front" class="normal"> ... </div> <div id="back" class="flipped"> <div id="contact-page" class="page"> <h1>Contact Page</h1> </div> </div>
JavaScript:
function flip(id) { // get a handle on the flippable region var front = getElement('front'); var back = getElement('back'); // again, just a simple way to see what the state is var classes = front.className.split(' '); var flipped = classes.indexOf('flipped'); if (flipped >= 0) { // already flipped, so return to original front.className = 'normal'; back.className = 'flipped'; FLIPPED = false; } else { // do the flip front.className = 'flipped'; back.className = 'normal'; FLIPPED = true; } }
CSS:
/*----------------------------flip transition */ #back, #front { position: absolute; width: 100%; height: 100%; -webkit-backface-visibility: hidden; -webkit-transition-duration: .5s; -webkit-transform-style: preserve-3d; } .normal { -webkit-transform: rotateY(0deg); } .flipped { -webkit-user-select: element; -webkit-transform: rotateY(180deg); }
现在我们讲完基本变换的方法了,让我们看看它们是如何工作和合成的。
为了使这个奇妙的调试会话得以发生,让我们启动你喜欢的一个IDE和浏览器。我使用Mac,因此操作可能和你的操作系统的命令与方式都不同。首先我在命令行设置一些调试中使用的环境变量,然后启动Safari浏览器。打开Terminal,键入以下内容:
这样就能开启Safari的两个调试助手功能。CA_COLOR_OPAQUE 会向我们展现哪个元素被实际合成和加速了。 CA_LOG_MEMORY_USAGE 会向我们展现当向backing store发送我们的绘制操作时使用了多少内存。这可以确切告诉你你给移动设备施加了多少压力,以及可能提示你你对GPU的使用会消耗目标设备多少电量。
现在让我们启动Chrome,这样我们可以很好地看到每秒多少帧(FPS)的信息:
注意:不要在所有页选项中激活 GPU 合成。当浏览器检测到你标签中的合成项目,会只在左边角落显示FPS计数器,而这不是我们在本案例中想要的。
如果你在威力增强版的Chrome中查看本讲座效果页面,你会在左上方看到红色的 FPS 计数器。
这就是我们怎样知道硬件加速功能被开启的方法。这也给了我们一个关于动画如何运行的和你是否有任何疏漏的想法(继续运行本应停止的动画)。
另一种让硬件加速变得实际可视化的方法是,如果你通过先设置我上面提到的环境变量来用Safari打开相同的页面。每个被加速的DOM元素都会有一个红色色调。这告诉了我们到底层合成了哪些元素。注意,白色的导航因为不能加速而没有变红。
另一个看到合成层的好方式,是开启这个选项之后查看WebKit的落叶演示。
最后,要真正了解我们的应用程序的图形硬件性能,让我们来看看内存是如何被消耗的。这里我们可以看到,我们正在把绘图指令产生的1.38MB数据推进到Mac OS上的CoreAnimation缓冲区。核心动画缓冲区是被OpenGL ES和GPU共享的,来创建你最终在屏幕上看到的像素。
当我们简单地调整一下浏览器窗口尺寸或把窗口最大化,我们会看到立即膨胀了。
这给你一个想法,内存是如何被消耗在移动设备上,只有当你调整浏览器的正确尺寸。如果你在调试或测试iPhone环境,请从320像素调整到480像素。我们现在明白了硬件加速究竟如何工作的,以及怎样来调试。这是一种用阅读数字来了解的方式,但也是真正看到GPU内存缓冲区可视化工作的方式,确实让事情变得透明了。
评论删除后,数据将无法恢复
评论(4)