第11章 HTML5的拖放和画布

习题解答

🙈 隐藏所有答案

编程题

1. 使用 HTML5 拖放 API 实现购物车拖放效果,如图 11-10 所示。
图 11-10 购物车拖放初始状态 图 11-10 购物车拖放后状态

这道题考的是 HTML5 的拖放 API。就像你在淘宝把商品拖进购物车一样——先让商品图片"可以被拖"(draggable="true"),再让购物车区域"允许接收"(监听 dragoverdrop 事件)。

从效果图可以看出:

  • 左侧是"商品列表"区域,展示多张商品图片(3张女装图),背景浅灰色
  • 右侧是"购物车"区域,背景浅灰色,初始为空,拖放后显示被拖入的图片
  • 两个区域各占页面约一半宽度,并排显示
  • 图片尺寸约为 100×130px,排列在区域中

完整代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML 5中的拖放</title>
    <style>
        body {
            font-family: "Microsoft YaHei", sans-serif;
            margin: 0;
            padding: 0;
        }
        /* 两栏布局:商品列表 + 购物车 */
        .container {
            display: flex;
            width: 100%;
            min-height: 300px;
        }
        /* 左侧商品列表 */
        #product-list {
            width: 50%;
            background: #d9d9d9;
            padding: 15px;
            box-sizing: border-box;
        }
        /* 右侧购物车 */
        #cart {
            width: 50%;
            background: #d9d9d9;
            padding: 15px;
            box-sizing: border-box;
            border-left: 1px solid #bbb;
        }
        h2 {
            font-size: 1.2rem;
            font-weight: bold;
            margin: 0 0 10px 0;
            padding-bottom: 8px;
            border-bottom: 1px solid #999;
        }
        /* 商品图片样式 */
        .product-img {
            width: 100px;
            height: 130px;
            object-fit: cover;
            margin: 5px;
            cursor: grab;
            border: 2px solid transparent;
            transition: border-color 0.2s;
        }
        .product-img:hover {
            border-color: #666;
        }
        /* 拖放目标区域高亮 */
        #cart.drag-over {
            background: #c8e6c9;
            border: 2px dashed #4caf50;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 左侧:商品列表 -->
    <div id="product-list">
        <h2>商品列表</h2>
        <!-- draggable="true" 使图片可以被拖动 -->
        <img class="product-img" src="images/girl1.jpg"
             draggable="true"
             ondragstart="dragStart(event)"
             alt="商品1">
        <img class="product-img" src="images/girl2.jpg"
             draggable="true"
             ondragstart="dragStart(event)"
             alt="商品2">
        <img class="product-img" src="images/girl3.jpg"
             draggable="true"
             ondragstart="dragStart(event)"
             alt="商品3">
    </div>

    <!-- 右侧:购物车 -->
    <div id="cart"
         ondragover="dragOver(event)"
         ondragleave="dragLeave(event)"
         ondrop="drop(event)">
        <h2>购物车</h2>
    </div>
</div>

<script type="text/javascript">
    // 拖动开始:记录被拖动图片的 src
    function dragStart(event) {
        // 把被拖动元素的 outerHTML 存入 dataTransfer
        event.dataTransfer.setData("text/html", event.target.outerHTML);
        event.dataTransfer.effectAllowed = "copy";
    }

    // 拖动悬停在目标上:必须阻止默认行为,否则无法触发 drop
    function dragOver(event) {
        event.preventDefault();
        event.dataTransfer.dropEffect = "copy";
        document.getElementById("cart").classList.add("drag-over");
    }

    // 拖动离开目标区域:取消高亮
    function dragLeave(event) {
        document.getElementById("cart").classList.remove("drag-over");
    }

    // 放下:把图片克隆到购物车中
    function drop(event) {
        event.preventDefault();
        var cart = document.getElementById("cart");
        cart.classList.remove("drag-over");

        // 取出之前保存的 HTML 内容
        var imgHtml = event.dataTransfer.getData("text/html");

        // 创建一个临时容器来解析 HTML
        var temp = document.createElement("div");
        temp.innerHTML = imgHtml;
        var img = temp.firstChild;

        // 调整样式后插入购物车
        img.style.margin = "5px";
        cart.appendChild(img);
    }
</script>
</body>
</html>

代码要点说明:

  • 给商品图片设置 draggable="true",这是启用拖放的第一步。
  • ondragstart:拖动开始时,用 dataTransfer.setData() 把图片的 HTML 内容存起来,相当于"复制到剪贴板"。
  • ondragover:必须调用 event.preventDefault(),否则浏览器会拒绝 drop 事件(默认不允许放置)。
  • ondrop:用 dataTransfer.getData() 取出内容,插入购物车容器中。

