HTML 多媒体:Canvas、SVG、Video 与 Audio

2024-08-23 前端

一、引言

HTML5 为 Web 平台带来了革命性的多媒体能力。曾经需要 Flash、Silverlight 或 Java Applet 才能实现的功能——播放视频、渲染矢量图形、实时绘制动画——如今都已原生内置于浏览器中。本文将深入探讨 HTML 多媒体生态的四大核心领域:图片优化与响应式技术、SVG 矢量图形、Canvas 2D/3D 绘制、以及 Video/Audio 媒体播放。

每个知识点均配有可直接运行的完整代码示例,旨在帮助你从"会用"进阶到"精通"。


二、图片深度:从 <img> 到现代化图像管道

2.1 基础 <img> 元素

图片是 Web 最古老的多媒体形式。<img> 元素的核心属性:

<img 
  src="photo.jpg" 
  alt="描述文字" 
  width="800" 
  height="600"
  decoding="async"
  fetchpriority="high"
>
  • alt:必需——屏幕阅读器依赖它,SEO 也依赖它。
  • width / height:设置宽高比,防止布局偏移(CLS)。
  • decoding="async":异步解码,不阻塞 DOM 解析。
  • fetchpriority="high":提示浏览器优先加载(LCP 优化)。

2.2 响应式图片:srcset 与 sizes

同一张图片在不同设备上需要不同分辨率。srcset 提供多套候选图,sizes 告诉浏览器图片实际渲染宽度。

<img
  src="photo-800.jpg"
  srcset="
    photo-400.jpg 400w,
    photo-800.jpg 800w,
    photo-1200.jpg 1200w,
    photo-1600.jpg 1600w
  "
  sizes="
    (max-width: 600px) 100vw,
    (max-width: 1200px) 50vw,
    33vw
  "
  alt="响应式示例"
>

工作原理:

  1. 浏览器检查 sizes,根据当前视口宽度计算图片渲染宽度。
  2. 在 srcset 中选取大于等于渲染宽度且最接近的那张图。
  3. 如果设备像素比(DPR)为 2x,还会加倍选取。

你也可以用 x 描述符按 DPR 匹配:

<img
  src="photo-1x.jpg"
  srcset="photo-1x.jpg 1x, photo-2x.jpg 2x, photo-3x.jpg 3x"
  alt="DPR 响应式"
>

2.3 <picture> 与 <source>:艺术指导与格式回退

当需要不同裁剪(艺术指导)或不同格式时,用 <picture> 包裹多个 <source>:

<picture>
  <!-- AVIF 格式,优先 -->
  <source 
    srcset="photo.avif" 
    type="image/avif"
  >
  <!-- WebP 格式回退 -->
  <source 
    srcset="photo.webp" 
    type="image/webp"
  >
  <!-- 手机端横图裁剪 -->
  <source 
    srcset="photo-mobile.jpg"
    media="(max-width: 600px)"
  >
  <!-- 最终 fallback -->
  <img 
    src="photo.jpg" 
    alt="完整回退链"
    loading="lazy"
  >
</picture>

<source> 的 media 属性支持任何媒体查询,type 属性用于格式协商。浏览器从第一个 <source> 开始依次尝试,一旦匹配就停止。

2.4 现代图片格式:WebP 与 AVIF

格式 压缩率 vs JPEG 特性
WebP 小 25-35% 有损/无损/透明/动画,Chrome 系原生支持
AVIF 小 50%+ AV1 编码,更优压缩,HDR 支持,渐进解码

检测浏览器是否支持 AVIF:

function checkAVIFSupport() {
  const img = new Image();
  img.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlm';
  return new Promise(resolve => {
    img.onload = () => resolve(true);
    img.onerror = () => resolve(false);
  });
}

checkAVIFSupport().then(supported => {
  console.log('AVIF 支持:', supported);
});

2.5 懒加载:loading="lazy"

HTML 原生的懒加载无需 JavaScript:

<img src="large-photo.jpg" loading="lazy" alt="懒加载图片">
<iframe src="map.html" loading="lazy" title="地图"></iframe>

loading 属性值:

值 行为
lazy 图片进入视口附近才开始加载
eager 立即加载(默认)

