介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用。
                 爱校码
HTML5 集设计者和开发者于一身,其主要任务就是构建高效的丰富 Internet 应用程序之富网络应用 (Rich Internet Application,简称RIA),尤其是富 UI(User Interface,用户界面)。所说的高效是指进行系统的、全面的改进,以数字的方式为站点所有者、所有者代理和站点使用者之间的对话提供便利。 
RIA 是满足用户体验的本源和工具,因此也是任何以网络为中心的成功企业的关键组成部分。实质上,以网络为中心的活动在某种程度上是协作性的活动。当机构想在各个级别(包括市场营销和管理)上取得成功时,实现数字协作的成功方法至关重要。站点的成功主要取决于站点满足访问用户的质量要求的效率。
如您所见,HTML5 是专门为实现具有跨平台功能、融合电信、统一语言、广泛计算和开源系统的协作式 “网络世界” 而量身定制的。符合逻辑的 RIA 是丰富用户体验的创建和管理,对于实现站点目标是重要的。HTML5 API 在开发高效 RIA 的过程中扮演重要角色。
API是Application Programming Interface(应用程序编程接口)的缩写,它是编程指令集合和访问软件应用程序的标准。通过调用一个 API,可以利用 API 提供的服务设计出强大的产品。
HTML5 提供了一些 API:
HTML5 Canvas 是一个极其有用的绘制和动画元素。Canvas 使用 JavaScript 直接在页面上绘制图形。Canvas 能够定义和控制矩形区域,并允许以动态方式或通过脚本呈现 2D 形状和位图。
对于生成能够增强 UI、图表、相册、图形、动画和嵌入式绘制应用程序的出色视觉材料而言,HTML5 Canvas 非常完美。Canvas 元素可以通过几种方法来绘制路径、矩形、圆形和字符。
在画布上进行绘制的前提条件是熟悉网格和坐标空间。空间区域的长度和宽度的单位为像素。用户可以围绕 x 和 y 坐标构建画布。当坐标为 x=0, y=0 时,画布原点位于左上角。 如图1所示。
图 1. x、y坐标

矩形画布区域的默认宽度为 300 像素,高度为 150 像素,但您可以通过修改这两个属性来获得所需的画布大小。图2 显示了如何实现 x 和 y 坐标。
图 2. 画布坐标

图 2 显示了长宽都为 100 像素的画布区域:
在向画布添加内容之前,必须先在 HTML 文件中定义画布,必须创建 JavaScript 代码,用它来访问 标记并与 HTML5 Canvas API 通信,这样就可以绘制图形。
标记的基本结构为:canvas 标记有两个专有的属性:width 和 height。此外,Canvas 还拥有所有关键的 HTML5 属性,比如 class、id 和 name。在上面显示的代码中就用到了 id 属性。JavaScript 代码使用这里创建的画布 id 来识别要绘制内容的画布。
JavaScript代码使用 document.getElementById()方法来确定正确的画布:
var canvas = document.getElementById("myCanvas");
每个画布都必须有一个上下文对象定义。到目前为止,官方的规范仅识别 2D 环境(3D绘图通过“webgl”参数创建上下文对象,仅对支持WebGL的浏览器有效):
var context = canvas.getContext("2d");
在识别画布并指定其上下文之后,就可以在其上绘制内容了。
讨论描述HTML5 Canvas 将用到的各种绘制工具、效果和变形。
这些绘制工具包括:
将使用的画布效果包括:
要讨论的变形包括:
绘制线条
要在画布上绘制线条,使用到的方法:
beginPath() 方法将启用一个新路径。在使用不同的子路径绘制新的线条之前,必须使用 beginPath()来表明绘制的新起点。首次绘制线条时没有必要调用 beginPath()方法。
moveTo()方法会声明新的子路径的开始点。lineTo()方法用于创建子路径。可以通过lineWidth和 strokeStyle改变线条的外观。lineWidth元素用于改变线条的粗细,而 strokeStyle 则用于改变线条的颜色。 
方法moveTo()和lineTo()实际上并不画线,而是在结束canvas操作的时候,通过调用stroke()方法完成线条的绘制。
对上下文的很多操作都不会立即反映页面上。beginPath()、moveTo()、lineTo()这些函数都不会直接修改canvas的展示结果。canvas中很多用于设置样式和外观的函数也同样不会直接修改显示结果。只有当对上下文对象应用绘制方法stroke() 或填充方法fill()时,结果才会显示出来。
在图 3 中,分别绘制了蓝色、绿色和紫色的线条。
图 3. 画布线条

图 3 中的线条是通过 '清单 1' 中的代码创建的。
清单 1. 在画布上创建 3 个颜色不同的线条
<!DOCTYPE HTML>
<html>
    <head>
        <title>线条示例</title>
        <style>
            body {
                margin: 0px;
                padding: 0px;
            }
            #myCanvas {
                border: 1px solid #9C9898;
            }
        </style>
        <script>
            window.onload = function() {
                var canvas = document.getElementById("myCanvas");
                var context = canvas.getContext("2d");
                // 圆形端的蓝色线
                context.beginPath();
                context.moveTo(50, 50);
                context.lineTo(300,50);
                context.lineWidth = 10;
                context.strokeStyle = "#0000FF"; 
                context.lineCap = "round";
                context.stroke();
                // 方形端的绿色线
                context.beginPath();
                context.moveTo(50, 100);
                context.lineTo(300,100);
                context.lineWidth = 20;
                context.strokeStyle = "#00FF00"; 
                context.lineCap = "square";
                context.stroke();
                // 平头端的紫色线
                context.beginPath();
                context.moveTo(50, 150);
                context.lineTo(300, 150);
                context.lineWidth = 30;
                context.strokeStyle = "#FF00FF"; 
                context.lineCap = "butt";
                context.stroke();
            };
        </script>
    </head>
    <body>
        <canvas id="myCanvas" width="400" height="200">
        </canvas>
    </body>