📖 参见 11.1 拖放、11.1.1 draggable属性、11.1.2 拖放事件、11.1.3 dataTransfer对象

2. 使用 <video> 标签播放视频,如图 11-11 所示。
图 11-11 播放视频效果

这道题考的是 HTML5 的 <video> 标签。HTML5 之前播放视频需要 Flash 插件,现在直接用一个标签就搞定了!效果图显示:页面标题是"播放视频",视频内容是一段海岛风光,宽度铺满页面主体,显示播放控制条(进度条、播放按钮、时间、音量、全屏按钮)。

完整代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>视频标签video示例</title>
    <style>
        body {
            font-family: "Microsoft YaHei", sans-serif;
            margin: 0;
            padding: 10px 15px;
            background: #fff;
        }
        h2 {
            font-size: 1.1rem;
            font-weight: bold;
            margin-bottom: 10px;
        }
        /* 视频元素样式:宽度撑满容器 */
        video {
            display: block;
            width: 100%;
            max-width: 700px;
            background: #000;
        }
    </style>
</head>
<body>
    <h2>播放视频</h2>

    <!--
        controls:显示播放控制条(播放/暂停、进度条、音量、全屏)
        width:视频宽度
        可选属性:
          autoplay  自动播放
          loop      循环播放
          muted     静音
          poster    封面图片
    -->
    <video controls width="700">
        <!-- source 元素指定视频文件路径,可同时提供多种格式以兼容不同浏览器 -->
        <source src="videos/beach.mp4" type="video/mp4">
        <source src="videos/beach.webm" type="video/webm">
        <!-- 当浏览器不支持 video 标签时显示此提示 -->
        您的浏览器不支持 HTML5 video 标签,请升级浏览器。
    </video>
</body>
</html>

代码要点说明:

  • controls 属性(布尔属性,不用赋值):显示浏览器默认的播放控制条,效果图中可以看到有播放按钮、进度条、时间显示、音量和全屏按钮。
  • <source> 元素:推荐同时提供 MP4 和 WebM 两种格式,浏览器会自动选择自己支持的那种。
  • type 属性:告诉浏览器视频格式,让它能快速判断是否支持,无需先下载再检测。
  • 兜底文字:写在 <video></video> 之间,只在浏览器不支持时显示。
  • 常用可选属性:autoplay(自动播放)、loop(循环)、muted(静音)、poster="封面.jpg"(视频封面图)。

📖 参见 HTML5 媒体元素 <video> 标签及其 controls、src、type 属性

3. 使用 canvas 元素绘制圆饼图,如图 11-12 所示。
图 11-12 Canvas圆饼图效果

这道题考的是 Canvas 的路径绘图——用 arc()lineTo() 画出多个扇形,拼成一张圆饼图。效果图显示:一个完整圆形被分成 5 个扇形,颜色各异(原图为彩色,在灰度截图中可见明显的明暗区分),绘制在带边框的白色 canvas 上。

