欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

程序员文章站 2022-06-12 10:55:52
...

HTML5 集设计者和开发者于一身,其主要任务就是构建高效的丰富 Internet 应用程序之富网络应用 (Rich Internet Application,简称RIA),尤其是富 UI(User Interface,用户界面)。所说的高效是指进行系统的、全面的改进,以数字的方式为站点所有者、所有者代理和站点使用者之间的对话提供便利。

RIA 是满足用户体验的本源和工具,因此也是任何以网络为中心的成功企业的关键组成部分。实质上,以网络为中心的活动在某种程度上是协作性的活动。当机构想在各个级别(包括市场营销和管理)上取得成功时,实现数字协作的成功方法至关重要。站点的成功主要取决于站点满足访问用户的质量要求的效率。

如您所见,HTML5 是专门为实现具有跨平台功能、融合电信、统一语言、广泛计算和开源系统的协作式 “网络世界” 而量身定制的。符合逻辑的 RIA 是丰富用户体验的创建和管理,对于实现站点目标是重要的。HTML5 API 在开发高效 RIA 的过程中扮演重要角色。

一、什么是 API?

API是Application Programming Interface(应用程序编程接口)的缩写,它是编程指令集合和访问软件应用程序的标准。通过调用一个 API,可以利用 API 提供的服务设计出强大的产品。

HTML5 提供了一些 API:

  • 用于渲染图形或其他可视图像的新 canvas 元素所使用的 2D 绘图 API
  • 支持离线 Web 应用程序的缓存 API
  • 使用新 video 和 audio 元素来播放视频和音频的 API
  • 支持浏览历史访问和添加页面的历史 API
  • draggable 属性支持的拖放 API
  • contenteditable 属性支持的编辑 API
  • 使用 JavaScript API 进行键-值对存储的客户端存储及嵌入式 SQL 数据库

二、什么是 Canvas?

HTML5 Canvas 是一个极其有用的绘制和动画元素。Canvas 使用 JavaScript 直接在页面上绘制图形。Canvas 能够定义和控制矩形区域,并允许以动态方式或通过脚本呈现 2D 形状和位图。

对于生成能够增强 UI、图表、相册、图形、动画和嵌入式绘制应用程序的出色视觉材料而言,HTML5 Canvas 非常完美。Canvas 元素可以通过几种方法来绘制路径、矩形、圆形和字符。

1. Canvas 坐标

在画布上进行绘制的前提条件是熟悉网格和坐标空间。空间区域的长度和宽度的单位为像素。用户可以围绕 x 和 y 坐标构建画布。当坐标为 x=0, y=0 时,画布原点位于左上角。 如图1所示。

图 1. x、y坐标 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

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

图 2. 画布坐标 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

图 2 显示了长宽都为 100 像素的画布区域:

  • 左上角的坐标为 x=0, y=0。
  • x 值在水平方向增加,而 y 值在垂直方向增加。
  • 右下角的坐标为 x=100, y=100。
  • 中心点的坐标为 x=50, y=50。

2. 起步

在向画布添加内容之前,必须先在 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");

在识别画布并指定其上下文之后,就可以在其上绘制内容了。

3. 绘制工具、效果和变形

讨论描述HTML5 Canvas 将用到的各种绘制工具、效果和变形。

这些绘制工具包括:

  • 线条
  • 矩形
  • 弧形
  • 贝塞尔曲线和二次方曲线
  • 圆形和半圆形

将使用的画布效果包括:

  • 填充和笔触
  • 线性渐变和辐射渐变

要讨论的变形包括:

  • 缩放
  • 旋转
  • 转换

绘制线条

要在画布上绘制线条,使用到的方法:

  • context.beginPath();
  • context.moveTo(x,y);
  • context.lineTo(x,y);
  • context.stroke(x,y);

beginPath() 方法将启用一个新路径。在使用不同的子路径绘制新的线条之前,必须使用 beginPath()来表明绘制的新起点。首次绘制线条时没有必要调用 beginPath()方法。

moveTo()方法会声明新的子路径的开始点。lineTo()方法用于创建子路径。可以通过lineWidth和 strokeStyle改变线条的外观。lineWidth元素用于改变线条的粗细,而 strokeStyle 则用于改变线条的颜色。

方法moveTo()和lineTo()实际上并不画线,而是在结束canvas操作的时候,通过调用stroke()方法完成线条的绘制。

对上下文的很多操作都不会立即反映页面上。beginPath()moveTo()lineTo()这些函数都不会直接修改canvas的展示结果。canvas中很多用于设置样式和外观的函数也同样不会直接修改显示结果。只有当对上下文对象应用绘制方法stroke() 或填充方法fill()时,结果才会显示出来。