</html>
蓝色的线条两端是圆角的,创建它的时候,首先要确定一个新路径即将开始:context.beginPath()。其创建过程如下所示:
上面的 3 个线条的长度都是 50 像素,但它们看起来不一样长,这是由线条两端套盖 (cap) 引起的视觉混淆。有 3 种可用的末端套盖:
butt 套盖是默认值。使用 round 或 square 套盖样式时,线条的长度将增加,增加的量为线条自身的宽度。例如,如果长度为 200 像素、宽度为 10 像素的线条采用 round 或 square 套盖样式,那么将导致线条的长度变为 210 像素,因为将在线条的两端各增加一个长度为 5 像素的套盖。如果长度为 200 像素、宽度为 20 像素的线条采用 round 或 square 套盖样式,那么将导致线条的长度变为 220 像素,因为将在线条的两端各增加一个长度为 10 像素的套盖。
绘制矩形
在画布上绘制矩形区域有 3 种方法:
对于这 3 种方法而言,x 和 y 表示矩形在画布上相对于左上角的位置(x=0, y=0),而 width 和 height 分别是矩形的宽度和高度。
图 4 显示了由 清单 2 中的代码创建的 3 个矩形区域。
图 4. 矩形画布

fillRect() 方法创建了一个矩形并使用默认的黑色填充它。clearRect()方法在第一个矩形的中心擦除一块矩形区域,该区域位于 fillRect() 方法所创建的矩形的中心。strokeRect()方法创建一个仅黑色边框可见的矩形。 
清单 2. 矩形画布代码
<!DOCTYPE HTML>
<html>
<head>
    <title>Rectangle Example</title>
    <style>
        body {
            margin: 0px;
            padding: 0px;
        }
        #myCanvas {
            border: 1px solid #000000;
            background-color: #ffff00;
        }
    </style>
    <script type="text/javascript">
         function drawShape(){
             var canvas = document.getElementById('myCanvas');
             var context = canvas.getContext('2d');
             context.fillRect(25,25,50,50);
             context.clearRect(35,35,30,30);
             context.strokeRect(100,100,50,50);
         }
     </script>
</head>
<body onload="drawShape();">
     <canvas id="myCanvas" width="200" height="200"></canvas>
</body>
</html>
绘制弧形、曲线、圆形和半圆形
绘制圆形和半圆形都使用 arc() 方法。arc() 方法接收 6 个参数:
context.arc(centerX, centerY, radius, startingAngle, endingAngle, antiClockwise);
centerX 和 centerY 参数是圆心的坐标。radius 的含义和其数学上半径的含义一样:表示从圆心到圆周的直线距离。所创建的弧形将作为圆形的一部分。startAngle 和 endAngle 参数分别为弧形的起点和终端,单位为弧度。anticlockwise 参数是一个布尔值。当其值为 true 时,弧形就逆时针绘制;当为 false 时,弧形就顺时针绘制。
要使用 arc() 方法绘制圆形,将起始角度定义为 0,结束角度为 2*PI,如下所示:
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
要使用 arc() 方法绘制半圆形,将结束角度定义为 startingAngle + PI,如下所示:
context.arc(centerX, centerY, radius, startingAngle, startingAngle + Math.PI, false);
二次方曲线
使用 quadraticCurveTo() 方法创建二次方曲线 ,如下所示。二次方曲线由上下文点、控制点和结束点定义。控制点确定线条的曲度。
context.moveTo(x, y);
context.quadraticCurveTo(controlX, controlY, endX, endY);
贝塞尔曲线
与二次方曲线一样,贝塞尔(Bezier)曲线也有一个起始点和一个结束点;但与二次方曲线不同的是,它有两个控制点:
context.moveTo(x, y);
context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
使用 bezierCurveTo() 方法来创建贝塞尔曲线。因为贝塞尔曲线通过两个而不是一个控制点来定义,所以可以创建更加复杂的曲线。
图 5 从左到右分别显示了弧形、二次方曲线、贝塞尔曲线、半圆形和圆形。
图 5. 弧形、曲线和圆形

清单 3. 弧形、曲线和圆形的代码
<!DOCTYPE HTML>
<html>
<head>
    <title>圆弧,曲线,圆和半圆</title>
    <style>
        body {
            margin: 0px;
            padding: 0px;
        }
        #myCanvas {
            border: 1px solid #9C9898;
        }
    </style>
    <script>
        function drawArc(){
           var canvas = document.getElementById("myCanvas");
           var context = canvas.getContext("2d");
           var centerX = 100;
           var centerY = 160;
           var radius = 75;
           var startingAngle = 1.1 * Math.PI;
           var endingAngle = 1.9 * Math.PI;
           var counterclockwise = false;
           context.arc(centerX, centerY, radius, startingAngle, 
           endingAngle, counterclockwise);
           context.lineWidth = 10;
           context.strokeStyle = "black"; 
           context.stroke();
        };
        function drawQuadratic(){
            var canvas = document.getElementById("myCanvas");
            var context = canvas.getContext("2d");
            context.moveTo(200, 150);
            var controlX = 288;
            var controlY = 0;
            var endX = 388;
            var endY = 150;
            context.quadraticCurveTo(controlX, controlY, endX, endY);
            context.lineWidth = 10;
            context.strokeStyle = "black"; 
            context.stroke();
        };
        function drawBezier(){
            var canvas = document.getElementById("myCanvas");
            var context = canvas.getContext("2d");
            context.moveTo(350, 350);
            var controlX1 = 440;
            var controlY1 = 10;
            var controlX2 = 550;
            var controlY2 = 10;
            var endX = 500;
            var endY = 150;
            context.bezierCurveTo(controlX1, controlY1, controlX2, 
            controlY2, endX, endY);
            context.lineWidth = 10;
            context.strokeStyle = "black"; 
            context.stroke();
       };
       function drawCircle(){
            var canvas = document.getElementById("myCanvas");
            var context = canvas.getContext("2d");
            var centerX = 450;
            var centerY = 375;
            var radius = 70;
            context.beginPath();
            context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
            context.fillStyle = "#800000";
            context.fill();
            context.lineWidth = 5;
            context.strokeStyle = "black";
            context.stroke();
       };
       function drawSemicircle(){
             var canvas = document.getElementById("myCanvas");
             var context = canvas.getContext("2d");
             var centerX = 100;
             var centerY = 375;
             var radius = 70;
             var lineWidth = 5;
             context.beginPath();
             context.arc(centerX, centerY, radius, 0, Math.PI, false);
             context.closePath();
             context.lineWidth = lineWidth;
             context.fillStyle = "#900000";
             context.fill();
             context.strokeStyle = "black";
             context.stroke();
        };
        window.onload = function (){
              drawArc();
              drawQuadratic(); 
              drawBezier(); 
              drawCircle(); 
              drawSemicircle()
         }
     </script>