白名单机制:loading=lazy 只在以下情况生效:

  • 设置了 width 和 height(浏览器需提前预留空间)
  • 图片不在首屏视口内
  • 未被 fetchpriority="high" 覆盖

Intersection Observer 自定义懒加载(更精细控制):

<img 
  data-src="real-image.jpg" 
  class="lazy" 
  alt="IO 懒加载"
  width="800" 
  height="600"
>
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      observer.unobserve(img);
    }
  });
}, { rootMargin: '200px' });

document.querySelectorAll('img.lazy').forEach(img => observer.observe(img));

2.6 图片 CDN 与优化策略

专业图片 CDN(Cloudinary、Imgix、Akamai Image & Video Manager)可在 URL 层面动态处理图片:

https://res.cloudinary.com/demo/image/upload/
  w_800,h_600,c_fill,f_auto,q_auto/v1/photo.jpg

参数解读:

  • w_800,h_600,c_fill:裁剪为 800×600
  • f_auto:浏览器协商最优格式(WebP/AVIF)
  • q_auto:自动质量

自建优化清单:

1. 格式选择:优先 AVIF → WebP → JPEG/PNG
2. 分辨率:每张图不超过实际渲染尺寸的 2x(DPR)
3. 压缩:JPEG 质量 85,PNG 用 pngquant
4. 响应式:提供 5-10 个断点版本
5. 懒加载:所有非首屏图片加 loading=lazy
6. CDN:开启格式协商、质量自适应、WebP 转换
7. 预加载关键图片(LCP):
   <link rel="preload" as="image" href="hero.webp">

三、SVG:可缩放矢量图形

SVG(Scalable Vector Graphics)是用 XML 描述 2D 图形的格式。它无限缩放不失真,文件体积通常小于位图,且可通过 CSS/JavaScript 完全控制。

3.1 在 HTML 中使用 SVG 的四种方式

方式一:内联 SVG(推荐)

<svg width="100" height="100" viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="40" fill="#4A90D9" />
</svg>

优点:可被 CSS/JS 完全控制,可继承文档样式,无需额外 HTTP 请求。
缺点:增加 HTML 体积,不易缓存。

方式二:<img> 引用

<img src="icon.svg" alt="SVG 图标">

优点:浏览器缓存,HTML 简洁。
缺点:不能通过 CSS 修改内部颜色,不能响应内部交互。

方式三:<object> 嵌入

<object data="chart.svg" type="image/svg+xml" width="400" height="300">
  <!-- 回退内容 -->
  <img src="chart.png" alt="图表回退">
</object>

优点:保留 SVG 内部脚本和交互,支持回退内容。
缺点:与宿主页面的 CSS 隔离。

方式四:<iframe> 嵌入

<iframe src="widget.svg" width="400" height="300" title="SVG 部件"></iframe>

优点:完全隔离,适合第三方 SVG 应用。
缺点:无法与父页面通信(除非 postMessage),SEO 不友好。

3.2 常用 SVG 元素

3.3 defs 与 use:SVG 精灵图

<defs> 定义可复用的图形资源,<use> 引用它们。这是SVG 精灵图的核心技术。

<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  <defs>
    <!-- 定义一个图标 -->
    <g id="icon-home">
      <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
    </g>
    <g id="icon-user">
      <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
    </g>
    <!-- 带多种颜色的复杂图标 -->
    <g id="icon-star">
      <polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
    </g>
  </defs>
</svg>

<!-- 使用图标 -->
<svg width="24" height="24" viewBox="0 0 24 24">
  <use href="#icon-home" fill="#333" />
</svg>
<svg width="32" height="32" viewBox="0 0 24 24">
  <use href="#icon-user" fill="#4A90D9" />
</svg>
<svg width="48" height="48" viewBox="0 0 24 24">
  <use href="#icon-star" fill="#F1C40F" />
</svg>

SVG 精灵图 vs 字体图标 vs PNG Sprite:

方案 清晰度 色彩 体积 可控性
SVG Sprite ∞ 多色 小 最高
字体图标 仅矢量 单色 小 一般
PNG Sprite 受限 任意 大 最低

3.4 响应式 SVG

SVG 天生响应式,但需要正确设置 viewBox。

