资讯动态

HTML5自测五题:视频播放、Canvas动画与移动端适配避坑指南

发布时间:2026/8/29 23:29:24 来源:尧图企业网站定制
我最近在梳理自己的HTML5笔记发现有些知识点当时背得滚瓜烂熟真到项目里碰到却还是会翻车。索性给自己出了一套自测题按真实场景来问不搞死记硬背。这是第二组一共五道题覆盖视频播放、Canvas动画、移动端适配、语义化标签和常用API。每一题背后都对应一个我实际踩过、后来花时间填平的坑也结合了社区里大家经常讨论的热点比如不同浏览器对HTML5播放器的支持差异、爱❤️烟花特效的实现思路、以及响应式页面设计里的那些隐藏细节。不管你是刚入门的前端新手还是写了几年业务代码的同学这组题应该都能让你找到点东西。我不会只给答案还会说清楚为什么是这个答案以及我在项目里是怎么处理的。1. 第一题移动端视频接入时浏览器兼容坑到底藏在哪1.1 常见答案为什么不够用先看一下这个场景页面要放一个视频你用原生video标签给个src指向MP4PC浏览器一开就能播。但同一个页面放到iOS微信里视频点击播放后直接全屏弹出来放到安卓手机Chrome上又可能要手动点两下才有画面。这是一道很经典的考察题我见过很多人的回答是“哪有什么兼容性直接video就能播啊”其实这句话只对了一半。HTML5视频播放器的兼容性问题本质上不是“标签不识别”而是“编码格式、解码能力、播放策略”三个层面在浏览器之间存在巨大差异。标签的支持是共识但标签里的具体内容能不能播、怎么播得看浏览器内核。1.2 不同浏览器对视频格式支持的真实差异先说编码格式。主流浏览器对HTML5video能播什么格式一直是按内核走的。你可以把视频编码理解成压缩包格式浏览器内置了解压器才能解开没有解压器就要靠系统或插件。Chrome、Firefox、Edge这条线支持WebM/VP9也支持MP4/H.264Safari以及旧版本iOS的内置WebView则对WebM支持一直很弱很长一段时间只稳定支持H.264/MP4。所以生产环境最稳妥的方案是准备MP4/H.264如果追求高压缩比还可以用H.265但H.265在浏览器里的授权和硬件解码支持又是一个复杂话题我一般不建议在常规网页里直接用。实际项目里更头疼的是移动端策略。iOS Safari如果视频没有设置playsinline旧版本还需要webkit-playsinline点击播放后默认会全屏铺满安卓的Chrome则对自动播放设置了严格限制没声音的视频可以自动播但有声音且不是用户主动点击时不允许自动播放。这是我第一次做落地页时翻车最惨的地方——视频明明加载了就是不出画面查了半天才发现是自动播放策略拦截了。一个比较稳的接入模板我贴在下面适合移动端H5场景video idheroVideo srchttps://example.com/video/hero.mp4 playsinline webkit-playsinline preloadmetadata muted loop posterposter.jpg /video属性不少每一个都有原因muted是让自动播放策略放行playsinline是让iOS在内联区域播放而不是全屏弹出preloadmetadata避免首屏加载太大视频数据loop适合背景视频。如果视频需要用户手动点击播放并且带声音那就不用autoplay让用户点击按钮后再调用video.play()。1.3 怎么用canPlayType判断当前环境能不能播有些时候你需要根据浏览器能力动态切换视频源这就得用上canPlayType。这个方法返回三个值probably、maybe、空字符串分别代表很可能支持、可能支持、不支持。注意它永远不返回true所以判断条件要反过来写。const video document.createElement(video); function canPlayMP4() { return video.canPlayType(video/mp4; codecsavc1.42E01E, mp4a.40.2) ! ; } function canPlayWebM() { return video.canPlayType(video/webm; codecsvp9, opus) ! ; }判断结果可以用来切换source标签或动态换src。不过我个人经验是多数业务场景直接准备一个H.264的MP4就够了WebM可以作为增益项不必为兼容性消耗太多开发时间。真正要重点关注的是移动端的全屏、自动播放、数据流量、以及视频在部分安卓低端机的硬件解码问题。真出现黑屏但有声音的情况多半是解码器问题可以考虑用.mp4的H.264 baseline profile牺牲一点画质换取兼容性。1.4 避坑心得这里有一个细节是我后来查文档才知道的iOS Safari会对preload属性有一定的“自主裁决”能力也就是说即使你写了preloadautoiOS可能仍然只在滑动到视口附近时才真正开始下载视频。所以如果你的视频是首屏背景不要依赖preload去提前加载建议用JS主动拉取一下或者干脆用poster先顶着画面。另一个坑是视频元素如果设置了loop在部分安卓WebView上循环播放会有明显卡顿因为每次循环结束都会走一次seek处理不好就会闪一下。这个问题没有特别优雅的解法我一般是在timeupdate或ended事件里手动重置当前时间并重新播放比原生loop更可控。2. 第二题用Canvas写一场“爱心烟花”要抓住哪些核心逻辑2.1 能搜到的烟花代码为什么拿过来经常跑不起来网上搜“html5爱心烟花特效代码”能搜到一堆画得不错的Demo但复制到自己的项目里经常出现几个问题要么Canvas整个画面糊成一片要么粒子数量一多就掉帧要么在手机上一碰就白屏。这道题如果你只停留在“会用Canvas画圆”的程度肯定答不到点子上。烟花特效的核心说穿了就是一个粒子系统粒子有位置、速度、加速度、寿命每一帧做四件事——更新粒子的当前位置和速度、把上一帧画面清掉或做拖尾效果、绘制粒子、检测粒子寿命并移除。循环驱动靠requestAnimationFrame而不是setInterval因为前者跟屏幕刷新率同步而且页面切到后台时会自动暂停省电。2.2 爱心轨迹的数学方程和坐标换算要做爱心形状的烟花第一步是让炸开的粒子落在爱心轨迹上。一个简洁的爱心参数方程是这样的x(t) 16 * sin^3(t) y(t) 13 * cos(t) - 5 * cos(2t) - 2 * cos(3t) - cos(4t)其中t从 0 到 2π。计算出来的 x 和 y 在 -16 到 16 左右范围比较小所以需要换算到Canvas的实际坐标。我通常的做法是算出所有目标点后乘一个缩放系数s再加到烟花爆炸中心(cx, cy)上。这里有个细节Canvas的y轴是向下为正而数学坐标的y轴向上为正所以要么在算式里把y取反要么在绘制时直接用cy - point.y。这个细节很容易被忽略但漏掉之后爱心会上下颠倒。2.3 一套可复用的粒子动画框架我贴一个精简版的粒子烟花框架适合做背景特效或节日贺卡。逻辑不复杂但包含了一个粒子系统最基本的骨架class Particle { constructor(x, y, targetX, targetY) { this.x x; this.y y; this.targetX targetX; this.targetY targetY; // 这里直接用简单的随机错位起步后续可以扩展物理模型 this.vx (Math.random() - 0.5) * 2; this.vy (Math.random() - 0.5) * 2; this.life 60; this.maxLife 60; this.color hsl(${Math.floor(Math.random() * 360)}, 80%, 65%); } update() { // 向目标点靠拢同时加入一点缓动效果 this.x (this.targetX - this.x) * 0.06 this.vx; this.y (this.targetY - this.y) * 0.06 this.vy; this.life--; } draw(ctx) { const alpha Math.max(0, this.life / this.maxLife); ctx.globalAlpha alpha; ctx.fillStyle this.color; ctx.beginPath(); ctx.arc(this.x, this.y, 2, 0, Math.PI * 2); ctx.fill(); } }初始化爆炸时先计算一组爱心轨迹点然后为每个点生成一个粒子。粒子从爆炸中心出发每一帧向目标点靠近同时加一点随机扰动看起来就像散开的火花汇聚成爱心。这个写法比“每个粒子沿直线飞出去”更接近烟花炸开的张力效果。2.4 性能优化画布模糊和掉帧的根源很多跑不起来的问题根源不是算法而是Canvas的尺寸没有适配设备像素比。Canvas的width、height属性是画布的实际像素尺寸而CSS里设置的尺寸是渲染宽度。如果两者不一致尤其是高分辨率屏幕Retina下浏览器会做插值缩放画面就糊。解决办法是读window.devicePixelRatio把画布像素尺寸乘上去const dpr window.devicePixelRatio || 1; canvas.width cssWidth * dpr; canvas.height cssHeight * dpr; ctx.scale(dpr, dpr);粒子数量也是性能关键。烟花特效粒子数量一多每一帧都要对每个粒子做一次更新和绘制设备旧了就会掉帧。我一般控制单屏粒子总量在800到1500之间超过这个量就对粒子做合并或降低绘制半径。另外一个容易被忽略的点是ctx.globalAlpha的频繁设置如果粒子颜色相同时可以用整批统一透明度绘制来减少状态切换开销但为了每个粒子渐隐效果大多数情况下只能逐个设置。这个知识点除了做烟花做“html5圣诞贺卡”里的雪花、彩灯、飘带动画都是同一套逻辑理解了粒子生命周期往哪个方向都能改。3. 第三题设计稿宽750px移动端页面为什么还是崩了3.1 viewport没写对整个适配都白搭设计稿750px很常见很多新手直接拿设计稿的像素值往CSS里写写完在电脑浏览器上把窗口缩到手机宽度看着也还行。一旦发到真机上字变得特别小或者整个页面像被缩小了、两边留白一大片。问题大概率出在viewportmeta标签。移动端浏览器默认的布局视口宽度大约是980px不同浏览器略有差异如果不设置viewport页面会先按980px的布局视口排版然后再缩放到手机屏幕宽度。所以设计稿750px的页面等于是被“缩小”塞进手机屏幕里了。正确做法是加上这行meta nameviewport contentwidthdevice-width, initial-scale1.0, viewport-fitcover /widthdevice-width让布局视口等于设备宽度initial-scale1.0禁止初始缩放viewport-fitcover处理刘海屏之类的安全区问题。如果漏掉这个标签后面所有rem、vw适配都会白搭。3.2 rem和vw的取舍以及一个实用方案移动端适配方案里最常用的是rem和vw。rem的原理是把根元素字号设为一个基准值所有尺寸用rem写页面根据屏幕宽度动态调整根字号实现等比缩放。vw更直接1vw等于视口宽度的1%不用写JS就能自适应。不过vw单位有精度问题某些老旧安卓WebView对小数vw渲染不够稳定而且字体用rem比vw更可控。我自己在项目里的习惯是布局距离用rem或者vw文字大小用rem边框线用px然后给根元素设置一个函数来动态计算font-size(function () { const baseWidth 750; // 设计稿宽度 const docEl document.documentElement; function setRemUnit() { const clientWidth docEl.clientWidth || 375; docEl.style.fontSize (clientWidth / baseWidth) * 100 px; } setRemUnit(); window.addEventListener(resize, setRemUnit); })();设计稿里一个宽度为375px的块写3.75rem字体大小24px写0.24rem。这样不同屏幕会等比缩放。需要注意根字号有一个适用上限超大屏幕上如果还等比放大下去布局会变得夸张建议设一个最大值比如超过某个屏幕宽度后固定用500px的基准来算。3.3 触摸事件与300ms延迟的残留问题移动端页面除了布局交互上也有一个经典坑click事件的300ms延迟。以前移动端为了区分单击和双击缩放会在点击后等待300ms才触发click导致页面“点一下没反应”。现代浏览器在设置了widthdevice-width的viewport后通常已经去掉了这个延迟但老版本WebView和部分安卓环境仍然存在。所以做移动端交互时能用touchstart或 Pointer Event 的优先用它们来提升响应速度。不过要注意touchstart的触发条件宽松用户手指一放上去就触发不是松开时触发这可能导致“滑一下页面就触发了按钮”的误操作。我通常的处理是按钮点击用click加节流或者用 Pointer Events 的pointerup必要场景再用触摸事件做优化。还有一个容易被忽略的细节就是触摸事件的passive属性。从HTML5新增的事件监听选项开始浏览器默认把很多触摸事件当作passive: true处理也就是说在touchstart或touchmove里调用preventDefault()是无效的浏览器会报Warning。如果确实需要阻止默认滚动得显式写{ passive: false }。这个坑在实现自定义弹层、拖拽的时候特别常见。这块内容经常被归到“html5网页设计”的讨论里但严格说它已经超出了HTML5标签范围属于移动端Web开发的基础。可不管归到哪一类写页面时绕不开。4. 第四题section和div看起来一样语义化真的有用吗4.1 视觉上没区别但页面结构信息完全不一样问题来了section和div在外观上没有任何视觉差异替换之后页面效果完全一样那为什么还推荐用语义标签答案在于浏览器、搜索引擎和辅助技术读取页面时看的不是视觉样式而是DOM的结构语义。div在语义上是“无意义的分组容器”它只告诉浏览器这是一个块级盒子不提供任何内容主题信息。而section表示“一个主题性的内容分组”要求内部通常有一个标题。这个差异对于屏幕阅读器用户来说非常关键使用辅助技术的用户可以在页面里快速跳转导航靠的就是页面里的语义结构和Landmark标记而不是靠听文字读一遍。4.2 用Landmark理论对照检查你的页面HTML5引入的语义化标签其实对应了一套“地标”Landmark体系header对应bannernav对应navigationmain对应mainfooter对应contentinfo。屏幕阅读器用户可以通过地标快速跳到页面主要区域就像视觉用户一眼能找到导航栏和正文区域一样。你可以打开浏览器开发者工具或者装上无障碍检查插件把页面里的标签列一遍问自己几个问题页面是否只有一个main标签如果有多个属于结构错误。导航是否放在nav里不是所有链接组都需要用nav主导航和页脚导航需要用。section内部是否都有标题如果一个section没有标题它的主题就不明确应该改用div。article是否真正代表一个独立、可单独分发的内容块博客文章、新闻条目适合用article产品卡片也适用。这些检查做下来即使页面视觉完全不变“结构图像”也已经完全不同了。4.3 标题层级和SEO的微妙关系还有一个很多人误会的点语义化标签对SEO不是“用了就立刻排名上升”而是帮助搜索引擎更好地理解页面结构从而间接影响内容在搜索结果里的展示。尤其是标题层级很多页面喜欢从h1直接跳到h4中间全是空的搜索爬虫在解析内容时就会搞不清你的信息层级。建议每页只有一个h1代表核心主题然后按h2、h3逐级往下中间不要跳级。如果想调整标题的视觉大小请用CSS去改而不要为了视觉大小去错用标签层级。我自己遇到过的一个反例是一个活动页面为了视觉效果把真正的页面主标题藏在一个不可见的p里视觉上的标题是h3最后导致页面在搜索结果里展示的标题内容完全不对。后来改回h1去匹配视觉标题第二天搜索抓取内容就正常了。所以不要觉得“反正是程序员看的无所谓”搜索和辅助技术都是页面的真实用户而且是很重要的用户。5. 第五题不引第三方库怎么给页面加全屏和复制功能5.1 Fullscreen API的两个硬性前提第五题是我自己特别喜欢问的现在需要做一个按钮点击后页面全屏再点一下退出全屏不准引库怎么做答案是用Fullscreen API。这个API挺简单核心就两个方法// 进入全屏 const el document.documentElement; el.requestFullscreen el.requestFullscreen(); // 退出全屏 document.exitFullscreen document.exitFullscreen(); // 监听全屏状态变化 document.addEventListener(fullscreenchange, function () { if (document.fullscreenElement) { console.log(进入了全屏); } else { console.log(退出了全屏); } });但这里有一个容易被忽略的硬性前提requestFullscreen()必须在用户手势如点击事件回调中调用不能在页面加载完成后自动调用否则会被浏览器拒绝。这和播放器自动播放、Notification权限请求是同一类限制目的是防止网站骚扰用户。另外旧版iOS Safari上这个API的支持比较特殊活动页面在iOS上做全屏还是会优先用video的webkitEnterFullscreen但如果是普通页面整体全屏iOS Safari至今仍不提供完整的requestFullscreen支持这是一个实际项目里常见的能力落差。5.2 Clipboard API复制文本比想象中简单另一件高频需求是复制文本老办法是动用隐藏iframe加document.execCommand(copy)写得麻烦还容易碰坑。现代浏览器可以使用Clipboard API写起来简洁得多async function copyText(text) { try { await navigator.clipboard.writeText(text); console.log(复制成功); } catch (err) { console.error(复制失败, err); } }但这个API同样有限制必须在安全上下文HTTPS或localhost下使用而且在非用户手势的异步回调里调用部分浏览器也会拦截。如果项目需要兼容比较老的环境还是得保留基于execCommand的降级方案。我一般写成优先使用Clipboard API如果返回undefined或抛错再回退到隐藏textarea加execCommand的方案。这个降级路径不到30行能覆盖绝大多数业务场景。5.3 权限机制与用户手势的绑定逻辑第五题真正考察的点其实是“你是否清楚浏览器为什么要把能力和用户操作绑定在一起”。HTML5新增的很多API比如Notification、Geolocation、Fullscreen、Clipboard都不再是网页一加载就能随便用的而是要求用户在页面里做了一次交互后再申请权限。这不是浏览器在为难开发者而是为了避免页面在后台偷偷读取定位、弹通知、复制剪贴板之类的滥用行为。所以写这类功能时的习惯应该是把权限请求放在用户真正会点击的那个按钮回调里而不是放在页面初始化代码里。如果你在onload里就请求定位权限用户会觉得很莫名其妙授权率也低但如果你在一个明确的业务动作里请求用户就能理解为什么要授权。这也是我在实际项目里从“功能能跑”到“体验合理”之间踩过的一段弯路。最后再多说一点我的测试习惯这五道题我一般每隔几个月就会拿出来自测一遍不是为了考试而是因为浏览器的更新迭代太快很多兼容性结论半年就会变一次。比如我之前一直认为Safari不支持WebM但最新的桌面版Safari已经开始提供有限支持再比如移动端Chrome对自动播放的策略不同版本也调整过好几轮。所以我的习惯是每个季度找一台上古安卓机加上最新版Chrome把视频播放、Canvas特效、移动端布局、权限API这些核心场景跑一遍记录和更新一版自己的兼容性笔记。这个方法听起来很土但确实能帮我提前发现很多“文档里没有、线上才爆”的问题。做HTML5开发很多东西不是在书面规范里学到的而是在不同浏览器和设备上试出来的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价