</head>
<body>
    <canvas id="myCanvas" width="600" height="500">
    </canvas>
</body>
</html>
变形、缩放和旋转
变形、缩放和旋转
translate()、scale() 和 rotate() 方法都用于修改当前的图形。translate(x, y)方法将画布上的元素移动到网格上的不同点。在 translate(x,y) 方法中,(x,y)坐标表明图像在 x 轴和 y 轴方向上应该移动的像素数。 
如果使用 drawImage()方法在 (15,25) 位置上绘制图形,那么可以使用参数为 (20,30) 的 translate() 方法,将图形放在 (15+20, 25+30) = (35, 55) 的位置上。 
scale(x,y)方法可改变图形的大小。x 参数指定水平缩放因素,而 y 参数指定垂直缩放因素。例如,scale(1.5, .75) 创建的图形比当前图形在 x 轴方向上大 50%,在 y 轴上大 75%。rotate(angle)方法可根据指定的角度来选择对象。 
图 6 展示了可以使用 translate()、scale() 和 rotate() 方法呈现的内容。
图 6. 使用变形
    
清单 4. 创建变形的代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>转换示例</title>
    <script>
        window.onload = function() {
            var canvas = document.getElementById("myCanvas");
            var context = canvas.getContext("2d");
            var rectWidth = 250;
            var rectHeight = 75;
            // 将上下文对象移到画布中心
            context.translate(canvas.width / 2, canvas.height / 2);
            // y分量的一半
            context.scale(1, 0.5);
            // 顺时针旋转45度
            context.rotate(-Math.PI / 4);
            context.fillStyle = "blue";
            context.fillRect(-rectWidth / 2, -rectHeight / 2, rectWidth, rectHeight);
            // 水平翻转上下文
            context.scale(-1, 1);
            context.font = "30pt Calibri";
            context.textAlign = "center";
            context.fillStyle = "#ffffff";
            context.fillText("镜像转换", 3, 10);
        }
    </script>
</head>
<body>
     <canvas id="myCanvas" width="400" height="400"></canvas>
</body>
</html>
渐变就是从一种颜色过渡到另一种颜色的填充过程,两种颜色相交时会进行混合。可在画布中创建的渐变有两种:线性和辐射性的。
可以使用createLinearGradient()方法创建线性渐变。createLinearGradient(x0,y0,x1,y1) 沿着由两个点识别到的直线生成渐变:(x0,y0)和 (x1,y1)分别是渐变的起点和终点。该方法返回一个对象。 
彩色渐变可以使用多种颜色。addcolorStop(offset, color)方法根据给定的偏移量指定颜色停止点。addColorStop()方法允许指定介于 0 和 1 之间的偏移量,在该偏移量后将开始渐变到另一种颜色。值 0是渐变的一端的偏移量;1是渐变的另一端的偏移量。在定义了颜色渐变之后,就可以将渐变对象分配给 fillStyle()。还可以通过 fillText() 方法使用渐变绘制文本。 
辐射渐变可以使用 createradialGradient(x0,y0,r0,x1,y1,r1)来实现,用六个参数将两种或多种颜色以圆形或锥形的图案融合在一起: 
图 7 包含 4 个渐变:一个线性渐变、一个文本渐变、一个对角线性渐变和一个辐射渐变。
图 7. 渐变示例
   
清单 5. 渐变示例代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>渐变示例</title>
    <script>
        window.onload = function() {
            var canvas = document.getElementById("myCanvas");
            var context = canvas.getContext("2d");
            //让我们尝试矩形上的渐变
            // 创建线性渐变
            var fillColor = context.createLinearGradient(50, 50, 150, 50);
            // 设置渐变颜色
            fillColor.addColorStop(0.15, "red");
            fillColor.addColorStop(0.35, "black");
            fillColor.addColorStop(0.65, "green");
            fillColor.addColorStop(0.87, "yellow");
            // 将渐变对象分配给fillstyle
            context.fillStyle = fillColor;
            // 画矩形
            context.fillRect(50, 50, 100, 100);
            // 带文字
            var fillColorText = context.createLinearGradient(300, 50, 600, 50);
            fillColorText.addColorStop(0.2, "red");
            fillColorText.addColorStop(0.4, "black");
            fillColorText.addColorStop(0.6, "green");
            fillColorText.addColorStop(0.8, "yellow");
            context.fillStyle = fillColorText;
            context.font = "40px verdana";
            context.textBaseline = "top";
            context.fillText("With text too!", 300, 50)
            // 对角线上的渐变
            var fillColordiagonal = context.createLinearGradient(50, 200, 100, 450);
            // 渐变色
            fillColordiagonal.addColorStop(0.2, "red");
            fillColordiagonal.addColorStop(0.4, "black");
            fillColordiagonal.addColorStop(0.6, "green");
            fillColordiagonal.addColorStop(0.75, "yellow");
            // 将渐变对象分配给fillstyle
            context.fillStyle = fillColordiagonal;
            // 画矩形
            context.fillRect(50, 225, 100, 250);
            // 绘制径向渐变
            fillColorRadial = context.createRadialGradient(450, 300, 0, 450, 300, 200);
            fillColorRadial.addColorStop(0, "red");
            fillColorRadial.addColorStop(0.2, "black");
            fillColorRadial.addColorStop(0.4, "green");
            fillColorRadial.addColorStop(0.7, "yellow");
            context.fillStyle = fillColorRadial;
            context.rect(300, 200, 500, 400);
            context.fill();
        }
    </script>
</head>
<body>
    <div>
        <p><canvas id="myCanvas" width="600" height="400"></canvas></p>
    </div>
</body>
</html>
可以通过裁剪选中的区域来改变图像。要在画布上进行裁剪,则需要重载 drawImage()方法。drawImage() 方法有 3 个选项。您可以使用 3 个、5 个或 9 个参数。 
3 个参数配置,即 drawImage(image, dx, dy),可将图像绘制在画布的目标坐标 (dx, dy) 上。该坐标构成图像的左上角。 
5 个参数配置,即drawImage(image, dx, dy, dw, dh),可为目标坐标提供宽度和高度。将缩放图像以适合目标宽度和高度。 
9 个参数配置,即 drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh),可从一个图像中剪切一个矩形区域,该区域的原坐标为 (sx,sy),宽度和高度为 (sw,sh),然后缩放该区域使之适合于目标宽度和高度 (dw,dh),并将其放置到画布的 (dx,dy) 位置上。 
图 8 显示了要剪切的图像。
图 8. 剪切图像
   