<!-- 正确:使用 viewBox 设置宽高比,不设 width/height 或设 100% -->
<svg viewBox="0 0 200 100" preserveAspectRatio="xMidYMid meet">
  <rect x="0" y="0" width="200" height="100" fill="#3498DB" rx="10" />
  <circle cx="50" cy="50" r="30" fill="#E74C3C" />
  <circle cx="150" cy="50" r="30" fill="#2ECC71" />
</svg>

preserveAspectRatio 详解:

语法:preserveAspectRatio="<align> <meetOrSlice>"

对齐值 含义
xMinYMin 左上角对齐
xMidYMid 居中(默认)
xMaxYMax 右下角对齐
meetOrSlice 含义
meet 完整显示(类似 contain)
slice 裁剪填充(类似 cover)
/* CSS 让 SVG 铺满容器 */
.responsive-svg {
  width: 100%;
  height: auto;
  max-width: 800px;
  display: block;
}

3.5 SVG 动画

方式一:CSS @keyframes + SVG 属性

方式二:SMIL 动画(原生 SVG 动画)

SMIL(Synchronized Multimedia Integration Language)直接在 SVG 标签内定义动画,无需 CSS 或 JS。

SMIL 核心元素:

元素 作用
<animate> 驱动单一属性动画
<animateTransform> 驱动变换动画(旋转、缩放、平移)
<animateMotion> 沿路径移动
<set> 瞬间设置属性值(非连续动画)

四、Canvas:像素级的绘图画布

Canvas 提供位图级别的绘制 API,通过 JavaScript 逐像素控制。与 SVG 的"保留模式"(保留图形对象树)不同,Canvas 使用立即模式——绘制后不保留对象信息。

4.1 基础与上下文

<canvas id="myCanvas" width="800" height="600">
  您的浏览器不支持 Canvas。
</canvas>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 检查上下文
if (!ctx) {
  console.error('Canvas 2D 上下文获取失败');
}

4.2 绘制形状

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// ----- 矩形 -----
ctx.fillStyle = '#3498DB';
ctx.fillRect(20, 20, 150, 100);          // 填充矩形

ctx.strokeStyle = '#E74C3C';
ctx.lineWidth = 4;
ctx.strokeRect(200, 20, 150, 100);       // 描边矩形

ctx.clearRect(50, 40, 80, 60);           // 擦除区域

// ----- 圆形(通过路径) -----
ctx.beginPath();
ctx.arc(400, 70, 50, 0, Math.PI * 2);    // 完整圆
ctx.fillStyle = '#2ECC71';
ctx.fill();
ctx.strokeStyle = '#27AE60';
ctx.lineWidth = 3;
ctx.stroke();

// 半圆(弧)
ctx.beginPath();
ctx.arc(520, 70, 50, 0, Math.PI);        // 上半圆
ctx.fillStyle = '#E67E22';
ctx.fill();

4.3 路径与贝塞尔曲线

4.4 文字渲染

4.5 渐变与阴影

4.6 图像处理:drawImage、getImageData、putImageData

4.7 Canvas 动画循环

使用 requestAnimationFrame 替代 setInterval 或 setTimeout,因为它:

  1. 与浏览器渲染帧同步(通常 60fps)
  2. 页面不可见时自动暂停,节省 CPU
  3. 更省电,动画更流畅

4.8 Canvas vs SVG 对比

维度 Canvas SVG
渲染模型 立即模式(像素) 保留模式(矢量对象树)
清晰度 受分辨率限制 无限缩放
对象操作 需重绘整个画布 DOM 操作单个元素
事件处理 需手动计算命中(isPointInPath) 原生 DOM 事件
性能(大量元素) 极优(>1000 元素) 下降明显(>1000 元素)
性能(少量元素 + 动画) 优 优
文本渲染 有限(无自动换行) 完整(与 HTML 一致)
无障碍 差(屏幕阅读器无法读取) 好(可加 aria-label)
导出 toDataURL / toBlob 序列化为 SVG 字符串
适用场景 游戏、图像处理、数据可视化(大量点)、实时视频处理 图标、图表、UI 组件、交互式地图、Logo

选择黄金法则:

如果元素数量 < 500 且需要交互 → SVG
如果元素数量 > 500 或需要像素操作 → Canvas
如果两样都要 → Canvas 上层叠 SVG 层

五、Video 与 Audio

