2.4 多媒体与图形:audio、video 与 canvas


2.4 多媒体与图形:audio、video 与 canvas

本节摘要:audio 与 video 让音视频成为 HTML 一等公民,canvas 与 SVG 打开像素级与矢量级两条图形路线。本节讲多媒体元素的属性体系、格式兼容与无障碍配套,讲 canvas 的位图模型与基本绘制流程,最后给出 canvas 对 SVG 的选型对比。

学习目标

阅读完本节,你应当能够:

  1. 用 video 与 audio 的属性组合出常见播放策略(自动播放、循环、预载);
  2. 用 source 元素做多格式兼容,并写出文本降级;
  3. 为视频配字幕轨道,说明无障碍配套的最小集合;
  4. 描述 canvas 的位图模型,完成矩形、路径、文字的基本绘制;
  5. 按场景在 canvas 与 SVG 之间做出选型并说明理由。

从插件到原生:先把属性盘清楚

原生多媒体的语法面很小,能量都在属性上。video 的常用属性一张表看全:

属性 作用 工程注意
controls 显示原生控件 不写则用户无法操作,除非自建控件
autoplay 自动播放 多数浏览器要求同时 muted 才生效
muted 静音 自动播放策略的通行证
loop 循环 背景氛围视频常用
poster 封面图 视频加载前的第一印象
preload 预载策略 none / metadata / auto 三档
width / height 尺寸 写明可避免加载时布局跳动

autoplay 这一行值得展开。浏览器厂商 collectively 对"带声音的自动播放"亮了红灯——用户没交互就出声的页面体验极差,于是策略统一为:静音的自动播放放行,有声的自动播放拦截。所以今天的自动播放视频几乎都是 muted 加 autoplay 加(可选)loop 的组合,海报位、氛围背景是它的正当场景;想给用户"有声体验",老老实实让用户点播放。

audio 与 video 属性几乎同构,少了 poster 与尺寸。一行出播放器:

<audio src="podcast.mp3" controls preload="metadata"></audio>

preload 的三档策略关系流量成本:none 是"用户点了我再下",metadata 只取时长等元信息,auto 让浏览器自行决定(通常提前缓冲)。移动端流量敏感,默认 metadata 是稳妥起点。

格式兼容:source 的多路供给

浏览器没有就视频格式达成大一统:MP4(H.264 视频 + AAC 音频)兼容性最广,WebM 与 OGG 是开放路线。解法是 source 元素——按优先级列出多个来源,浏览器挑第一个认识的:

<video controls width="640" poster="cover.jpg"> <source src="demo.webm" type="video/webm"> <source src="demo.mp4" type="video/mp4"> <track src="captions.zh.vtt" kind="subtitles" srclang="zh" label="中文字幕" default> <p>你的浏览器不支持原生视频,<a href="#transcript">这里提供文字实录</a>。</p> </video>

三件事在这段代码里同时发生:source 列表做格式兼容(WebM 在前,能省则省体积,MP4 兜底);track 元素挂了一条字幕轨道;最后的 p 是终极降级——老到连 video 都不认识的浏览器会把它显示出来。type 属性带格式与编码信息,浏览器据此跳过不认识的源,省一次探测下载。

字幕不只是照顾听障用户:静音自动播放的视频配字幕是所有用户的刚需;搜不到声音的搜索引擎也依赖文本轨理解视频内容。WebVTT 格式的字幕文件就是纯文本加时间轴,任何文本编辑器都能写。kind 属性区分 subtitles(翻译字幕)与 captions(含声音描述的字幕),两者服务不同场景。

canvas:一块脚本能画的位图

canvas 元素本身只是一块固定尺寸的空白画布,真正的绘制全靠脚本。它的心智模型是位图 + 画笔:你拿到一个二维绘图上下文,用命令式的方式一笔一笔画,画完的结果是像素,浏览器不再记得"这里曾有一个矩形"。

<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><title>画布第一课</title></head> <body> <canvas id="board" width="400" height="220" style="border:1px solid #ccc">你的浏览器不支持画布。</canvas> <script> const ctx = document.getElementById('board').getContext('2d'); // 矩形:填一块底色 ctx.fillStyle = '#eaf2fc'; ctx.fillRect(10, 10, 380, 200); // 路径:画一个三角形 ctx.beginPath(); ctx.moveTo(200, 40); ctx.lineTo(280, 170); ctx.lineTo(120, 170); ctx.closePath(); ctx.fillStyle = '#2980b9'; ctx.fill(); // 文字 ctx.fillStyle = '#2c3e50'; ctx.font = '18px sans-serif'; ctx.fillText('画布上的三角形', 140, 200); </script> </body> </html>