以图 8 中的图像为背景将一组图像放到画布上。选用与画布大小相同的图像作为背景。另一个要创建的图像则应更小一些,将插入画布的右下角。第三个图像是拿破仑头部的剪切,放置于画布的左上角。图 9 显示了裁剪之后的图像。
图 9. 裁剪之后的图像
   
清单 6. 裁剪实例图像的代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>图像裁剪示例</title>
    <script type="text/javascript">
        window.onload = function() {
            var canvas = document.getElementById("myImage");
            var context = canvas.getContext("2d");
            var imageObj = new Image();
            imageObj.onload = function() {
                // 绘制图像以覆盖整个画布
                context.drawImage(imageObj, 0, 0, 600, 400);
                // 在右下角绘制小图像
                var sourceX = 0;
                var sourceY = 0;
                var sourceWidth = 1200;
                var sourceHeight = 801;
                var destX = 300;
                var destY = 200;
                var destWidth = sourceWidth - 900;
                var destHeight = sourceHeight - 600;
                context.drawImage(imageObj, sourceX, sourceY, sourceWidth,
                    sourceHeight, destX, destY, destWidth, destHeight);
                //只画拿破仑的头
                var sourceNapoleanX = 460;
                var sourceNapoleanY = 25;
                var sourceNapoleanWidth = 250;
                var sourceNapoleanHeight = 175;
                var destNapoleanX = 0;
                var destNapoleanY = 0;
                var destNapoleanWidth = sourceNapoleanWidth - 150;
                var destNapoleanHeight = sourceNapoleanHeight - 100;
                context.drawImage(imageObj, sourceNapoleanX, sourceNapoleanY,
                    sourceNapoleanWidth, sourceNapoleanHeight,
                    destNapoleanX, destNapoleanY,
                    destNapoleanWidth, destNapoleanHeight);
            }
            imageObj.src = "img/canvas_image0.png";
        }
    </script>
</head>
<body>
    <div>
        <p><canvas id="myImage" width="600" height="400"></canvas></p>
    </div>
</body>
</html>
在处理动画时,层是经常遇到问题的地方。层允许隔离组件,使编写和调试代码变得更容易、更高效。Canvas API 没有层,但它可以创建多个画布。
必须通过时间来控制动画。因此,要创建动画必须实现动画的每个帧。Canvas API 在动画方面有一个主要的局限性:一旦在画布上创建了图形,将无法再改变它。要移动该图形,就必须重新绘制它。
创建动画的流程:
可以通过两种方式来控制动画:使用 setInterval 或 setTimeout 函数,两个函数均可在固定的时间段内调用函数。setInterval 函数重复执行所提供的代码。setTimeout 函数仅在所提供的时间段到达时调用一次。
图 10 显示了游泳者的多画布动画中的一帧。水在一个画布上,而游泳者在另一个画布上。
图 10. 使用图像的动画 
   
使用清单 7 中的代码来创建游泳者 (swimmer)。游泳者使用线性渐变来创建水 (water)。水有 4 种蓝色阴影,这就产生了类似水的视觉。然后可以使用 positionX 和 positionY 值来创建游泳者的动画,这将改变图像的姿势。使用 arc() 方法来创建游泳者的头部。通过绘制直线创建游泳者的胳膊和腿,然后在改变它们的 lineTo() 位置。再通过改变 moveTo() 的位置来改变躯干。由于这是动画,所以必须运行代码才能看到游泳者的游泳动作。
清单 7. 动画示例
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>动画和多画布示例</title>
    <script>
        // 水画布
        function drawWater() {
            var canvasWater = document.getElementById("myWaterCanvas");
            var contextWater = canvasWater.getContext("2d");
            contextWater.globalAlpha = .50;
            // 创建线性渐变填充
            var linearGrad = contextWater.createLinearGradient(0, 0, 400, 400);
            linearGrad.addColorStop(0, '#0000ff'); // sets the first color
            linearGrad.addColorStop(.25, '#0099ff'); // sets the second color
            linearGrad.addColorStop(.50, '#00ccff'); // sets the third color
            linearGrad.addColorStop(.75, '#00ffff'); // sets the fourth color
            contextWater.fillStyle = linearGrad;
            contextWater.fillRect(0, 0, 400, 400);
        }
        // 游泳者画布
        setInterval(drawSwimmer, 30);
        var positionX = 0;
        var positionY = 0;
        function drawSwimmer() {
            var canvasSwimmer = document.getElementById("mySwimmerCanvas");
            var contextSwimmer = canvasSwimmer.getContext("2d");
            contextSwimmer.clearRect(0, 0, 400, 400);
            if(positionX < 30) {
                positionX += 1;
                positionY += 1;
            } else {
                positionX = 0;
                positionY = 0;
            }
            contextSwimmer.save();
            // 为头画圆
            var centerX = 200;
            var centerY = 50;
            var radius = 20;
            contextSwimmer.beginPath();
            contextSwimmer.arc(centerX, centerY + positionY,
                radius, 0, 2 * Math.PI, false);
            contextSwimmer.fillStyle = "#000000";
            contextSwimmer.fill();
            contextSwimmer.lineWidth = 5;
            // 躯干
            contextSwimmer.beginPath();
            contextSwimmer.moveTo(200, 70 + positionY);
            contextSwimmer.lineTo(200, 175);
            contextSwimmer.lineWidth = 10;
            contextSwimmer.strokeStyle = "#000000";
            contextSwimmer.lineCap = "round";
            contextSwimmer.stroke();
            // 图像右臂
            contextSwimmer.beginPath();
            contextSwimmer.moveTo(200, 100);
            contextSwimmer.lineTo(175 - positionX, 140 - positionY);
            contextSwimmer.lineWidth = 10;
            contextSwimmer.strokeStyle = "#000000";
            contextSwimmer.lineCap = "round";
            contextSwimmer.stroke();
            // 图像左臂
            contextSwimmer.beginPath();
            contextSwimmer.moveTo(200, 100);
            contextSwimmer.lineTo(225 + positionX, 140 - positionY);
            contextSwimmer.lineWidth = 10;
            contextSwimmer.strokeStyle = "#000000";
            contextSwimmer.lineCap = "round";
            contextSwimmer.stroke();
            // 图像右腿
            contextSwimmer.beginPath();
            contextSwimmer.moveTo(200, 175);
            contextSwimmer.lineTo(190 - positionX, 250 - positionY);
            contextSwimmer.lineWidth = 10;
            contextSwimmer.strokeStyle = "#000000";
            contextSwimmer.lineCap = "round";
            contextSwimmer.stroke();
            // 图像左腿
            contextSwimmer.beginPath();
            contextSwimmer.moveTo(200, 175);
            contextSwimmer.lineTo(210 + positionX, 250 - positionY);
            contextSwimmer.lineWidth = 10;
            contextSwimmer.strokeStyle = "#000000";
            contextSwimmer.lineCap = "round";
            contextSwimmer.stroke();
            contextSwimmer.restore();
        };
    </script>