5.1 <video> 元素属性

<video 
  src="movie.mp4"
  controls
  width="720"
  height="404"
  poster="thumbnail.jpg"
  preload="metadata"
  muted
  loop
>
  您的浏览器不支持视频播放。
</video>

核心属性详解:

属性 值 说明
src URL 视频源地址
controls 布尔 显示浏览器原生控制栏
autoplay 布尔 自动播放(被大多数浏览器限制,需配合 muted)
muted 布尔 静音
loop 布尔 循环播放
poster URL 封面图
preload none / metadata / auto 预加载策略
playsinline 布尔 iOS Safari 内联播放
crossorigin anonymous / use-credentials CORS 设置

自动播放三原则(现代浏览器):

1. 必须 muted(静音)
2. 必须有 user gesture(用户点击)
3. 或者浏览器判定用户"经常在此站播放媒体"

完整的多格式回退示例:

<video controls width="720" height="404" poster="poster.jpg">
  <!-- 按顺序尝试,直到找到可播放格式 -->
  <source src="movie.av1.mp4" type='video/mp4; codecs="av01.0.05M.08"'>
  <source src="movie.hevc.mp4" type='video/mp4; codecs="hvc1"'>
  <source src="movie.webm" type="video/webm">
  <source src="movie.mp4" type="video/mp4">

  <!-- 字幕 -->
  <track src="subtitles_zh.vtt" kind="subtitles" srclang="zh" label="中文">
  <track src="subtitles_en.vtt" kind="subtitles" srclang="en" label="English">

  <!-- 终极回退 -->
  <p>
    您的浏览器不支持 HTML5 视频。
    <a href="movie.mp4">下载视频文件</a>
  </p>
</video>

5.2 <track> 字幕与 WebVTT

WebVTT(Web Video Text Tracks)格式:

WEBVTT

00:00:01.000 --> 00:00:04.000
大家好,欢迎来到本教程

00:00:04.500 --> 00:00:08.000
今天我们来学习 HTML 多媒体技术

00:00:09.000 --> 00:00:12.500
首先是 Canvas 画布

00:00:15.000 --> 00:00:20.000 align:end
<v 旁白>这段是旁白说明</v>

00:00:22.000 --> 00:00:28.000
支持 <b>加粗</b> <i>斜体</i> 和 <c.textHighlight>高亮</c> 样式

<track> 的 kind 属性:

kind 用途
subtitles 翻译字幕
captions 听障字幕(含音效描述)
descriptions 视频内容语音描述
chapters 章节导航
metadata 元数据轨道(供脚本读取)
// JS 控制字幕切换
const video = document.querySelector('video');
const tracks = video.textTracks;

for (let i = 0; i < tracks.length; i++) {
  const track = tracks[i];

  // 切换到中文
  if (track.language === 'zh') {
    track.mode = 'showing';
  } else {
    track.mode = 'hidden';
  }
}

// 监听字幕队列变化
video.addEventListener('cuechange', function() {
  const activeCues = video.textTracks[0].activeCues;
  for (let i = 0; i < activeCues.length; i++) {
    console.log('当前字幕:', activeCues[i].text);
  }
});

5.3 JavaScript 媒体 API

5.4 媒体事件

HTML5 媒体元素触发丰富的事件,构建自定义播放器时非常有用。

5.5 自适应流式传输简介

HLS(HTTP Live Streaming)

Apple 开发的流媒体协议,将视频切分成 10s 的 TS 片段 + 一个 m3u8 索引文件。

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=854x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
1080p.m3u8
// 通过 hls.js 在非 Safari 浏览器播放 HLS
import Hls from 'hls.js';

const video = document.getElementById('video');
const hlsUrl = 'https://example.com/stream.m3u8';

if (Hls.isSupported()) {
  const hls = new Hls();
  hls.loadSource(hlsUrl);
  hls.attachMedia(video);
  hls.on(Hls.Events.MANIFEST_PARSED, () => {
    video.play();
  });

  // 监听自适应切换
  hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
    console.log(`切换到 ${data.level} 码率`);
  });
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
  // Safari 原生支持
  video.src = hlsUrl;
}

DASH(Dynamic Adaptive Streaming over HTTP)

MPEG-DASH 是国际标准,使用 MP4 片段 + MPD 清单文件。