在图 3 中,分别绘制了蓝色、绿色和紫色的线条。

图 3. 画布线条 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

图 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()。其创建过程如下所示:

  • context.moveTo(50, 50),将路径的开始点置于 (x=50, y=50)
  • context.lineTo(300,50),识别线条的结束点
  • context.lineWidth = 10,线条的宽度
  • context.strokeStyle = "#0000FF",线条的颜色
  • context.lineCap = "round",使线条的两端变圆
  • context.stroke(),真实地将线条绘制在画布上

上面的 3 个线条的长度都是 50 像素,但它们看起来不一样长,这是由线条两端套盖 (cap) 引起的视觉混淆。有 3 种可用的末端套盖:

  • context.round(圆形)
  • context.square(方形)
  • context.butt(平头形,默认)

butt 套盖是默认值。使用 round 或 square 套盖样式时,线条的长度将增加,增加的量为线条自身的宽度。例如,如果长度为 200 像素、宽度为 10 像素的线条采用 round 或 square 套盖样式,那么将导致线条的长度变为 210 像素,因为将在线条的两端各增加一个长度为 5 像素的套盖。如果长度为 200 像素、宽度为 20 像素的线条采用 round 或 square 套盖样式,那么将导致线条的长度变为 220 像素,因为将在线条的两端各增加一个长度为 10 像素的套盖。

绘制矩形

在画布上绘制矩形区域有 3 种方法:

  • fillRect(x,y,width,height),绘制已填充的矩形
  • strokeRect(x,y,width,height),绘制矩形框线
  • clearRect(x,y,width,height),擦除矩形的给定区域使之完全透明

对于这 3 种方法而言,x 和 y 表示矩形在画布上相对于左上角的位置(x=0, y=0),而 width 和 height 分别是矩形的宽度和高度。

图 4 显示了由 清单 2 中的代码创建的 3 个矩形区域。

图 4. 矩形画布 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

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. 弧形、曲线和圆形 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

清单 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. 使用变形 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

清单 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>

4. 渐变

渐变就是从一种颜色过渡到另一种颜色的填充过程,两种颜色相交时会进行混合。可在画布中创建的渐变有两种:线性和辐射性的。

可以使用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)来实现,用六个参数将两种或多种颜色以圆形或锥形的图案融合在一起:

  • (x0,y0) 圆锥的第一个圆形的中心
  • r0 第一个圆形的半径
  • (x1,y1) 圆锥的第二个圆形的中心
  • r1 第二个圆形的半径

图 7 包含 4 个渐变:一个线性渐变、一个文本渐变、一个对角线性渐变和一个辐射渐变。

图 7. 渐变示例 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

清单 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>

5.图像裁剪

可以通过裁剪选中的区域来改变图像。要在画布上进行裁剪,则需要重载 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. 剪切图像 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

以图 8 中的图像为背景将一组图像放到画布上。选用与画布大小相同的图像作为背景。另一个要创建的图像则应更小一些,将插入画布的右下角。第三个图像是拿破仑头部的剪切,放置于画布的左上角。图 9 显示了裁剪之后的图像。

图 9. 裁剪之后的图像 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

清单 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>

6.动画和多个画布

在处理动画时,层是经常遇到问题的地方。层允许隔离组件,使编写和调试代码变得更容易、更高效。Canvas API 没有层,但它可以创建多个画布。

必须通过时间来控制动画。因此,要创建动画必须实现动画的每个帧。Canvas API 在动画方面有一个主要的局限性:一旦在画布上创建了图形,将无法再改变它。要移动该图形,就必须重新绘制它。

创建动画的流程:

  1. 清除先前在画布上绘制的所有形状。
  2. 保存画布状态,确保每次绘制帧是使用的都是最初状态。
  3. 执行呈现帧的步骤。
  4. 如果已保存了状态,请在绘制新的帧之前恢复它。

可以通过两种方式来控制动画:使用 setInterval 或 setTimeout 函数,两个函数均可在固定的时间段内调用函数。setInterval 函数重复执行所提供的代码。setTimeout 函数仅在所提供的时间段到达时调用一次。

图 10 显示了游泳者的多画布动画中的一帧。水在一个画布上,而游泳者在另一个画布上。

图 10. 使用图像的动画 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

使用清单 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>

7.Canvas综合案例:林间小路

林间小路的美景如图11所示:

图 11. 林间小路 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

清单 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>