</head>
<body onload="drawWater();">
    <canvas id="myWaterCanvas" width="400" height="400" style="z-index: 2; 
                  position:absolute;left:0px;top:0px;">
            </canvas>
    <canvas id="mySwimmerCanvas" width="400" height="400" style="z-index: 1; 
                  position:absolute;left:0px;top:0px;">
            </canvas>
</body>
</html>
林间小路的美景如图11所示:
图 11. 林间小路 
   
清单 8. 林间小路示例
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>林间小路</title>
    <style>
        body {
            margin: 0px;
            padding: 0px;
        }
        #myCanvas {
            border: 1px solid #9C9898;
        }
    </style>
    <script>
        // 加载砾石背景图
        var gravel = new Image();
        gravel.src = "img/gravel.jpg";
         // 加载图像后,在画布上绘制
        gravel.onload = function() {
            drawTrails();
        }
        function createCanopyPath(context) {
            // 绘制树冠
            context.beginPath();
            context.moveTo(-25, -50);
            context.lineTo(-10, -80);
            context.lineTo(-20, -80);
            context.lineTo(-5, -110);
            context.lineTo(-15, -110);
            // 树顶
            context.lineTo(0, -140);
            context.lineTo(15, -110);
            context.lineTo(5, -110);
            context.lineTo(20, -80);
            context.lineTo(10, -80);
            context.lineTo(25, -50);
            // 关闭路径回到起点
            context.closePath();
        }
        //创建树对象绘制函数,将树图绘制移到自己的函数中以供重用
        function drawTree(context) {
            // 保存当前画布状态以供后续使用
            context.save();
            // 借助拉伸变换创建一棵用作阴影的倾斜的树,
            //  改变X值为随Y值增加而增加,
            // 应用了变换以后,所有坐标都与矩阵相乘
            // context.transform(a,b,c,d,e,f) 
            // a:水平缩放,b:水平倾斜,c:垂直倾斜,
            // d:垂直缩放,e:水平移动,f:垂直移动
            context.transform(1, 0, -0.5, 1, 0, 0);
            // 将阴影缩小到Y尺寸的60%高度
            context.scale(1, 0.6);
            // 使用透明度为20%的黑色填充树干
            context.fillStyle = 'rgba(0, 0, 0, 0.2)';
            context.fillRect(-5, -50, 10, 50);
            // 重绘具有阴影效果的树
            createCanopyPath(context);
            context.fill();
            // 恢复画布状态
            context.restore();
            //在整个树干上水平创建一个3级渐变
            var trunkGradient = context.createLinearGradient(-5, -50, 5, -50);
            // 树干的开头是中棕色
            trunkGradient.addColorStop(0, '#663300');
            // 树干的左中颜色较浅
            trunkGradient.addColorStop(0.4, '#996600');
            // 树干的右边颜色最暗
            trunkGradient.addColorStop(1, '#552200');
            // 将渐变作为填充样式,并绘制树干
            context.fillStyle = trunkGradient;
            // 为树干填充一个矩形
            context.fillRect(-5, -50, 10, 50);
            // 第二个垂直渐变从树干上的树冠创建阴影
            var canopyShadow = context.createLinearGradient(0, -50, 0, 0);
            // 阴影渐变的开头是黑色,但透明度为50%
            canopyShadow.addColorStop(0, 'rgba(0, 0, 0, 0.5)');
            // 稍微向下一点,渐变完全消失为完全透明。 其余的树干没有阴影。
            canopyShadow.addColorStop(0.2, 'rgba(0, 0, 0, 0.0)');
            // 在渐变树干之上绘制渐变阴影
            context.fillStyle = canopyShadow;
            context.fillRect(-5, -50, 10, 50);
            createCanopyPath(context);
            context.lineWidth = 4;
            context.lineJoin = 'round';
            //以棕色笔划绘制路径
            context.strokeStyle = '#663300';
            context.stroke();
            context.fillStyle = '#339900';
            context.fill();
        }
        function drawTrails() {
            // 获取canvas元素及其绘图上下文
            var canvas = document.getElementById('trails');
            var context = canvas.getContext('2d');
            // 在X = 130,Y = 250处绘制第一棵树
            // 保存当前画布状态以供后续使用
            context.save();
            //将绘图上下文向右和向下移动
            context.translate(130, 250);
            //调用树对象绘制函数
            drawTree(context);
            // 恢复画布状态
            context.restore();
            // 在X = 260,Y = 500处绘制第二棵树
            context.save();
            context.translate(260, 500);
            // 将此树在两个维度上均缩放两次
            context.scale(2, 2);
            drawTree(context);
            context.restore();
            //保存画布状态并绘制路径
            context.save();
            context.translate(-10, 350);
            context.beginPath();
            //第一条曲线向右上方弯曲
            context.moveTo(0, 0);
            context.quadraticCurveTo(170, -50, 260, -190);
            //第二条曲线继续向右下方弯曲
            context.quadraticCurveTo(310, -250, 410, -250);
            //用背景图替代棕色粗线条,用重复的背景图案替换实线
            context.strokeStyle = context.createPattern(gravel, 'repeat');
            context.lineWidth = 20;
            context.stroke();
            // 恢复先前的画布状态
            context.restore();
            // 在画布上绘制标题文字
            context.save();
            // 设置字体
            context.font = "60px impact";
            // 用棕色填充文字
            context.fillStyle = '#996600';
            // 显示时可以对齐文本
            context.textAlign = 'center';
            // 在文本上设置黑色阴影,透明度20%
            context.shadowColor = 'rgba(0, 0, 0, 0.2)';
            // 将阴影向右移动15个像素,向上移动10个像素
            context.shadowOffsetX = 15;
            context.shadowOffsetY = -10;
            // 轻微模糊阴影
            context.shadowBlur = 2;
            // 在画布中间绘制文本
            context.fillText('快乐足迹!', 200, 60, 400);
            context.restore();
        }
    </script>