// 通过 dash.js 播放
import dashjs from 'dashjs';

const player = dashjs.MediaPlayer().create();
player.initialize(
  document.getElementById('video'),
  'https://example.com/stream.mpd',
  true  // autoPlay
);

为什么需要自适应流:

  1. 自适应码率:根据网络状况自动切换画质
  2. 直播支持:低延迟直播
  3. DRM:数字版权保护
  4. 广告插入:在清单层面无缝插入

5.6 <audio> 基础

<audio> 的 API 与 <video> 几乎完全一致。

<audio controls preload="metadata">
  <source src="music.mp3" type="audio/mpeg">
  <source src="music.ogg" type="audio/ogg">
  <source src="music.flac" type="audio/flac">
  您的浏览器不支持音频播放。
</audio>

音频可视化(结合 Canvas):

5.7 MediaRecorder API:录音与录屏


六、WebGL 简介

WebGL(Web Graphics Library)是 Canvas 的 3D 上下文,基于 OpenGL ES 2.0/3.0。它通过 GPU 加速实现高性能 3D 渲染。

6.1 获取 WebGL 上下文

const canvas = document.getElementById('glCanvas');
let gl;

try {
  gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
} catch (e) {
  console.error('WebGL 不支持');
}

if (!gl) {
  alert('您的浏览器不支持 WebGL');
}

6.2 原生 WebGL 示例:渲染一个三角形

原生 WebGL 使用着色器(Shader)编程。我们来看一个最简示例:

6.3 Three.js 示例

实际项目中通常使用 Three.js 等库,封装了着色器和矩阵计算。

6.4 WebGL 能力检测与回退


七、综合实战:搭建一个多媒体仪表盘

将上述知识点融合——一个包含 Canvas 粒子背景、SVG 矢量图表、视频播放器和音频可视化器的综合示例。


八、性能最佳实践

8.1 Canvas 性能优化

8.2 SVG 性能优化

// 1. 精简 SVG(使用 SVGO 等工具)
// svgo icon.svg -o icon.min.svg

// 2. 使用 CSS 动画替代 SMIL(更高效)

// 3. 限制 DOM 节点数(< 5000)

// 4. 复杂图形使用 <canvas> 渲染后转为图片

// 5. SVG 精灵图合并多个 SVG 为一份文件

// 6. 对于不会变化的 SVG,用 <img> 或 CSS background 替代内联

8.3 媒体性能

// 1. 预加载关键视频
// <link rel="preload" as="video" href="hero.mp4">

// 2. 使用 poster 占位,延迟视频实际加载
// preload="metadata"

// 3. 视口外暂停
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const video = entry.target;
    if (entry.isIntersecting) {
      video.play();
    } else {
      video.pause();
    }
  });
}, { threshold: 0.3 });

document.querySelectorAll('video[data-autopause]').forEach(v => observer.observe(v));

// 4. 释放资源
video.removeAttribute('src');
video.load(); // 清空

九、总结

HTML5 多媒体 API 构成了现代 Web 交互体验的基石:

  • 图片:从 srcset / picture 到 AVIF / WebP,配合懒加载与 CDN,构成了高性能的图像管道。
  • SVG:无限缩放的矢量世界,通过内联方式获得 CSS/JS 完全控制,精灵图技术大幅优化 HTTP 请求。
  • Canvas:像素级的绘图引擎,适合游戏、数据可视化、图像处理等高性能场景。requestAnimationFrame 是其动画核心。
  • Video & Audio:原生媒体播放从 <video> / <audio> 到 MediaRecorder,从 WebVTT 字幕到 HLS / DASH 自适应流。
  • WebGL:通过 Three.js 等库,3D 渲染触手可及。

选择策略:

✅ 图标/Logo/简单交互 → SVG
✅ 数据可视化 (<500 元素) → SVG
✅ 数据可视化 (>500 元素) → Canvas
✅ 游戏/滤镜/图像处理 → Canvas
✅ 视频播放/字幕 → Video API
✅ 3D 渲染 → WebGL (Three.js)
✅ 录音/录屏 → MediaRecorder
✅ 需要像素操作 → Canvas getImageData

掌握这些技术,你就拥有了构建下一代 Web 应用的完整多媒体工具箱。