绘制流程永远是四拍:取上下文、设状态(颜色、线宽、字体)、定义路径、填充或描边。矩形有快捷方法,其余形状走路径。状态是全局的——fillStyle 设了之后一直生效到下次修改,画复杂图时要留意状态的保存与恢复。

位图模型决定了 canvas 的两面性。优势:逐像素控制、与位图图像无缝混合、大规模重绘性能可控,游戏与数据可视化首选。代价:画完即忘——图形不再有 DOM 节点,没法给它挂事件监听(点击了哪个三角形要自己用坐标算)、没法被读屏理解(对无障碍它是空白,需要额外提供文本替代表述)、放大就糊(位图没有矢量信息)。这些代价反过来正是 SVG 的优势面。

SVG:文档里的矢量图形

SVG 作为另一种图形路线,是声明式的矢量标记:图形以元素写进文档,每个圆、每条线都是 DOM 节点,可挂事件、可被样式表控制、无限缩放不失真。本册的配图大量使用内联 SVG,你在正文中已经见过不少。一条心形的差别示意:

SVG:文档里的矢量图形

canvas 对 SVG:选型对比

维度 canvas SVG
图形模型 位图,像素矩阵 矢量,几何描述
交互 坐标自算,无节点 每个图形是 DOM 节点,直接挂事件
无障碍 差,需文本替代 好,可加标题与描述
缩放 放大模糊 无限缩放不失真
元素数量 与复杂度无关 数千节点后 DOM 开销显著
典型场景 游戏、图像处理、热力图 图标、架构图、图表、动画插画

选型口诀:动得猛、算得多选 canvas;要交互、要缩放、要无障碍选 SVG。工程里两者混用很常见:图表库底层数千个数据点用 canvas 画,图例与提示层用 SVG 做;页面小图标直接内联 SVG,单色可控、随字号缩放。别把选型当信仰,它是渲染模型的匹配问题。

02-04-fig02

画布进阶:动画与状态

第一课的四拍流程是静态绘制,canvas 的真正威力在动画循环。浏览器提供逐帧回调接口,每帧先清空画布再重画,静止图就动了:

<canvas id="anim" width="400" height="150" style="border:1px solid #ccc"></canvas> <script> const cv = document.getElementById('anim'); const g = cv.getContext('2d'); let x = 20, dir = 1; function frame() { g.clearRect(0, 0, cv.width, cv.height); // 擦掉上一帧 g.fillStyle = '#2980b9'; g.beginPath(); g.arc(x, 75, 22, 0, Math.PI * 2); // 圆 g.fill(); x += dir * 2; if (x > 380 || x < 20) dir = -dir; // 碰壁反弹 requestAnimationFrame(frame); // 排队下一帧 } frame(); </script>

这段循环揭示了位图模型的工作方式:所谓动画,就是"擦除、重算、重画"的高速重复,画布本身不记得任何上一帧的东西。对比一下 SVG 的动画思路——改一个属性值(比如圆心坐标),浏览器自动补间过渡。命令式全控与声明式托管,两种图形哲学在这里碰面,这也是选型时"动得猛选 canvas"的根据:数千个对象每帧重算时,canvas 的固定成本优势明显。

画布还有一个常被忽略的工程事实:它有两种尺寸。元素属性 width/height 决定画布的像素分辨率,CSS 尺寸决定显示大小,两者解耦。高分辨率屏幕上把画布属性尺寸设为 CSS 尺寸的两倍、再用 CSS 压回显示尺寸,图形立刻锐利一倍——这是画布版的"高分屏适配",数据大屏与图表项目里是必修动作。反过来,只写 CSS 尺寸不写属性尺寸,画布默认三百乘一百五,图全糊。

多媒体的元信息与接口

video 与 audio 还暴露了一组脚本接口与事件,把"播放器"变成了可编程对象。常用的状态接口:paused(是否暂停)、currentTime(当前进度,可读写,拖进度条就是改它)、duration(总时长,元信息加载后才有效)、volume(音量)。常用的事件:timeupdate 每次进度变化触发(自制进度条靠它)、ended 播放结束触发(连播下一集)、canplay 可开始播放时触发(解锁自定义播放按钮)。

<video id="v" src="lesson.mp4" width="480"></video> <div> <button id="play">播放</button> <button id="jump">跳到第 10 秒</button> <span id="info"></span> </div> <script> const v = document.getElementById('v'); document.getElementById('play').onclick = () => v.play(); document.getElementById('jump').onclick = () => { v.currentTime = 10; }; v.addEventListener('timeupdate', () => { document.getElementById('info').textContent = Math.floor(v.currentTime) + ' 秒 / 共 ' + Math.floor(v.duration) + ' 秒'; }); </script>

注意一个细节:示例里去掉了 controls,播放控制完全交给自建按钮——这正是很多视频站的做法(统一品牌化的播放器 UI)。自建控件的代价在第 4 章会算总账:键盘可达性、焦点管理、字幕开关,原生控件免费送的东西全要自己做。所以"要不要自建控件"的判断标准是:定制收益能不能盖过无障碍与维护成本,小项目答案是几乎不能。

另一个值得知道的能力是图片级的 loading 属性同样适用于内容资源思路:多媒体虽然没有 img 那样的原生懒加载属性,但"preload 设 none 加 data 属性存真实地址、进入视口再赋值"是标准的手工懒加载模式,思路与第 5 章性能节讲的图片懒加载一脉相承。

无障碍与懒加载配套

多媒体是可访问性的重灾区,最小配套三件套:视频配 track 字幕;audio 提供文字实录链接;canvas 提供文本替代表述(页面上写清"此图展示了什么")。另外 audio 与 video 是 HTML 里少数自带 alt 机制的缺席者——规范没给它们 alt 属性,替代内容就写在元素内部(上文 source 后面的那段 p 就是),两全其美:支持的浏览器忽略它,不支持的浏览器显示它。

加载策略上,多媒体是页面体量的大头。preload 默认值交浏览器决定未必合你意——首屏外的视频一律 preload 值设为 none,配 loading 懒加载图片同理;配合"封面图 + 点击后再初始化播放器"的延迟方案,能把首屏流量砍掉一大块。这套优化思路第 5 章性能节还会系统整理。

⚠️ 常见坑:给视频写 autoplay 却忘了 muted,然后在本地测试"明明能播"。本地文件或用户已发生交互的页面可能享受宽松策略,上线后真实用户遇到的是严格拦截——自动播放组合永远写全 muted 属性再测。

动手实验:三媒体一页

<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><title>多媒体实验</title></head> <body> <h1>多媒体三连</h1> <section> <h2>音频</h2> <audio controls preload="metadata" src="t-rex-roar.mp3"> <p>听不了?文字实录在此。</p> </audio> </section> <section> <h2>视频</h2> <video controls width="480" muted poster="flower-cover.jpg" src="flower.mp4"> <track kind="captions" label="暂无字幕" srclang="zh"> <p>浏览器不支持原生视频。</p> </video> </section> <section> <h2>画布</h2> <canvas id="c" width="480" height="120" style="border:1px solid #ccc"></canvas> <script> const g = document.getElementById('c').getContext('2d'); for (let i = 0; i < 12; i++) { g.fillStyle = i % 2 ? '#2980b9' : '#e67e22'; g.fillRect(20 + i * 38, 30 + (i % 3) * 20, 30, 60); } </script> </section> </body> </html>

示例里的音视频用公开的免授权素材地址,联网即可播放。实验建议:删掉 muted 刷新,观察自动播放相关行为差异(需自行加 autoplay 试验);给画布循环改几个参数,体会"命令式绘制"与 HTML"声明式标记"的手感差异——这个差异正是 canvas 与 SVG 选型的手感版。

两个高频疑问

问:视频要不要自己写播放器控件?
默认不要。原生 controls 免费带键盘支持、无障碍标签、各平台习惯的交互;自建控件要补齐这一切才能打平,多数项目补不齐。只有当"品牌化 UI"或"特殊交互"的收益明确时才自建,且自建时必须逐项核对第 4 章的可访问性清单。

问:canvas 上画的图表,怎么让读屏用户知道内容?
双管齐下:页面上提供数据表格(图表的文本替代表述,本就把同一份数据换一种呈现),并在画布元素内部写一段说明文字作为降级内容。图表库的成熟方案也是这条路——可视化给看得见的用户,表格给听的用户和搜索引擎,一份数据两种表达。

本节要点回顾

  • 属性即策略:controls 出控件、muted 加 autoplay 过拦截、preload 管流量、poster 管第一印象。
  • source 多路 + 文本降级:格式兼容按优先级列源,元素内部内容是不支持时的最终表达。
  • 字幕是刚需不是慈善:track 挂 WebVTT 字幕,静音播放、听障用户、搜索引擎三方受益。
  • canvas 是画完即忘的位图:逐像素能力换来了无节点、无事件、无障碍差;游戏与大数据量场景的最优解。
  • SVG 是可交互的矢量 DOM:节点级事件、无损缩放、可无障碍;图标与图表的默认答案。
  • 选型口诀:动得猛算得多选 canvas,要交互要缩放选 SVG,混用是常态。

第 2 章收官。结构层的大厦盖完,下一章给大厦刷漆——HTML 与 CSS 的协作机制。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U