</head>
<body>
    <canvas id="trails" style="border: 1px solid;" width="400" height="600"> </canvas>
    </canvas>
</body>
</html>
是否厌倦了将您的客户端数据塞入那么小的cookie当中?cookie也称为 Web cookie或浏览器cookie,是服务器在用户浏览器中存储的一小段文本信息。服务器在返回浏览器发出的请求的响应时设置cookie。浏览器存储cookie并将其与下一个请求一起发送回同一服务器。Cookie通常用于会话管理,用户跟踪和存储用户首选项。它是一个在服务器和客户端浏览器之间来回传递文本值的内置机制。cookie的主要缺陷有:
现如今,Web应用程序的需求更大。 如果说每个用户的浏览器上可以获得5 MB的数据,该怎么办? 嗯,不需要持怀疑态度,HTML5 Web Storage API可以做到这一点! 在本文的该标题中,将带您了解所需要的信息,将任何对象在本地存储在用户设备上,并在其中使用您的Web体验。
什么是Webstorage?HTML5在浏览器中提供了一个漂亮的简单的JavaScript API,用于持久存储键/值对。 您也不再局限于小巧的4k字节的存储空间;今天所有的浏览器都会很乐于为您提供5至10 MB的存储空间。 HTML5的本地存储同样也是通过Web App( 包含移动App)创建的。需要
注意!本地存储意味着您的应用可以将数据存储在浏览器中,减少与服务器之间的通信。 来看看它是如何工作的:
开发者可以将数据存储在JavaScript对象中,对象在页面加载时保存,并且容易获取。通过使用sessionStorage或localStorage,在打开新窗口或新标签页,或者重启浏览器时,开发者可以选择是否激活这些数据。存储的数据不会在网络传输,重新浏览页面时也容易获取到。
sessionStorage或localStorage在编程上的唯一的区别是访问它们的名称不同,二者在行为上的差异主要体现在数据的保存时长,以及它们的共享方式:
设置数据很简单,只需执行以下语句:
sessionStorage.setItem('myKey','myValue');
或
localStorage.setItem('key','value');
为了便于记忆,存储对象应该是window对象的属性对象,即window.sessionStorage或 window.localStorage,在此省略了window对象的引用,storage对象可以从默认的页面上下文中获得;setItem()方法需要一个字符串类型的“键”,和一个字符串类型的“值”作为其参数;执行结果将字符串值设置到sessionStorage或 localStorage中,随后可通过键名获取。
获取数据同样简单,只需执行以下语句:
var myvalue = sessionStorage.getItem('myKey');
或
 var value = localStorage.getItem('key');
不过,访问storage对象还有更简单的方法,可以使用“属性”设置和获取存储对象中的值,根据键值的配对关系,直接在sessionStorage或localStorage对象上设置和获取数据。使用这种方法,设置数据的代码可以改写为:
sessionStorage.myKey = 'myValue';
sessionStorage['myKey'] = 'myValue';
或
localStorage.key = 'value';
localStorage['key'] = 'value';
同样,获取数据的代码可以改写为:
var myvalue = sessionStorage.myKey;
var myvalue1 = sessionStorage['myKey'];
或
var value = localStorage.key;
var value1 = localStorage['key'];
数据能够保存多久?对于设置到sessionStorage中的对象,只要浏览器窗口(或标签页)不关闭,它们就会一直存在。当用户关闭浏览器窗口(或标签页),sessionStorage数据就被清除。使用sessionStorage能够跨页面暂存,不会将其泄漏到用户仍在浏览其他信息的窗口中。这样,不同的偏好信息就会被隔离在各自的窗口中。    
将数据存储在本地客户端,进而从本地而不是远程获取数据,既可以降低网络流量,又可以提升浏览器的响应能力。当用户浏览某个页面时,如果能够快速获取数据并加以显示,对于增强用户体验来说无疑是非常好的方式。
接下来将完成一个便利贴系统(通常称为即时贴)。它是如何工作的:在便利贴上记下“要做”的事情,然后将它贴在某个地方,一旦完成任务后,便会将便利贴放入垃圾桶(或回收)。
首先,使用HBuilder开发工具创建一个名为stickies的项目,接下来,在项目中创建一个名为mynotes.html的html5的文件,该文件包含了页面的基本结构(一个head和一个body):
清单 9. html5初始页面结构
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
</body>
</html>
① 写一个便利贴的内容:“提醒我去拿干洗的衣服”,让我们从存储这个便利贴开始:
Web Storage API通过localStorage对象是可行的方案,您会发现它已经通过浏览器成为本地的底层存储系统。它只能存储字符串类型,不能直接存储数字或对象。setItem方法将两个字符串作为参数,用作键/值对:
 localStorage.setItem("sticky_0", "提醒我去拿干洗的衣服");
为了存储,使用setItem方法。第一个字符串参数是存储项的键,只要它是字符串,就可以为它命名sticky_0;第二个字符串是要存储在本地存储系统中的值。
②那很容易,让我们向本地存储中添加第二项目内容:
localStorage.setItem("sticky_1", "取消有线电视,现在谁需要它?");
sticky_1是另一个键名。 就像我们已经说过的那样,您可以使用任何喜欢的键,只要它是字符串即可,但是每个键只能存储一个值。第二项参数是对应新建的值。
③ 现在我们已经将两个值安全地存储在浏览器的本地存储中,现在您可以使用其中之一键从localStorage检索其相应的值。 像这样:
var sticky = localStorage.getItem("sticky_0");
alert(sticky);
从本地存储中获取与键“sticky_0”关联的值,并将其分配给名为sticky的变量。为了使这一点更有趣,让我们使用告警功能在屏幕上弹出便笺的值。
④ 认真研究便利贴系统,进行需求分析:
我们将创建一个便笺应用程序,以便您可以查看便笺并添加新便笺。即时贴App将向我们显示便笺笔记,并在localStorage中添加新便笺笔记。我们需要一种添加新便笺的方法,因此,我们将创建一个带有输入框和按钮的表单。
如果存储中有现有的便利贴,则希望在加载页面时看到它们,就像我们已经拥有的两个便笺一样。要显示即时贴,我们将遍历localStorage中的所有即时贴,并将其添加到页面的DOM对象中展示。
我们将使用CSS设置即时贴的样式,使其看起来像真正的即时贴!
请记住,已有的两个即时贴的键是“ sticky_0”和“ sticky_1”。我们将继续遵守这个惯例,并使用递增的整数创建用于即时贴的键名,例如sticky_2,sticky_3等。
我们还将看到显示中有任何新的即时贴的更新,并且我们将为每个即时贴向DOM对象中添加新元素来实现这一点。当您单击“添加便笺”的按钮时,新的便笺将添加到localStorage。
⑤ 创建页面:
在这里,我们需要一种输入便笺文本的方法。如果我们可以在页面中看到它们,那就太好了,所以我们需要一个界面元素来保持页面中的所有笔录。清单 10 中的代码,从HTML标记-使用现有的HTML文件并添加一个<form>元素、<ul>元素和指向它的CSS链接<link>、以及JavaScript脚本<script>。
清单 10. 输入显示便笺页面
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>我的便笺</title>
    <link rel="stylesheet" href="mynotes.css">
    <script src="mynotes.js"></script>