完整代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Canvas绘制圆饼图</title>
    <style>
        body {
            font-family: "Microsoft YaHei", sans-serif;
            margin: 10px;
        }
        canvas {
            display: block;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <!-- canvas 画布:宽300,高270,加边框 -->
    <canvas id="myCanvas" width="300" height="270"
            style="border:1px solid #ccc;"></canvas>

    <script type="text/javascript">
        // 获取 canvas 元素和 2D 绘图上下文
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");

        // 圆饼图的数据:各扇形占比(百分比,合计100)
        var data = [30, 15, 25, 20, 10]; // 5个数据项

        // 对应的填充颜色(5种颜色)
        var colors = [
            "#e74c3c",   // 红
            "#3498db",   // 蓝
            "#2ecc71",   // 绿
            "#f39c12",   // 橙
            "#9b59b6"    // 紫
        ];

        // 圆心坐标和半径
        var cx = 150;    // 圆心 X
        var cy = 135;    // 圆心 Y
        var radius = 110; // 半径

        // 起始角度:从正上方(-90度,即 -π/2)开始,习惯上饼图从12点钟方向开始
        var startAngle = -Math.PI / 2;

        // 计算数据总和
        var total = 0;
        for (var i = 0; i < data.length; i++) {
            total += data[i];
        }

        // 逐个绘制扇形
        for (var i = 0; i < data.length; i++) {
            // 当前扇形的弧度 = (当前数据 / 总数) * 2π
            var sliceAngle = (data[i] / total) * 2 * Math.PI;
            var endAngle = startAngle + sliceAngle;

            ctx.beginPath();
            // 移动到圆心
            ctx.moveTo(cx, cy);
            // 画弧线(顺时针方向)
            ctx.arc(cx, cy, radius, startAngle, endAngle, false);
            // 连线回到圆心,形成闭合扇形
            ctx.closePath();

            // 设置填充颜色并填充
            ctx.fillStyle = colors[i];
            ctx.fill();

            // 绘制白色边框线,让扇形之间有分隔
            ctx.strokeStyle = "#ffffff";
            ctx.lineWidth = 2;
            ctx.stroke();

            // 下一个扇形从当前结束角度开始
            startAngle = endAngle;
        }
    </script>
</body>
</html>

代码要点说明:

  • 每个扇形的绘制路径:moveTo(圆心)arc(画弧)closePath(连回圆心),三步构成一个封闭扇形。
  • arc(cx, cy, r, 起始角, 结束角, 逆时针):角度以弧度为单位,0 对应3点钟方向;用 -Math.PI/2 作起点,让饼图从12点钟位置开始,更符合阅读习惯。
  • 每个扇形的弧度 = (该项数据 / 总数据) × 2π,这是饼图的核心公式。
  • beginPath() 每次必须调用,否则所有路径会连在一起。
  • 白色边框(strokeStyle = "#ffffff")用于在扇形之间画出清晰的分隔线。

📖 参见 11.2 画布(canvas)、11.2.4 绘制路径(arc方法)、11.2.5 绘制样式(fillStyle/fill)

4. 使用 Geolocation API 实现地理定位,首先测试浏览器是否支持地理定位,如果支持则弹出消息框显示支持的信息;单击"确定"按钮后,再次弹出消息框显示当前位置的经度与纬度。

这道题考的是 HTML5 的地理定位 API(Geolocation API)。就像手机地图的"我的位置"功能——浏览器会先问用户"是否允许获取你的位置",用户同意后才能拿到经纬度数据。

实现思路:

  1. navigator.geolocation 判断浏览器是否支持地理定位。
  2. 支持则弹出 alert() 提示"支持地理定位"。
  3. 用户点"确定"后,调用 navigator.geolocation.getCurrentPosition() 获取位置。
  4. 在成功回调中拿到 position.coords.latitude(纬度)和 position.coords.longitude(经度),用 alert() 显示出来。

完整代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML5地理定位</title>
</head>
<body>
    <h3>HTML5 Geolocation 地理定位示例</h3>
    <button onclick="getLocation()">获取我的位置</button>
    <p id="result"></p>

    <script type="text/javascript">
        function getLocation() {
            // 第一步:检测浏览器是否支持 Geolocation API
            if (navigator.geolocation) {
                // 支持:弹出提示框
                alert("您的浏览器支持地理定位!");

                // 用户点击"确定"后,请求获取当前位置
                // getCurrentPosition() 是异步的,成功时调用 showPosition,失败时调用 showError
                navigator.geolocation.getCurrentPosition(showPosition, showError);
            } else {
                // 不支持:提示升级
                alert("您的浏览器不支持地理定位功能,请升级浏览器!");
            }
        }

        // 获取位置成功的回调函数
        // position 对象包含 coords(坐标信息)和 timestamp(时间戳)
        function showPosition(position) {
            var lat = position.coords.latitude;   // 纬度
            var lng = position.coords.longitude;  // 经度

            // 弹出消息框显示经纬度
            alert("当前位置:\n纬度(latitude)= " + lat + "\n经度(longitude)= " + lng);

            // 也可以在页面中显示
            document.getElementById("result").innerHTML =
                "纬度:" + lat.toFixed(6) + "<br>经度:" + lng.toFixed(6);
        }

        // 获取位置失败的回调函数(用户拒绝授权或定位超时等)
        function showError(error) {
            switch(error.code) {
                case error.PERMISSION_DENIED:
                    alert("用户拒绝了地理定位请求。");
                    break;
                case error.POSITION_UNAVAILABLE:
                    alert("位置信息不可用。");
                    break;
                case error.TIMEOUT:
                    alert("请求超时。");
                    break;
                default:
                    alert("发生未知错误:" + error.message);
            }
        }
    </script>
</body>
</html>

代码要点说明:

  • navigator.geolocation:这是浏览器内置的地理定位对象,存在则表示支持,用 if 判断即可。
  • getCurrentPosition(成功回调, 失败回调):异步方法,浏览器会弹出"是否允许获取位置"的权限提示,用户同意后才执行成功回调。
  • position.coords.latitude:纬度(南北方向,范围 -90° ~ 90°)。
  • position.coords.longitude:经度(东西方向,范围 -180° ~ 180°)。
  • 注意:HTTPS 环境才能正常使用 Geolocation API,本地 file:// 协议在部分浏览器中也可以测试。

📖 参见 HTML5 Geolocation API、navigator.geolocation.getCurrentPosition() 方法