三、什么是 Webstorage?

是否厌倦了将您的客户端数据塞入那么小的cookie当中?cookie也称为 Web cookie或浏览器cookie,是服务器在用户浏览器中存储的一小段文本信息。服务器在返回浏览器发出的请求的响应时设置cookie。浏览器存储cookie并将其与下一个请求一起发送回同一服务器。Cookie通常用于会话管理,用户跟踪和存储用户首选项。它是一个在服务器和客户端浏览器之间来回传递文本值的内置机制。cookie的主要缺陷有:

  • cookie的大小受限:一个cookie只能设置约4kb的数据。
  • cookie有安全风险并消耗带宽:只要有请求涉及cookie,它就要在后端服务器和前端浏览器之间来回传送,而且,cookie数据在网络上是可见的。

现如今,Web应用程序的需求更大。 如果说每个用户的浏览器上可以获得5 MB的数据,该怎么办? 嗯,不需要持怀疑态度,HTML5 Web Storage API可以做到这一点! 在本文的该标题中,将带您了解所需要的信息,将任何对象在本地存储在用户设备上,并在其中使用您的Web体验。

什么是Webstorage?HTML5在浏览器中提供了一个漂亮的简单的JavaScript API,用于持久存储键/值对。 您也不再局限于小巧的4k字节的存储空间;今天所有的浏览器都会很乐于为您提供5至10 MB的存储空间。 HTML5的本地存储同样也是通过Web App( 包含移动App)创建的。需要 注意!本地存储意味着您的应用可以将数据存储在浏览器中,减少与服务器之间的通信。 来看看它是如何工作的:

开发者可以将数据存储在JavaScript对象中,对象在页面加载时保存,并且容易获取。通过使用sessionStoragelocalStorage,在打开新窗口或新标签页,或者重启浏览器时,开发者可以选择是否**这些数据。存储的数据不会在网络传输,重新浏览页面时也容易获取到。

  • 在浏览器的本地存储中,一个页面可以存储一个或多个键/值对。
  • 使用键检索它的相应的值。

sessionStoragelocalStorage在编程上的唯一的区别是访问它们的名称不同,二者在行为上的差异主要体现在数据的保存时长,以及它们的共享方式:

  • sessionStorage:数据会保存到存储它的窗口或标签页关闭时(浏览器刷新时可以存储数据,浏览器关闭时不可以),数据只在构建它们的窗口或标签页内可见。
  • localStorage:数据的生命期比窗口或浏览器(打开与关闭)的生命期要长。数据可被同源的每个窗口或者标签页共享。

1.设置和获取数据

设置数据很简单,只需执行以下语句:

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对象还有更简单的方法,可以使用“属性”设置和获取存储对象中的值,根据键值的配对关系,直接在sessionStoragelocalStorage对象上设置和获取数据。使用这种方法,设置数据的代码可以改写为:

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'];

2.封堵数据泄露

数据能够保存多久?对于设置到sessionStorage中的对象,只要浏览器窗口(或标签页)不关闭,它们就会一直存在。当用户关闭浏览器窗口(或标签页),sessionStorage数据就被清除。使用sessionStorage能够跨页面暂存,不会将其泄漏到用户仍在浏览其他信息的窗口中。这样,不同的偏好信息就会被隔离在各自的窗口中。

3.构建Web storage应用

将数据存储在本地客户端,进而从本地而不是远程获取数据,既可以降低网络流量,又可以提升浏览器的响应能力。当用户浏览某个页面时,如果能够快速获取数据并加以显示,对于增强用户体验来说无疑是非常好的方式。

接下来将完成一个便利贴系统(通常称为即时贴)。它是如何工作的:在便利贴上记下“要做”的事情,然后将它贴在某个地方,一旦完成任务后,便会将便利贴放入垃圾桶(或回收)。

首先,使用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 便笺测试 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

⑧ 完成用户界面:

现在,需要做的就是启用表单,以便可以添加新的便笺。为此,需要为单击“添加便笺”按钮时添加处理程序,并编写一些代码以创建新的便笺。 以下为添加处理程序的代码:

// 将新代码添加到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 新增便笺 HTML5 基础知识,第 3 部分 HTML5 API 之应用-介绍HTML5 API 的用法和价值,Canvas 提供的创造性,以及Web storage的离线应用

这是我们的测试运行! 看起来不错!新增便笺的键名应该是“ 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清理是安全的,请尝试一下! 请使用您的浏览器的菜单项-->开发者工具,以便观察更改变化。

4.Web storage应用优化清单

清单 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;
}