</head>
<body>
    <form>
        <input type="text" id="note_text">
        <input type="button" id="add_button" value="添加便笺">
    </form>
    <ul id="stickies">
    </ul>
</body>
</html>
我们通过<link>标记添加了“mynotes.css”的CSS样式文件,使外观看起来更像真实的便利贴;并将所有JavaScript代码由<script>标记移至“mynotes.js”文件;添加了一个<form>表单作为用户交互接口,用于输入新的即时贴。而且我们必须在某个地方将便笺展示在页面上,因此我们将其放置在<ul></ul>无序列表中,CSS处理使每个列表项看起来更像一个便利贴。
⑥ 现在添加JavaScript代码:
现在,已经设计好了页面,存储在localStorage中便笺等待显示。 先从localStorage中读取它们,然后将其放入刚刚在页面上创建的无序列表元素。 操作方法如下:
window.onload = init;                                 // 加载页面后,将调用init函数;
// 初始化函数从localStorage读取所有现有的便笺,并通过DOM对象将它们添加到<ul>中。
function init() {
    for(var i = 0; i < localStorage.length; i++) {    // 遍历存储中的所有项;
        var key = localStorage.key(i);                // 抓住每一个键;
        if(key.substring(0, 6) == "sticky") {         // 判断便利贴的键名是否以“sticky”开头;
            var value = localStorage.getItem(key);
            addStickyToDOM(value);                    // 若是便利贴,则抓取其值并将它添加到页面中(通过DOM)。
        }
    }
}
现在需要编写addStickyToDOM函数,该函数将把便笺插入<ul>元素中:
// 通过传递便笺文本作为参数。 需要得到无序列表对象,创建列表项,然后将便笺插入。
function addStickyToDOM(value) {
    var stickies = document.getElementById("stickies"); //获取id为“stickies”的列表元素。
    var sticky = document.createElement("li");          // 创建一个列表li元素。
    var span = document.createElement("span");          // 创建一个span元素。
    span.setAttribute("class", "sticky");               // 并给span一个class名为“sticky”,可以设置样式。
    span.innerHTML = value;                             // 设置span元素的内容,其为便笺文本。
    sticky.appendChild(span);                           // 并将span添加到li.
    stickies.appendChild(sticky);                       // 并将li添加到ul列表。
}
⑦ 是时候测试一下:
将以上的脚本代码放入您的mynotes.js文件,并将其设置在页面<script>标记的src属性的值,在浏览器中加载页面后得到的结果如图12所示:
图12 便笺测试
   
⑧ 完成用户界面:
现在,需要做的就是启用表单,以便可以添加新的便笺。为此,需要为单击“添加便笺”按钮时添加处理程序,并编写一些代码以创建新的便笺。 以下为添加处理程序的代码:
// 将新代码添加到init函数中
function init() {
    var button = document.getElementById("add_button");  // 获取“添加便笺”按钮的对象引用
    button.onclick = createSticky;   // 并添加一个单击时的处理程序。 触发调用createSticky函数。
    // for循环在这里。init的其余代码保持不变,在此不再重复。
}
以及创建新便签的createSticky函数代码:
// 单击该按钮时,将调用此处理程序。
function createSticky() {
    var value = document.getElementById("note_text").value;  // 它首先在表单文本框中检索文本。
    // 需要为‘便笺’创建唯一的键。使用“ sticky_”与整个存储对象的长度相结合,它会继续增加。
    var key = "sticky_" + localStorage.length;  
    localStorage.setItem(key, value);  //然后使用给定的键名,向‘localStorage’添加新的便利贴。
    addStickyToDOM(value); // 最后,将新文本添加到DOM中以表示便利贴。
}
⑨ 再次测试一下:
现在是真正的交互操作! 在浏览器中加载此新代码,输入新的“给自己的便笺”,然后单击或点击“添加便笺”按钮。 您应该看到新的即时贴出现在您的即时贴列表中。 如图13所示:
图13 新增便笺
   
这是我们的测试运行! 看起来不错!新增便笺的键名应该是“ sticky_2”,即存储对象 localStorage的长度(在添加之前)与“ sticky_”串联。
确认一下,尝试关闭浏览器窗口,然后再次打开页面文件, 是否仍然看到已有的“便笺”吗?
⑩ 自我维护:
创建一个名为“maintenance.html”的新页面文件:
清单 11. 便笺维护
<!doctype html>
<html>
<head>
    <title>便笺维护</title>
    <meta charset="utf-8">
    <script>
        // 我们在页面上添加了一个按钮,此代码为该按钮添加了一个点击处理程序。
        window.onload = function() {
            var clearButton = document.getElementById("clear_button");
            // 当您单击按钮时,将调用clearStorage函数。
            clearButton.onclick = clearStorage;
        }
        function clearStorage() {
            // 该函数所做的全部就是调用localStorage.clear方法。 请谨慎使用,因为它将删除与此维护页面来源相关的所有项目!
            localStorage.clear();
        }
    </script>
</head>
<body>
    <form>
<!-- 这是我们的按钮。 每当您需要擦除localStorage中的所有内容时使用此文件(适用于测试)。 -->
        <input type="button" id="clear_button" value="清除存储">
    </form>
</body>
</html>
输入页面标记及代码后,继续并将其加载到浏览器中。(就我们的便利贴应用而言)进行localStorage清理是安全的,请尝试一下! 请使用您的浏览器的菜单项-->开发者工具,以便观察更改变化。
清单 12. 页面优化
<!doctype html>
<html>
<head>
    <title>我的便笺</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="css/mynotes.css">
    <script src="js/mynotes2.js"></script>
</head>
<body>
    <form>
        <label for="note_color">颜色: </label>
        <select id="note_color">
            <option value="LightGoldenRodYellow">浅金黄色</option>
            <!-- #FAFAD2 -->
            <option value="PaleGreen">浅绿色</option>
            <!-- #98FB98 -->
            <option value="LightPink">粉红色</option>
            <!-- #FFB6C1 -->
            <option value="LightBlue">浅蓝色</option>
            <!-- #ADD8E6 -->
        </select>
        <label for="note_text">文本:</label> <input type="text" id="note_text">
        <input type="button" id="add_button" value="添加便笺">
    </form>
    <ul id="stickies">
    </ul>
</body>
</html>
清单 13. 程序优化
window.onload = init;
function init() {
    var button = document.getElementById("add_button");
    button.onclick = createSticky;
    var stickiesArray = getStickiesArray();
    for(var i = 0; i < stickiesArray.length; i++) {
        var key = stickiesArray[i];
        var value = JSON.parse(localStorage[key]);
        addStickyToDOM(key, value);
    }
}
function getStickiesArray() {
    var stickiesArray = localStorage.getItem("stickiesArray");
    if(!stickiesArray) {
        stickiesArray = [];
        localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));
    } else {
        stickiesArray = JSON.parse(stickiesArray);                                                                                                                                        
    }
    return stickiesArray;
}
function createSticky() {
    var stickiesArray = getStickiesArray();
    var value = document.getElementById("note_text").value;
    var colorSelectObj = document.getElementById("note_color");
    var index = colorSelectObj.selectedIndex;
    var color = colorSelectObj[index].value;
    // 使用JSON创建便签以保留值和颜色
    var currentDate = new Date();
    var key = "sticky_" + currentDate.getTime();
    var stickyObj = {
        "value": value,
        "color": color
    };
    localStorage.setItem(key, JSON.stringify(stickyObj));
    // 向数组添加新的便利贴键名并更新localStorage中的stickiesArray数组
    stickiesArray.push(key);
    localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));
    addStickyToDOM(key, stickyObj);
}
function deleteSticky(e) {
    var key = e.target.id;
    if(e.target.tagName.toLowerCase() == "span") {
        key = e.target.parentNode.id;
    }
    var stickiesArray = getStickiesArray();
    if(stickiesArray) {
        for(var i = 0; i < stickiesArray.length; i++) {
            if(key == stickiesArray[i]) {
                stickiesArray.splice(i, 1);
            }
        }
        localStorage.removeItem(key);
        localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));
        removeStickyFromDOM(key);
    }
}
function addStickyToDOM(key, stickyObj) {
    var stickies = document.getElementById("stickies");
    var sticky = document.createElement("li");
    // 将键设置为id属性,以便我们更易查找
    // ids存储在stickies数组中
    sticky.setAttribute("id", key);
    // 使用stickyObj颜色,并设置背景颜色CSS样式
    sticky.style.backgroundColor = stickyObj.color;
    var span = document.createElement("span");
    span.setAttribute("class", "sticky");
    // 使用stickyObj值作为便笺的文本
    span.innerHTML = stickyObj.value;
    // 将所有内容添加到DOM
    sticky.appendChild(span);
    stickies.appendChild(sticky);
    // 添加事件侦听器,以便在单击便笺时可以将其删除
    sticky.onclick = deleteSticky;
}
function removeStickyFromDOM(key) {
    var sticky = document.getElementById(key);
    sticky.parentNode.removeChild(sticky);
}
function clearStickyNotes() {
    localStorage.clear();
    var stickyList = document.getElementById("stickies");
    var stickies = stickyList.childNodes;
    for(var i = stickies.length - 1; i >= 0; i--) {
      stickyList.removeChild(stickies[i]);
    }
    // 重置便笺数组
    var stickiesArray = getStickiesArray();
}
清单 14. CSS样式
body {
    background-color: #dbdbdb;
    font-size: 100%;
}
form input#note_text {
    width: 350px;
}
/* 便条 */
ul#stickies li {
    display: block;
    list-style: none;
    z-index: 1;
    float: left;
    margin: 30px;
    padding: 15px 15px 50px 15px;
    width: 200px;
    height: 200px;
    border: 1px solid #bfbfbf;
    background-color: LightGoldenRodYellow;
    /* use #fafad2 if name doesn't work */
    color: black;
    text-decoration: none;
    -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
    -moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
    -o-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
    box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
    -webkit-transition: all 0.5s ease-in;
    -moz-transition: all 0.5s ease-in;
    -o-transition: all 0.5s ease-in;
    -ms-transition: all 0.5s ease-in;
    transition: all 0.5s ease-in;
    overflow: hidden;
}
ul#stickies li span.sticky {
    font-family: Verdana, Helvetica, sans-serif;
    font-size: 200%;
}
/* 
 * 旋转
 */
ul#stickies li:nth-child(even) {
    -webkit-transform: rotate(2deg);
    -moz-transform: rotate(2deg);
    -o-transform: rotate(2deg);
    -ms-transform: rotate(2deg);
    transform: rotate(2deg);
}
ul#stickies li:nth-child(odd) {
    -webkit-transform: rotate(-1deg);
    -moz-transform: rotate(-1deg);
    -o-transform: rotate(-1deg);
    -ms-transform: rotate(-1deg);
    transform: rotate(-1deg);
}
ul#stickies li:nth-child(3n) {
    -webkit-transform: rotate(1deg);
    -moz-transform: rotate(1deg);
    -o-transform: rotate(1deg);
    -ms-transform: rotate(1deg);
    transform: rotate(1deg);
}
/* 
转换演示
 使用过渡(上面定义)来缓解。
*/
ul#stickies li:hover {
    -webkit-box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.4);
    -moz-box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.4);
    -o-box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.4);
    -webkit-transform: rotate(0deg) scale(1.25);
    -moz-transform: rotate(0deg) scale(1.25);
    -o-transform: rotate(0deg) scale(1.25);
    -ms-transform: rotate(0deg) scale(1.25);
    transform: rotate(0deg) scale(1.25);
    z-index: 10;
}
           
       
   博文最后更新时间: