WebRTC 与动画体系

共 73 题
📑 题目列表 73 题
#
★★★

1. Motion One(基于 Web Animations API)

Motion One 作为基于 Web Animations API 的动画库有哪些特点与工程价值?

  • 基于 WAAPI 的实现架构
  • 与 GSAP 等框架型动画库的对比
  • 体积、性能与用法

Motion One(@motionone/dom)是建立在 Web Animations API(WAAPI)之上的轻量动画库:核心 animate() 直接驱动 Element.animate()(浏览器合成器线程执行动画),自带 easing(spring 弹簧、滑环 easing)、keyframes、时间线编排(timeline)与滚动/视口驱动的 motion() 高级功能。工程价值:第一,体积——比 GSAP 小一个量级(~10KB gzip 内,GSAP 核心 ~23KB+,完整插件更多),按需引入(tree-shaking 友好);第二,性能——动画在合成器线程运行(transform/opacity 走合成,主线程不参与每帧计算),滚动驱动动画(scroll progress)与视口触发减少 JS 介入;第三,API——声明式参数(duration/easing/repeat/direction)与 WAAPI 对齐,容易与 CSS 动画心智模型衔接;React 生态有配套(motion 包的 useAnimate 等,与 Framer Motion 同门);第四,互操作——WAAPI 的事件(finish/cancel)与 getAnimations() 查询可混用,便于与其他动画方案共存。边界与取舍:能力上限低于 GSAP(无 ScrollTrigger 的完整生态、无 Draggable 等专有插件),复杂编排(时间线嵌套、动态回调)需更多手写;兼容性依赖 WAAPI(Safari 已支持 Element.animate,覆盖良好);自定义 easing 的数学表达力不如 GSAP 的贝塞尔自由曲线(但也有 spring 等)。选型:轻量需求(微交互、列表过渡、滚动动效)Motion One 更优;重量级营销/复杂时间线 GSAP 生态更全。

考察动画库的架构取向:WAAPI 之上封装 = 小体积 + 合成器性能 + 声明式,回答需对比 GSAP 给出"轻量 vs 完整"的选型判断。

#
★★★

2. GSAP 的 gsap.context() 与 React 生命周期清理

GSAP 的 gsap.context() 如何配合 React 生命周期做动画清理?

  • gsap.context() 的作用域收集机制
  • React 严格模式与卸载清理
  • 与 useEffect/useLayoutEffect 的配合

gsap.context()(GSAP 3.11+)创建动画作用域:context 内创建的动画(gsap.to/from/fromTo、timeline、ScrollTrigger 等)被自动收集,context.revert()(或 context.revert() 的回调)可统一撤销/清理——包括恢复 CSS 内联样式与移除 ScrollTrigger。React 配合要点:第一,useEffect/useLayoutEffect 中创建 context,cleanup 中调用 ctx.revert()——卸载时动画、事件监听、ScrollTrigger 全部清除,防止内存泄漏与"卸载后动画继续操作已移除 DOM"的报错;第二,严格模式(StrictMode)双调用——开发环境 effect 会 mount→unmount→mount,若无清理会出现动画重复/残留,context.revert() 保证幂等;第三,context 作用域传 DOM——ctx.add(() => {...}) 或把元素作为函数参数,让 GSAP 只操作该组件子树(配合选择器作用域 ctx 的 scope 参数避免跨组件误伤);第四,React 18 并发——useLayoutEffect(同步于 DOM 变更)初始化动画减少闪烁,清理逻辑不变;第五,依赖更新——依赖项变化时先 revert 旧 context 再重建(避免旧动画与状态错位)。工程注意:不清理的常见坑(ScrollTrigger 注册残留导致滚动错乱、定时动画泄漏、className 样式残留),用 context 统一治理;动画实例也可用 gsap.killTweensOf 精确清理。

考察 GSAP 与 React 生命周期的整合:context 收集 + revert 清理、StrictMode 幂等、useLayoutEffect 时机,回答需落到"创建与清理成对"的工程习惯。

#
★★★

3. Lottie 在 React Native 的性能边界与替代方案(Skia、Rive)

Lottie 在 React Native 中的性能边界是什么?Skia、Rive 等替代方案如何取舍?

  • Lottie 在 RN 的渲染实现与性能瓶颈
  • Skia(React Native Skia)与 Rive 的能力
  • 按动效复杂度选型

Lottie(lottie-react-native)把 AE 导出的 JSON 动画在 RN 内用原生渲染器(iOS 用 CALayer 重绘、Android 用自绘)播放:性能边界——第一,节点数敏感:复杂动画(数千图层/路径)在低端机掉帧,因为每帧都要走原生层树更新与绘制(非 GPU 合成友好路径);第二,内存:大 JSON(含位图资源)与频繁实例化内存开销大(列表滚动中多实例并发播放卡顿);第三,特性支持:表达式、某些效果(部分遮罩/混合模式)渲染器支持不完全,需要预先验证;第四,更新节奏:新 AE 特性(Lottie 4.x 格式)在 RN 端滞后。替代方案:React Native Skia(@shopify/react-native-skia)——把 Skia 图形引擎暴露为声明式组件,动画由 JS/Reanimated 驱动(逐帧状态 → GPU 绘制),性能上限高(复杂图形、自定义着色器、物理效果),但需要"图形 + 动画逻辑"自建(JSON 动效需转换/重写,skottie 可播放 Lottie JSON,支持度中等);Rive——自有 .riv 格式(编辑器设计),运行时(RN 支持官方 SDK)在原生端用其自研渲染器(图形 + 骨骼 + 状态机),动画逻辑(触发、状态切换)进文件、播放性能好、内存占用低,是"复杂交互动效"的现代选择,代价是设计工具链切换(AE 生态迁 Rive)。取舍:简单位移动画(透明度/缩放/位移)甚至用 Reanimated 直接做(零依赖);经典 AE 动效(插画风格)Lottie 合适(验证节点数);高性能/可交互动效(游戏化 UI、角色动画)Skia/Rive。监控与预检:RN 端做真机帧率与内存基线测试。

考察跨端动效引擎的工程选型:Lottie 的节点/内存边界、Skia 的强与成本、Rive 的状态机优势,回答需按动效形态给出分级方案。

#
★★★

4. Framer Motion(layoutId、AnimatePresence)

Framer Motion 的 layoutId 与 AnimatePresence 如何工作?有什么工程价值?

  • layoutId 的共享布局过渡原理
  • AnimatePresence 的进出场动画
  • 性能与边界

Framer Motion(motion 库)提供声明式 React 动画:layoutId——给不同位置的组件标记同一 layoutId,框架在组件"消失/出现/移动"间做共享布局过渡(如列表项展开为详情页:列表中的缩略图与详情页大图共享 layoutId,位置/尺寸/圆角自动补间动画),其原理是把源元素与目标元素的项目符号按投影坐标映射,用 transform 模拟过渡(必要时 clone 元素桥接);AnimatePresence——管理 React 树的卸载动画:被包裹的组件在移除时先播放 exit 动画再真正卸载(默认 React 卸载即消失),支持 initial/animate/exit 三态与 mode(wait/sync/popLayout)控制编排(如弹窗列表的进出场交错)。工程价值:第一,页面/列表过渡免手写测量与 FLIP 代码(layoutId 自动完成);第二,UI 状态切换的编排(进出场、列表重排、弹窗层级)声明化;第三,与 React 渲染模型对齐(基于组件树而非全局)。边界与注意:layout 动画本质是"投影 + transform",内容变化大时(字体换行、图片加载)投影不准需用 layout 动画的约束(layout="position")或关闭;大量元素 layoutId 会显著增加每帧布局计算(性能敏感列表慎用/用 LayoutGroup 控制范围);AnimatePresence 需 key 稳定、popLayout 用于堆叠布局;React 18 并发渲染下动画与中断行为需验证。

考察声明式动画框架的核心机制:layoutId 的投影过渡、AnimatePresence 的卸载编排,回答需覆盖用法、原理与性能边界。

#
★★★

5. GSAP(Timeline、ScrollTrigger)的动画编排能力

GSAP 的 Timeline 与 ScrollTrigger 有哪些动画编排能力?

  • Timeline 的时间轴控制(position/标签/回调)
  • ScrollTrigger 的滚动驱动机制
  • 复杂页面动效的编排模式

GSAP 的核心是 Timeline:在一条时间轴上编排多个 tweens(gsap.to 等),通过 position 参数(数字、+=、标签)精确控制并发/串行/重叠(如动画 B 在 A 播放 50% 时开始),提供 play/pause/seek/progress、标签(addLabel)、回调(onStart/onComplete/onUpdate)与时间缩放(timeScale);嵌套 Timeline 组织复杂序列。ScrollTrigger 是滚动编排插件:以滚动位置驱动动画(scrub 让进度绑定滚动、toggle 触发进出场、pin 固定元素、start/end 定义触发区间、trigger 元素与视口对齐),可配合时间线(动画进度 = 滚动进度)实现"滚动叙事"页面(视差、章节固定、进度条);还支持容器滚动(scroller 参数)、批处理(batch)与动态 refresh(图片加载后重算)。编排模式:第一,阶段化——每个章节一条 timeline,ScrollTrigger 按区间触发;第二,时间线组合——入场(load 动画)+ 滚动触发 + 循环背景动画分层;第三,响应式——matchMedia 按断点启用不同编排;第四,可访问性——prefers-reduced-motion 时跳过/缩短动画(ScrollTrigger 提供 reduceMotion 配置)。工程注意:pin 会产生 spacing 补偿(修改布局),容器 overflow 与性能(大量 pin 的滚动开销),destroy/refresh 清理;GSAP 的插件体系(ScrollTrigger/Draggable/MotionPathPlugin)是完整动效编排的行业标准。

考察 GSAP 编排能力的层次:Timeline 的时间控制、ScrollTrigger 的滚动驱动、组合模式与响应式处理,回答需体现"时间 + 滚动"双轴编排的方法论。

#
★★★

6. position: sticky 边界 在现代组件库的工程价值

position: sticky 有哪些边界?在现代组件库中有何工程价值?

  • sticky 的生效条件与滚动容器
  • 边界(父容器、overflow、table)
  • 组件库中的封装实践

position: sticky 是"相对 + fixed"的混合:元素在滚动容器内先随文档流滚动,到达阈值(top/left 等)后固定在视口(相对最近滚动祖先),随父容器边界离开时释放。边界:第一,滚动容器——sticky 相对"最近的可滚动祖先"生效,父级/祖先 overflow: hidden/auto/scroll 会改变滚动上下文(sticky 可能失效或固定到错误的容器),祖先 overflow: visible 才以视口为滚动体;第二,父容器约束——sticky 元素只能在父容器范围内"固定",父容器结束即释放(高度不够则看不到固定效果);第三,table 兼容——thead/tbody 的 sticky 支持(Chrome/Firefox 支持 thead th sticky,但 border-collapse 布局下行为怪异),跨浏览器需验证;第四,与 transform 祖先——祖先有 transform/filter/will-change 会创建包含块,影响 fixed 语义(sticky 仍相对滚动容器,但 fixed 子元素行为变化);第五,性能——sticky 由合成器处理滚动贴附,通常性能好,但大量 sticky + 复杂容器有重排成本。组件库工程价值:表格表头吸顶(virtualized table 的表头)、吸顶导航/工具栏、筛选栏、目录高亮(结合 IntersectionObserver 或 ScrollTrigger);封装时需处理:滚动容器的注入(context 传递 scroller)、嵌套 sticky 的层级(z-index 策略)、降级(不支持时回退 fixed + JS 测量)与 IntersectionObserver 的联动(目录激活状态)。

考察 sticky 的工程边界与组件化:生效条件(滚动祖先/父容器)、table 与 transform 陷阱、组件库中的容器注入与降级,回答需给出可复用的封装要点。

#
★★★

7. Canvas 2D Path2D/OffscreenCanvas 在复杂路径复用与 Worker 离屏的工程价值

Canvas 2D 的 Path2D 与 OffscreenCanvas 在复杂路径复用与 Worker 离屏渲染中有何工程价值?

  • Path2D 的路径对象复用
  • OffscreenCanvas 的 Worker 渲染
  • 组合使用的性能模式

Path2D 把路径(moveTo/lineTo/arc/rect、贝塞尔)封装为可复用对象:第一,复用——同一复杂路径(地图边界、图表曲线、图标轮廓)绘制多次(多个画布、多帧、多实例)时,路径构建只做一次,fill/stroke 直接引用(减少每帧路径构建的 CPU 成本);第二,组合——addPath 合并子路径、clip 用 Path2D 做复杂裁剪、isPointInPath 命中检测(拾取)直接对路径对象做点包含判断(图表点击、地图标注交互);第三,与 SVG path 数据互通(new Path2D(svgPathString))——把设计师的 SVG 路径直接用于 Canvas 绘制。OffscreenCanvas 提供"离屏画布 + 渲染上下文":第一,Worker 渲染——在 Worker 中创建 OffscreenCanvas 并 transferControlToOffscreen 移交控制权,主线程只保留展示用 canvas(由浏览器合成),复杂渲染(粒子、地图瓦片、图表重绘)在主线程零阻塞;第二,预渲染——离屏 canvas 先在内存绘制静态层(背景、网格、标注),再 drawImage 到主画布,避免每帧重绘;第三,ImageBitmap 配合——渲染结果转 ImageBitmap 后高效上屏。组合模式:Worker 中用 Path2D 构建并缓存复杂路径 + OffscreenCanvas 离屏绘制 + ImageBitmap 上屏——大数据可视化/游戏地图的黄金组合;边界:Worker 内 OffscreenCanvas 的 context 能力(2D/WebGL)随浏览器而异,纹理/字体(Worker 无 DOM 字体测量)需注意。

考察 Canvas 渲染优化的两个关键 API:Path2D 的路径资产化(构建一次多次绘制 + 命中检测)、OffscreenCanvas 的线程化(Worker 渲染 + 离屏预渲染),回答需给出组合式优化架构。

#
★★★

8. WebGPU 在 3D 场景渲染与 Three.js WebGPURenderer 的应用

WebGPU 在 3D 场景渲染中有哪些应用?Three.js 的 WebGPURenderer 如何工作?

  • WebGPU 的现代 GPU 抽象(管线、资源绑定)
  • Three.js WebGPURenderer 的架构
  • 工程迁移与边界

WebGPU 是新一代浏览器 GPU API:以现代图形 API(Vulkan/Metal/DX12)为蓝本——GPUDevice 管理设备、RenderPipeline/ComputePipeline 显式声明管线状态(着色器(WGSL)、顶点布局、混合)、BindGroup 统一资源绑定(纹理/缓冲/采样器)、RenderPass/ComputePass 录制指令、Queue.submit 提交执行;相对 WebGL 提供:计算着色器(GPGPU)、更细粒度资源控制(buffer usage、对齐)、无状态绑定切换(减少驱动开销)、显式同步(无隐式 GL 状态机)。Three.js 的 WebGPURenderer(three 0.17x+ 的 WebGPURenderer 类,配合 TSL 着色器)把 Three 的场景图(Scene/Camera/Mesh/Material/Light)翻译到 WebGPU 管线:材质用 Node 着色器系统(TSL)描述、自动生成 WGSL、管理 BindGroup 布局与缓存、支持计算管线(粒子、后处理);同一套场景 API,renderer 从 WebGLRenderer 换为 WebGPURenderer 即可迁移大部分场景(自定义 ShaderMaterial 需改写为 TSL/WGSL)。工程应用:3D 场景(模型查看器、数据可视化、游戏)、高性能粒子、计算着色器驱动的物理/流体模拟、后处理链。边界:浏览器支持(Chrome/Edge 稳定,Safari 26+ 默认、Firefox 推进中)需特性检测 + WebGL 降级;WGSL 生态(调试工具、编辑器支持)仍在成熟;移动端功耗/兼容需实测。

考察 WebGPU 时代的 3D 渲染工程:管线与绑定模型、Three.js 的 TSL/WGSL 翻译架构、迁移路径与降级,回答需体现"现代 GPU 抽象"与"框架封装"的层次。

#
★★★

9. requestAnimationFrame 驱动的 Canvas 动画与粒子系统

如何用 requestAnimationFrame 驱动 Canvas 动画与粒子系统?有哪些工程要点?

  • rAF 的帧循环语义(vs setTimeout)
  • 帧率无关的时间步进(delta)
  • 粒子系统的更新/渲染优化

requestAnimationFrame(rAF)让回调在每次屏幕刷新(VSync,通常 60Hz/120Hz)前执行,浏览器自动节流(后台标签页暂停)、与绘制同步(避免撕裂)、一帧内合并多次调用——相比 setTimeout 的固定间隔更平滑省电,是 Canvas 动画的标准驱动。帧循环工程要点:第一,delta 时间步进——用 performance.now() 计算两帧间隔 delta,物理量(位移/速度)乘以 delta 实现帧率无关(60Hz 与 120Hz 屏幕动画速度一致),并用 clamp 防止切后台回前台后的超大 delta 跳变;第二,rAF 循环结构——requestAnimationFrame(loop) 在 loop 内部再调度自身,动画结束(所有粒子死亡)时停止调度省电;第三,状态与渲染分离——更新(粒子位置/速度/碰撞)与渲染(clear → draw)分函数,更新可下沉 Worker(OffscreenCanvas);粒子系统优化:池化(粒子对象复用,避免 GC)、批量绘制(同属性粒子用同一路径/同 fillStyle 分组,减少状态切换;或 ImageBitmap 精灵绘制)、fixed timestep(固定步长物理 + 插值渲染,防抖动)、空间网格减少碰撞检测量、透明度/数量与 DPR 适配;第四,性能预算——监控每帧耗时(DevTools Performance),超过 16ms 则降粒子数或降级效果。

考察 Canvas 动画系统的核心工程:rAF 语义、delta 帧率无关、粒子池化与批量渲染,回答需给出"更新/渲染分离 + 预算控制"的完整实践。

#
★★★

10. 后期处理、阴影、3D 模型加载(GLTF/GLB)

Three.js 中后期处理、阴影与 3D 模型加载(GLTF/GLB)如何实现?工程要点是什么?

  • EffectComposer 后期处理链
  • 阴影(shadow map 配置与成本)
  • GLTF/GLB 加载与优化

后期处理——Three.js 用 EffectComposer + 渲染 Pass 链实现:EffectComposer 把场景渲染到离屏目标,依次执行 RenderPass(场景)、UnrealBloomPass(泛光)、SSAOPass(环境光遮蔽)、OutputPass(色彩空间输出)等,各 Pass 间经中间纹理传递,最终合成到屏幕;顺序与数量影响性能(每 Pass 一次全屏绘制),移动端需精简 Pass。阴影——基于 Shadow Map:灯光设置 castShadow、物体设置 castShadow/receiveShadow、shadow.mapSize 决定阴影纹理分辨率(512-2048+,与质量/内存权衡)、shadow.camera 近远与范围(过小阴影裁切、过大浪费精度)、PCFSoftShadowMap 等算法平滑边缘;多个灯光同时开阴影成本叠加(每灯独立 shadow map 渲染),大场景常用"单主光阴影 + 烘焙/假阴影"降级。GLTF/GLB 加载——GLTFLoader 加载 .gltf/.glb(含网格、材质(PBR)、骨骼、动画、场景结构),异步 + 进度回调;优化:Draco 压缩(几何压缩,需 draco decoder)、KHR_mesh_quantization、KTX2/Basis 纹理压缩(GPU 纹理)、Meshopt 压缩;加载策略——预加载进度条、按需加载(LOD/视野触发)、模型缓存(浏览器缓存 + 离线缓存)、实例化(InstancedMesh 合并大量重复模型)。工程注意:材质粗糙度/金属度贴图的色彩空间(sRGB vs linear)处理、模型单位与坐标约定(upAxis 转换)、合批与 DrawCall 控制。

考察 Three.js 生产渲染链路:后处理 Pass 链与成本、shadow map 的配置权衡、GLTF 加载与 Draco/KTX2 优化,回答需覆盖"效果实现 + 性能治理"两面。

#
★★★

11. RTCPeerConnection 建立连接的完整流程,信令交换、SDP offer/answer 协商与 ICE candidate 收集交换

RTCPeerConnection 建立连接的完整流程是什么?信令、SDP 协商与 ICE 收集如何配合?

  • 信令交换的两端流程(offer/answer)
  • SDP 的内容与协商目的
  • ICE candidate 收集与连接建立

WebRTC 连接建立(以 A 呼叫 B 为例):第一,创建 RTCPeerConnection(配置 ICE servers:STUN/TURN),A 调用 createOffer() 生成 offer SDP(描述 A 的媒体能力:编解码、方向、SSRC、候选占位),setLocalDescription(offer) 后经信令通道(WebSocket/HTTP,WebRTC 不自带信令)发给 B;第二,B setRemoteDescription(offer)、createAnswer() 生成 answer SDP、setLocalDescription(answer) 回发 A,A setRemoteDescription(answer)——SDP 协商完成(双方就编解码(H.264/VP8/VP9/AV1/Opus)、参数、方向达成一致);第三,ICE——双方同时开始候选收集(host(本机)、srflx(STUN 反射)、relay(TURN 中继)),通过 onicecandidate 把候选(IP/端口/协议)经信令交换给对方(Trickle ICE 边收集边发,不等待全部),双方执行连通性检查(STUN binding,按候选对优先级探测)选出可用路径,最终连接状态 ICE 完成(connected)并开始媒体传输(SRTP)。完整流程要点:onnegotiationneeded 事件驱动 offer 创建;重协商(新增流、改分辨率)再次走 offer/answer;ICE restart 处理网络切换;错误处理(无可用候选、DTLS 失败);最佳实践——信令带事务 ID 防乱序、ICE 超时回退 TURN、mDNS 候选(隐私)、Bundle(单传输通道承载多流)。

考察 WebRTC 建连全流程的精确掌握:offer/answer 的 SDP 协商语义、ICE 候选的收集/交换/连通性检查、信令通道的外置,回答需按时间线组织并点明关键事件与错误面。

#
★★★

12. 缓动函数(Easing)在动画手感中的应用

缓动函数(Easing)如何影响动画手感?如何选择与实现?

  • 缓动的物理直觉(加速/减速/回弹)
  • 常见缓动族的特性
  • 自定义缓动与手感调优

缓动函数定义动画进度(时间)到数值进度(位移)的映射,决定"物体如何运动",是动画手感的核心:线性(匀速)机械生硬;ease-in(先慢后快)适合"离场/飞出"(重力感);ease-out(先快后慢)适合"入场/落位"(自然的停止感,UI 最常用);ease-in-out(两头慢中间快)适合往返/循环(呼吸感);回弹(bounce/back)带超调与回弹,表达"弹性"与强调(点赞、弹窗)。选型原则:入场用 ease-out(快进慢停,爽快)、出场用 ease-in(加速离开)、状态往返用 ease-in-out;位移/缩放(transform)用标准曲线,颜色/透明度用线性或 ease;时长与距离匹配(大位移需更长时长,velocity 感)。实现方式:CSS——cubic-bezier()(贝塞尔自定义,一次到位)、内置关键字(ease/linear/ease-in-out);JS——GSAP 的 Power.easeOut、spring(物理弹簧:质量/刚度/阻尼参数,Framer Motion 的 spring 与 useSpring、Motion One 的 spring)——弹簧更"活",适合交互反馈;自定义——贝塞尔四点、或数学函数(三次/五次多项式、指数)。手感调优实践:参考系统动效规范(Material 的 easing 曲线)、统一设计令牌(设计系统定义标准 easing/时长)、真机验证(过快难感知、过慢拖沓)、尊重 prefers-reduced-motion(重动效用户禁用/简化)、关键元素(首屏/状态切换)优先保证"到位感"。

考察动画手感的系统认知:缓动族与语义映射、按运动方向选型、CSS/JS/弹簧的实现差异、设计令牌与可访问性,回答需给出"缓动即语义"的设计视角。

#
★★★

13. PIXI.js / Babylon.js / Three.js 在 2D/3D 渲染的工程取舍

PIXI.js、Babylon.js、Three.js 在 2D/3D 渲染上有何工程取舍?

  • 三个库的定位(2D WebGL / 3D 引擎 / 3D 库)
  • 功能与生态对比
  • 按场景选型

三者定位不同:PIXI.js——2D WebGL 渲染器(精灵、粒子、纹理图集、遮罩、滤镜),面向 2D 游戏、交互大屏、富媒体应用,API 贴近 2D 场景图,性能路径(批处理、纹理管理)成熟;Three.js——3D 场景库(Scene/Camera/Mesh/材质/灯光/加载器/后处理),生态最大(加载器、后处理、示例、教程海量),API 易用、社区活跃,是"Web 3D 默认选择";Babylon.js——完整 3D 引擎(渲染器 + 物理 + 粒子 + 场景编辑器(Playground)+ 资产管理 + 高级工具(GUI、音效、WebXR)),面向游戏级复杂应用,工程化(场景序列化、工具链、官方编辑器)更强。工程取舍:第一,维度——纯 2D(游戏 UI、仪表盘动效)PIXI.js 性能与 API 最优(Three.js 也能 2D 但更重);第二,3D 复杂度——展示型 3D(产品展示、数据可视化、模型查看)Three.js 上手快生态足;游戏级/重交互/需要完整工具链(物理、场景编辑、多平台导出)Babylon.js 更全;第三,渲染能力——两者都支持 PBR/后处理/阴影,Babylon 的引擎级特性(高级粒子、材质编辑器)更丰富,Three 靠社区扩展(postprocessing 库、react-three-fiber 等);第四,团队与维护——Three.js 社区最大(招聘/资料容易),Babylon 由微软主导迭代节奏稳健;第五,性能调优——大量对象/合批/实例化需求下,两者都需手控(InstancedMesh/DrawCall),PIXI 的 2D 批处理自动程度高。选型建议:2D → PIXI;3D 展示 → Three.js;3D 复杂应用/游戏 → Babylon.js(或 Three + 生态组合)。

考察 3D/2D 渲染库的选型框架:按维度(2D/3D)、复杂度(展示 vs 游戏)、生态(社区/工具链)三维决策,回答需给出清晰的归属矩阵。

#
★★★

14. Canvas 2D 全局状态(globalCompositeOperation)

Canvas 2D 的全局状态(globalCompositeOperation 等)如何工作?工程上如何管理?

  • 全局状态清单(合成、变换、样式、裁剪)
  • globalCompositeOperation 的混合模式
  • 状态保存/恢复与批量绘制的性能

Canvas 2D 是"状态机"模型:绘图上下文持有一组全局状态——变换(transform/translate/scale/rotate)、样式(fillStyle/strokeStyle/lineWidth/阴影/渐变)、合成(globalAlpha、globalCompositeOperation)、裁剪路径(clip)、文本设置(font/textAlign)等;每次绘制指令使用当前状态。globalCompositeOperation 定义新图形与已有画布像素的混合:source-over(默认覆盖)、lighter/multiply/screen/overlay 等 16+ 模式(相当于 CSS mix-blend-mode),用于光照叠加(lighter 做发光粒子)、蒙版效果(destination-in/out 抠像)、橡皮擦(destination-out)。工程管理要点:第一,save()/restore()——批量设置状态前 save、操作后 restore(栈式),避免状态泄漏到后续绘制;第二,状态切换开销——频繁改状态(如逐元素改 globalCompositeOperation)破坏批处理、增加重绘成本,应按"状态分组绘制"(同合成模式的一起画、再切换);第三,与离屏缓存配合——复杂混合先离屏画好再 drawImage 合成(避免每帧多混合遍);第四,混合模式的浏览器差异(个别模式实现差异)与 GPU 加速情况(合成在合成器/GPU 处理,混合模式多则性能下降);第五,状态重置——新帧开头重置 transform/globalAlpha(ctx.setTransform(1,0,0,1,0,0)),防止上一帧状态残留;调试可用 ctx.getTransform() 检查。

考察 Canvas 状态机的工程化:状态清单与影响、合成模式的应用、save/restore 与分组绘制、离屏缓存,回答需落到"状态即性能"的批量策略。

#
★★★

15. ICE/STUN/TURN 在 NAT 穿透中的各自作用,以及 P2P 直连与 TURN 中继的成本与延迟取舍

ICE/STUN/TURN 在 NAT 穿透中各起什么作用?P2P 直连与 TURN 中继如何取舍?

  • STUN 的地址发现、TURN 的中继转发
  • ICE 的候选收集与排序
  • 直连与中继的成本/延迟/可靠性

NAT 穿透三件套:STUN(Session Traversal Utilities for NAT)——客户端向 STUN 服务器发请求,服务器返回"客户端公网 IP:端口"(NAT 映射地址),客户端用它生成 srflx 候选(反射候选),用于"地址发现"(不转发媒体);TURN(Traversal Using Relays around NAT)——在 NAT 无法直连(对称 NAT、防火墙封锁 UDP)时,客户端与 TURN 服务器建立中继分配(relay 候选),媒体经服务器转发(TURN 服务器承担带宽);ICE(Interactive Connectivity Establishment)——收集全部候选(host/srflx/relay)、经信令交换给对方、成对探测(STUN binding check 按优先级排序:host > srflx > relay)并选最优可用路径,最终"连通性检查"成功即建立 P2P(媒体直连或经 TURN)。取舍:P2P 直连(host/srflx 成功)——零中继成本(无服务器带宽费用)、延迟最低(路径最短)、数据不出用户网络(隐私好);TURN 中继——兜底可靠性(对称 NAT/企业防火墙/移动 CGNAT 下唯一可行)、代价是带宽成本(服务器流量计费)、延迟增加一跳、可扩展性受限(中继容量)。工程取舍:ICE 自动优先直连、TURN 兜底(TURN 服务器仅失败时启用);生产建议——部署自己的 TURN(coturn)控制成本与可用性,监控"直连 vs 中继"占比(质量与成本指标),对延迟敏感业务(音视频)在弱网时评估中继路径质量(部分场景中继更稳定);安全——TURN 认证(长期凭证/时间受限凭证)防滥用。

考察 WebRTC 连接路径的完整认知:STUN 发现、TURN 中继、ICE 排序与探测,回答需量化"直连省成本延迟 vs 中继保可用"并给出部署监控实践。

#
★★★

16. RTCDataChannel 的可靠有序与不可靠(unordered、maxRetransmits/maxPacketLifeTime)模式及各自适用场景

RTCDataChannel 的可靠有序与不可靠模式如何配置?各自适用什么场景?

  • 两种模式的配置参数
  • 可靠/不可靠与有序/无序的组合语义
  • 典型场景映射

RTCDataChannel 创建时通过 dataChannelInit 配置传输语义:可靠有序——默认模式(reliable + ordered),消息保证送达且按序(底层 SCTP over DTLS),适合需要完整语义的通信;不可靠模式——配置 maxRetransmits(最多重传次数,0 表示不重传)或 maxPacketLifeTime(消息存活毫秒,超时丢弃),配合 ordered: false(无序)实现"尽力投递 + 丢弃过期数据"的实时语义。组合矩阵:ordered=true + 重传=有限(有序但丢包重传,部分可靠);unordered + maxPacketLifeTime(无序且仅存活窗口,游戏同步标准配置);unordered + maxRetransmits=0(无序不重传,最新数据覆盖)。场景映射:可靠有序——文件传输、聊天文本、操作日志(必须全部到达且顺序正确);不可靠无序——实时游戏状态同步(位置/朝向,旧帧无意义,等重传反而延迟)、实时协作光标/白板笔画(丢点可接受)、音视频控制消息(非媒体数据但时效敏感)。工程要点:混合使用——同一连接可开多条 DataChannel 配置不同模式(可靠通道传关键消息 + 不可靠通道传高频状态);监控丢包(不可靠通道丢包率)与 SCTP 缓冲;大文件用可靠通道分片 + 流控(DataChannel 的背压:bufferedAmount);SCTP 通道数量与消息大小限制(Chrome 约 64KB 单消息边界、通道数上限)需遵循。注意 ordered:false 与重传参数互斥约束(maxRetransmits 与 maxPacketLifeTime 二选一)。

考察 DataChannel 模式矩阵的精确理解:参数语义(重传次数/存活时间/有序性)、四类组合、场景映射与混合通道设计,回答需给出"按数据时效性开多通道"的工程模式。

#
★★★

17. getUserMedia 的媒体约束(分辨率/帧率/设备 ID)与 enumerateDevices 设备切换、权限拒绝的处理

getUserMedia 的媒体约束如何设置?enumerateDevices 设备切换与权限拒绝如何处理?

  • 约束对象(分辨率/帧率/设备 ID/理想值)
  • enumerateDevices 的设备枚举与切换
  • 权限生命周期与拒绝处理

getUserMedia 通过 constraints 声明媒体要求:{ video: { width: { ideal: 1920, max: 1920 }, height: {...}, frameRate: { ideal: 30 }, facingMode: 'user', deviceId: { exact: id } }, audio: {...} }——ideal 表示偏好(浏览器在能力范围内尽量满足)、exact 强制精确(不满足则失败)、min/max 范围约束;高级约束(Pan/Tilt/Zoom)可用 applyConstraints 动态调整(不重新采集)。enumerateDevices 返回设备列表(MediaDeviceInfo:deviceId/kind/label),用于:设备切换(切换摄像头:停旧轨道 → getUserMedia({deviceId: {exact: newId}}) → 替换轨道 replaceTrack(保持连接))、多设备选择 UI(label 在授权后才完整显示)。权限处理:第一,授权流程——getUserMedia 触发浏览器权限提示(同源记住、跨域策略(secure context 必须 HTTPS)、权限持久化(Permissions API 可查询 camera/microphone 状态));第二,拒绝处理——catch NotAllowedError(用户拒绝/权限策略禁止)给降级体验(无视频模式的提示、引导设置)、NotFoundError(无设备)、OverconstrainedError(约束不满足,去掉 exact 重试);第三,权限变化——用户撤销权限(浏览器设置)后轨道停止(onmute/onended 事件)需感知并重试流程;第四,隐私——不使用即刻 stop() 轨道(灯灭)、卸载清理(track.stop() + 释放);第五,安全——fake device(测试)、安全上下文要求、企业策略(权限请求被静默拒绝时给出指引)。工程实践:设备状态管理与 UI 联动(设备热插拔监听 devicechange)、约束降级链(高→中→低分辨率重试)。

考察媒体采集的完整工程:约束语义(ideal/exact)、设备切换链路(enumerate → 重采 → replaceTrack)、权限状态机与异常分类,回答需覆盖"采集-切换-降级"全流程。

#
★★★

18. CSS Animations Level 2 提案(@keyframes 的 composite/timeline-scope)的工程价值

CSS Animations Level 2 提案(composite、timeline-scope 等)有何工程价值?

  • composite 的动画合并语义
  • timeline-scope 与命名时间线
  • 与 WAAPI/JS 动画的互补

CSS Animations Level 2 扩展了经典 CSS 动画的表达力:第一,composite 属性——定义动画如何与基础值合并:replace(默认,覆盖)、add(累加,如位移叠加)、accumulate(累积,适合旋转/缩放叠加),让"基础状态 + 动画增量"解耦(元素静止态由样式决定、动画只做增量,重排场景可叠加多个动画而不互相覆盖),也利于与 WAAPI 的 composite 对齐;第二,timeline-scope 与命名滚动时间线——@keyframes 可与命名时间线(scroll-timeline/view-timeline 声明、timeline-scope 把子作用域时间线提升给祖先动画使用)绑定,实现"滚动驱动的动画"(animation-timeline: --scroll)与视口驱动(view())进 CSS 语法,无需 JS/ScrollTrigger 监听滚动;第三,动画范围(animation-range)——控制动画进度区间(动画从滚动 20%-80% 执行),精确编排滚动叙事。工程价值:把"交互动画"从 JS 下沉到 CSS(合成器线程执行、无主线程开销、无 JS 运行时依赖),与 CSS 变量/设计令牌结合实现声明式动效系统;与 WAAPI(JS 创建动画)互补——静态/可声明部分用 CSS L2,动态编排用 WAAPI。边界:提案实现进度不一(Chrome 支持 scroll-driven animations 与 composite 部分、Safari/Firefox 跟进),需特性检测 + 降级(JS 监听滚动回退);命名时间线的作用域与嵌套容器行为需仔细验证。

考察 CSS 动画规范演进:composite 的增量合并语义、滚动/视口时间线的 CSS 化、与 JS 方案的分工,回答需落到"合成器性能 + 降级"的工程判断。

#
★★★

19. Scroll-driven Animations 的 animation-range-start/end 与视口驱动滚动

Scroll-driven Animations 的 animation-range-start/end 与视口驱动如何工作?

  • scroll-timeline 与 view-timeline 的声明
  • animation-range 的进度区间
  • 视口驱动(view())的语义

Scroll-driven Animations 让动画进度绑定滚动位置:声明——容器上 scroll-timeline-name: --scroll(命名滚动时间线,以该容器滚动进度为时间线),元素 animation-timeline: --scroll 使用;视口驱动——view-timeline(元素自身相对滚动容器/视口的可见进度)与 view() 函数(animation-timeline: view())驱动"元素进入/离开视口"的动画(如卡片逐个浮现、图片视差内联效果);animation-range-start/animation-range-end 定义动画的进度区间(如 animation-range: entry 20% exit 60%,元素从进入 20% 到离开 60% 期间播放动画),支持 entry(进入视口段)/exit(离开段)/cover(全程可见段)与百分比/像素/元素尺寸单位组合,实现精确的"滚动叙事"编排(章节进入时动画、离开时反播)。工程价值:纯 CSS 实现视差、入场、进度条(滚动阅读进度)、sticky 联动动效,无 JS 滚动监听(合成器执行、性能好、可被浏览器优化);与 IntersectionObserver 方案的对比——CSS 时间线粒度连续(精确到像素的进度)而 IO 只给阈值布尔事件。边界:需特性检测(Chrome 115+ 支持、Safari/Firefox 推进)、嵌套滚动容器的时间线选取(默认最近祖先滚动)、animation-range 的语法兼容(旧浏览器忽略整个 animation-timeline)、与 prefers-reduced-motion 的配合(禁用或简化);回退方案——不支持时用 ScrollTrigger/IntersectionObserver + WAAPI 实现同效果。

考察滚动驱动动画的 CSS 化机制:两种时间线声明、range 的进度区间语义、view() 视口驱动,回答需给出"特性检测 + JS 回退"的工程路径。

#
★★★

20. Lightning CSS 在性能预算与首屏渲染的取舍

Lightning CSS 在性能预算与首屏渲染中有何工程取舍?

  • Lightning CSS 的能力(解析/转译/压缩/打包)
  • 与现代 CSS 特性的处理(嵌套、层级、draft 语法)
  • 性能预算与首屏的收益

Lightning CSS(原 Parcel CSS)是 Rust 实现的 CSS 工具链:解析 + 转译(把现代/草案 CSS(嵌套、@layer、级联、颜色函数(oklch)、媒体查询范围语法)编译为兼容目标浏览器的旧语法)、压缩(体积优化)、打包(bundle 与 dedupe)、自动加浏览器前缀与 targets 优化(按 browserslist 删除不需要的规则)。工程取舍:第一,性能——Rust 实现比 JS 工具(PostCSS 链)快一到两个数量级(大型样式表构建时间从秒级降到毫秒级),显著改善构建性能预算(尤其大型 monorepo 与频繁增量构建);第二,体积——压缩与"按 target 裁剪"减少 CSS 字节(首屏样式更小,配合关键 CSS 内联降低首屏渲染阻塞);第三,现代语法先行——团队可直接写嵌套/层级等未来语法,构建期转译(渐进交付),无需等待浏览器支持;第四,特性与生态——相对 PostCSS 插件生态(autoprefixer、cssnano 等)更"一体化",但插件扩展性有限(特殊需求仍需 PostCSS 链或混用);与 CSS 原生特性(@layer、容器查询)演进并行。取舍建议:新项目/构建敏感(Webpack/Vite 均可集成:Vite 内置支持)选 Lightning CSS 提升预算;深度依赖 PostCSS 插件生态(stylelint 流程、专有插件)时保留 PostCSS(或 Lightning CSS 做转译 + 插件做专项);首屏优化还需配合关键 CSS、内联与异步加载(CSS 加载优先级)综合治理。

考察现代 CSS 工具链的选型:Rust 性能收益、现代语法转译、target 裁剪与体积、生态边界,回答需给出"构建预算 + 首屏"双目标的落地建议。

#
★★★

21. Flex min-width: 0 与现代浏览器一致的工程取舍

Flex 布局中 min-width: 0 的作用是什么?现代浏览器如何保持一致?

  • flex 项的默认最小尺寸(min-width: auto)
  • min-width: 0 的溢出修复
  • 现代浏览器的一致性与工程实践

Flex 布局中 flex 项的默认 min-width 为 auto(而非 0):此时弹性项的最小尺寸 = 内容最小尺寸(content-based,不可压缩低于内容/文字的最小宽度),导致"弹性项内容过长时无法收缩、撑破容器/溢出(横向滚动/文本截断)"的经典问题。设置 min-width: 0 允许弹性项压缩到 0(真正按 flex-basis/flex 分配空间),配合 overflow: hidden 或 text-overflow 实现"固定宽度 + 可收缩 + 省略号"的布局;min-height: 0 同理解决纵向溢出(配合 overflow-y: auto 的滚动容器)。现代浏览器一致性:该行为自 2017 年左右起各浏览器(Chrome/Firefox/Safari)已统一遵循 min-width: auto 规范(早期 Firefox 曾表现不一致,现已修复),现代布局中"min-width: 0 修复溢出"是通用方案;工程实践:第一,识别场景——flex 容器内放长文本/表格/iframe(不可压缩内容)出现溢出的首选排查项;第二,封装——组件库中"可收缩弹性容器"(如表格列、输入框前缀、聊天消息气泡)默认加 min-width: 0 或提供布局原语(避免每个业务重复踩坑);第三,与 overflow 组合——省略号三件套(min-width: 0 + overflow: hidden + text-overflow: ellipsis + white-space: nowrap);第四,grid 的类比——grid 项的 min-width 也受 minmax(auto) 影响(grid-template-columns: minmax(0, 1fr) 同理);第五,验证——现代浏览器布局回归测试(视觉 + 溢出检测),避免"本机正常、线上溢出"。

考察 Flex 布局的深层语义:min-width: auto 的内容最小尺寸机制、min-width: 0 的修复原理、与现代浏览器的统一行为,回答需给出可复用的工程封装。

#
★★★

22. prefers-reduced-motion 在现代组件库的工程价值

prefers-reduced-motion 在现代组件库中有何工程价值?

  • prefers-reduced-motion 的语义与取值
  • 组件库中的动效降级策略
  • 工程实现(CSS/JS/媒体查询)

prefers-reduced-motion 媒体查询反映用户"减少动效"的偏好(系统级设置,如 iOS 减弱动态效果、Windows 关闭动画):取值 no-preference(默认)与 reduce。工程价值:第一,可访问性——眩晕/前庭障碍用户对大幅移动敏感(滚动视差、大缩放、闪烁),尊重偏好是 WCAG(2.3.3 动画交互)要求的现代实践,也避免法律/体验风险;第二,组件库实现——提供降级策略:CSS 层——@media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }(或禁用关键动画,保留不透明度淡入等低刺激过渡);JS 层——window.matchMedia('(prefers-reduced-motion: reduce)') 检测,GSAP/WAAPI 跳过或缩短动画、滚动驱动动画(ScrollTrigger)改为"立即显示最终态";第三,语义分层——区分"功能必需动画"(进度指示、焦点指示(必须保留,仅减时长))与"装饰动画"(视差、入场、微交互(可完全禁用)),组件 API 提供 prop(如 reduceMotion 或遵循全局)与配置项(design token);第四,响应偏好变化——matchMedia 的 change 事件监听(用户在运行中切换系统设置);第五,工程注意——禁止用 !important 一刀切禁用焦点环(可访问性反噬)、测试覆盖 reduce 模式下的可用性(动画不依赖时序)。现代组件库(MUI、Radix、Framer Motion)均有内置支持。

考察动效可访问性的工程落地:偏好语义、CSS/JS 双层降级、功能动画保留原则、偏好变化监听,回答需给出组件库级别的动效治理方案。

#
★★

23. SDP(会话描述协议)的作用与编解码协商(H.264/VP9/AV1/Opus)的工程要点

SDP 在 WebRTC 中的作用是什么?编解码协商(H.264/VP9/AV1/Opus)有哪些工程要点?

  • SDP 的媒体段结构(m=、a=rtpmap)
  • offer/answer 的编解码选择规则
  • 编解码的兼容性与质量权衡

SDP(Session Description Protocol)是 WebRTC 的媒体会话描述:描述媒体流(m= 行:音频/视频类型、端口、传输)、编解码器列表(a=rtpmap:RTP payload type ↔ 编解码(H.264/VP8/VP9/AV1)、a=rtcp-fb(反馈机制:NACK/REMBI)、a=fmtp(编解码参数:H.264 的 profile-level、packetization-mode)、方向(a=sendrecv/sendonly/recvonly)、SSRC 与候选占位;offer 提出能力列表(按偏好排序),answer 从其中选择交集(共同的编解码),协商由"offer 列出、answer 挑选"的规则完成(RTCRtpSender/Receiver 的 getCapabilities 可查询本端支持)。编解码工程要点:第一,兼容性——H.264 是最大公约数(硬件编解码、全平台支持,但浏览器实现 profile 差异(Constrained Baseline)需 fmtp 对齐(packetization-mode=1、profile-level-id 协商));VP8 兼容性次之(无专利顾虑);VP9 质量好(软件/部分硬件);AV1 压缩率最高但编码器(软件)成本高、硬件支持少——生产按"目标设备矩阵"配置 codecPreferences(RTCRtpTransceiver.setCodecPreferences 调整优先级,如移动端优先 H.264 硬件编解码省电);第二,质量与带宽——H.264/VP8 同等码率质量低于 VP9/AV1,但编解码功耗与兼容是现实约束;第三,Opus——音频默认(宽频、带内前向纠错(FEC)、DTX 静音检测省带宽),码率自适应(与带宽估计联动);第四,协商后验证——onnegotiationneeded 重协商(码率/分辨率变更)、媒体不启动排查(编解码不匹配、payload 类型冲突(rtx/red 等冗余机制)))。

考察 SDP 协商机制与编解码工程:SDP 结构、offer/answer 交集规则、codecPreferences 与 fmtp 对齐、H.264/VP9/AV1/Opus 的取舍,回答需落到"按设备矩阵定优先级"的实践。

#
★★

24. Simulcast 与 SVC 在多人视频会议弱网下的应用与带宽分配

Simulcast 与 SVC 在多人视频会议弱网下如何应用?带宽如何分配?

  • Simulcast(多路独立流)与 SVC(可分层单流)的机制
  • SFU 的带宽分配策略
  • 弱网适应与质量权衡

Simulcast 与 SVC 是"一个发送者服务多种接收能力"的方案:Simulcast——发送端同时编码多路独立流(如 180p/360p/720p,各自独立码率),SFU 按接收端需求转发其中一路(切换粒度是"路",发送端编码成本高(多路同时编码)、实现简单成熟,浏览器支持好(Chrome/Firefox/Safari 均支持 send 与 receive(部分)));SVC(可分层视频编码)——单流内分层(空间层/时间层/质量层,如 VP9 的 SVC、AV1 的 SVC 演进),SFU 可丢弃增强层只转发基础层(粒度更细、发送端单路编码成本低、带宽适应平滑),但浏览器支持(接收端解码分层)依赖编解码器(VP9 SVC 在 Chromium 支持,Safari 受限)与 SFU 端裁剪能力。弱网应用:带宽分配——SFU 根据接收端可用带宽(下行估计)选择转发层/路:带宽充足转高分辨率,弱网降层(Simulcast 切小路或 SVC 丢增强层),同时多流竞争时按"发言人优先"(active speaker 高码率、其余低码率)分配;会议服务器(LiveKit/mediasoup/Jitsi)均实现"分层转发 + 按订阅分配";端侧——接收端用 RTCRtpReceiver.getStats 观察丢包/码率触发切换、发送端配合带宽估计(GCC)调节编码码率。工程取舍:浏览器兼容优先(全端)选 Simulcast(SFU 切路简单);编解码层(VP9/AV1)+ 服务器能力允许时 SVC 更省带宽;弱网兜底——质量阶梯(分辨率/帧率/码率三级降级)与"只有基础层也保持可听/可视"的体验设计。

考察多人会议的传输架构:Simulcast 与 SVC 的机制对比、SFU 按带宽分层转发与发言人优先分配、弱网降级阶梯,回答需体现"发送端编码 × 服务器转发 × 接收端选择"的协同。

#
★★

25. addTrack/replaceTrack 与 transceiver 在动态媒体流管理(静音、切换摄像头)中的协作

addTrack/replaceTrack 与 transceiver 如何协作管理动态媒体流(静音、切换摄像头)?

  • transceiver 的组成与语义
  • addTrack/replaceTrack 的时机
  • 静音与设备切换的最佳实践

RTCRtpTransceiver 是"一发一收"的传输对:包含 sender(RTCRtpSender)与 receiver,关联一条媒体流方向(sendrecv/sendonly/recvonly/inactive);addTrack(track, stream) 首次添加时创建 transceiver 并触发协商(onnegotiationneeded → offer/answer),同一 transceiver 后续换轨用 sender.replaceTrack(newTrack)——不重建传输(无需重新协商、连接保持,对端 RTCRtpReceiver.track 自动更新),是切换摄像头/麦克风的推荐路径。动态管理:第一,静音——音频:track.enabled = false(零成本,本端直接停止发送数据,对端静音);视频"假静音"(黑屏):替换为静态黑帧轨道或 cover 处理;标准做法——保留轨道(muted 状态由 UI 表达)避免触发协商;第二,切换摄像头——getUserMedia 新约束采集新轨道 → replaceTrack(对比:removeTrack + addTrack 会触发重协商、有切换毛刺);第三,方向切换——收发方向变更(如从 sendrecv 转 recvonly)需 setDirection + 重协商(协商后生效),用于"共享屏幕后切回"等场景;第四,轨道生命周期——停止旧轨道(track.stop())释放设备(灯灭)、transceiver 停用(stop())完全移除收发;第五,事件与状态——onnegotiationneeded 的触发条件(添加/移除轨道、方向变化)与协商完成(setLocalDescription 后 ICE 复用)的时序;注意 replaceTrack 无需协商但若新轨道编码能力不同(分辨率超协商范围)可能仍需重协商(部分实现)。工程实践:状态机管理"设备/静音/共享"矩阵,切换操作统一走"采集 → replaceTrack → 停旧轨"管线。

考察 WebRTC 动态媒体流的精细操作:transceiver/sender 分层、replaceTrack 免协商切换、静音与方向管理、轨道生命周期,回答需给出无毛刺切换的工程管线。

#
★★

26. WebRTC 拥塞控制(GCC/transport-cc)与带宽估计的观察手段

WebRTC 的拥塞控制(GCC/transport-cc)与带宽估计如何工作?如何观察?

  • GCC 与 transport-cc 的机制
  • 带宽估计对编码的影响
  • getStats 的观察指标

WebRTC 拥塞控制目标是"在不过度丢包/排队的前提下最大化吞吐",核心机制:GCC(Google Congestion Control)——基于丢包(loss-based)与延迟(delay-based)双估计:接收端反馈(RTCP REMB/transport-wide-cc 的丢包率、往返时间、到达延迟梯度)让发送端估计可用带宽,动态调整发送码率(拥塞时指数下降、恢复时加性增长,AIMD 风格);transport-cc(Transport-wide Congestion Control)——在 RTCP 反馈中携带传输级序号与到达时间戳,发送端精确计算每包延迟趋势,是现代默认的带宽估计反馈(配合 REMB 的码率请求)。带宽估计驱动编码器:RTCRtpSender 收到估计带宽后调编码码率(分辨率/帧率降级),媒体质量随网络自适应(弱网自动降清晰度、强网恢复)。观察手段:第一,getStats——RTCRtpSender/Receiver 的 stats 报告:丢包(packetsLost)、RTT(往返时间)、jitter、framesPerSecond、bitrate(编码/发送速率)、qualityLimitationReason(带宽受限/CPU 受限/无)——qualityLimitationReason 直接指示"是否因带宽而降级";第二,chrome://webrtc-internals——图形化查看带宽估计曲线(Target Bitrate、实际发送)、丢包/抖动/RTT 时间线、候选对(选路),是 WebRTC 调试的标配;第三,应用侧打点——周期采集 stats 聚合(RTT/丢包/降级事件)上报监控平台(多用户会议的质量归因)。工程注意:带宽估计需要正确的反馈回路(RTCP 未阻塞、transport-cc 协商成功(SDP 的 rtcp-fb))、NACK/重传占用的带宽计入估计(丢包严重时重传风暴与编码竞争)。

考察 WebRTC 自适应码率的机制与观测:GCC 双估计、transport-cc 反馈、编码联动与 getStats/webrtc-internals 的排查手段,回答需覆盖"机制 + 观测 + 告警"。

#
★★

27. WebRTC 与 WebSocket/WebTransport 在低延迟场景(实时消息 vs 实时音视频)的取舍

WebRTC 与 WebSocket/WebTransport 在低延迟场景如何取舍?

  • 媒体传输(SRTP)与数据通道的差异
  • 低延迟数据(游戏/协作)的传输选择
  • 组合架构

三者定位:WebRTC 专为"实时媒体"设计——音视频经 SRTP(媒体)与 DataChannel(数据),内置拥塞控制、JitterBuffer、回声消除/降噪(音频处理链)、端到端低延迟(P2P 直连);WebSocket/WebTransport 是通用数据通道(WebTransport 提供多流与不可靠数据报)。取舍维度:第一,媒体——音视频必须 WebRTC(WS/WT 无音视频处理管线:编解码(浏览器内 getUserMedia + RTCRtpSender 负责)、同步、回声消除);第二,低延迟数据——游戏状态/协作编辑:WebTransport 的 datagram(不可靠、低延迟、无重传等待)+ 多流更优(WebSocket 单流可靠有队头阻塞、无不可靠通道);WebRTC 的 DataChannel 也可做(SCTP 不可靠模式),但"建连成本 + 媒体设施 + 信令复杂度"比 WebTransport 重(DataChannel 适合与媒体同会话的场景:音视频会议中的白板/弹幕数据);第三,连接拓扑——WebRTC 支持 P2P(浏览器直连,媒体低延迟+省服务器带宽,但需 TURN 兜底与信令);WebSocket/WebTransport 是客户端-服务器(服务端可控、易扩展(SFU/网关)、可观测);第四,复杂度与生态——WebSocket 最简单、WebTransport 居中(QUIC 门槛)、WebRTC 最复杂(协商/ICE/媒体栈)但实时媒体唯一选择。组合架构(生产常见):音视频走 WebRTC(媒体 + DataChannel 控制),信令与旁路数据走 WebSocket,需要不可靠低延迟数据(游戏非媒体部分)用 WebTransport 或 DataChannel;监控与降级(WebRTC 不可用(老旧环境)回退 WHIP/WHEP 或 SFU 转播)。

考察实时技术的分层选型:媒体必须 WebRTC、低延迟数据 WT/WC 按可靠性与拓扑选、P2P 与服务器模型的取舍,回答需给出组合架构而非单一替代。

#
★★

28. 用 getStats 监控 WebRTC 的丢包率、RTT、码率与候选类型(candidate type)的工程实践

如何用 getStats 监控 WebRTC 的丢包率、RTT、码率与候选类型?

  • getStats 的统计对象结构
  • 关键指标的换算(丢包率、RTT、码率)
  • 监控上报与告警设计

RTCPeerConnection.getStats() 返回 RTCStatsReport(Map<id, RTCStats>),按类型访问:inbound-rtp(接收方向:packetsReceived/packetsLost、jitter、framesDecoded、bytesReceived → 码率)与 outbound-rtp(发送方向:packetsSent、bytesSent → 发送码率、framesEncoded、qualityLimitationReason)、remote-inbound-rtp(对端反馈:roundTripTime 直接给 RTT)、candidate-pair(selected 候选对:localCandidateId/remoteCandidateId、nominated 状态)与 local-candidate/remote-candidate(候选类型:host/srflx/relay)。关键换算:丢包率 = packetsLost / (packetsReceived + packetsLost)(inbound);码率 = 相邻两次 bytes 差 / 时间差(采样窗口,注意计数器回绕与重置(oniceconnectionstatechange 断开后归零));RTT 直接读(p95/p99 聚合)。工程实践:第一,采样策略——setInterval(如 5s)getStats 全量或按需(仅统计所选 candidate-pair 与 rtp 流),避免高频采集开销;第二,指标聚合——按"会议/会话/用户"上报:丢包率阈值(>2% 提示弱网、>5% 告警)、RTT 分位、发送/接收码率曲线、候选类型分布(relay 占比 = TURN 成本与质量信号)、qualityLimitationReason 分布;第三,告警与联动——高丢包/RTT 时触发"降级提示"(UI 提示网络不佳、自动切换质量档)、上报后端聚合(多端会议定位劣化方向:上行 vs 下行);第四,事件对齐——getStats 与连接状态事件(iceconnectionstatechange/connectionState)关联(重连/ICE restart 后的统计分段);第五,隐私与开销——指标不含媒体内容(可上报)、采样频率与上报体积平衡(客户端聚合再上报)。工具:chrome://webrtc-internals 做人工排查,自研上报做长期质量监控。

考察 WebRTC 质量监控的完整实践:统计对象结构、指标换算口径、采样上报与阈值告警,回答需落到可落地的监控体系设计。

#
★★

29. SFU/MCU 媒体服务器拓扑在大规模实时音视频中的架构与容量取舍

SFU 与 MCU 媒体服务器拓扑在大规模实时音视频中如何取舍?

  • SFU(转发单元)与 MCU(混合单元)的机制
  • 带宽、CPU 与质量权衡
  • 大规模部署架构

媒体服务器拓扑:MCU(Multipoint Control Unit,混合)——所有参与者的音视频流汇聚到服务器,由服务器解码-混合-再编码为各路(每端一路"合成画面"):端侧带宽与解码成本最低(只收一路)、兼容性最好(老旧端也能收合成流),但服务器 CPU/编码成本高(每路重编码)、扩展成本高、端侧无法单独控制布局(灵活性差);SFU(Selective Forwarding Unit,选择性转发)——服务器只做"选流转发"(不解码不混合,按订阅把参与者的流转发给需要的端):服务器成本低(纯转发 + 带宽调度)、端侧可自由布局(多画面拼接在端侧)、支持 Simulcast/SVC 分层转发(按带宽选路),是现代主流(LiveKit/mediasoup/Jitsi/JANUS 均 SFU);代价是端侧上行带宽(每端上行一路/多路上行(Simulcast))与端侧解码成本(收 N 路解 N 路,端侧性能受限时需降路数)。容量与架构取舍:第一,规模——小会(<10 人)SFU 单机可行;大会(100+)需 SFU 集群(按房间/租户分区、跨区中继(SFU 间级联转发)、TURN 与媒体节点分离);第二,带宽——SFU 的转发带宽 = 各接收订阅之和(与人数近似平方级),需按"发言人优先"(只有活跃发言人的流全量转发,其他人低码率/暂停)控制;MCU 带宽固定(每端一路)但 CPU 成本线性于人数×路数;第三,质量——SFU 保留原始质量(无级联编解码损失),MCU 有重编码损失;第四,混合架构——"MCU 合成 + SFU 转发"混合(如大会议用 SFU、录制/合流用 MCU(livekit 的 egress));部署选型:自建 mediasoup/LiveKit 或托管(LiveKit Cloud/Agora/Daily);容量规划按"并发房间 × 人均订阅路数 × 码率"估算媒体节点与带宽成本。

考察会议架构的容量工程:SFU/MCU 的机制与成本模型(转发 vs 重编码)、发言人优先与分层转发、集群与混合架构,回答需给出按规模与质量的选型计算。

#
★★

30. CSS transition-property 的 all/transform 等的工程取舍

CSS transition-property 使用 all 与 transform 等具体属性有何工程取舍?

  • all 的过度动画与性能
  • transform/opacity 的合成器路径
  • 属性白名单的工程实践

transition-property 决定哪些属性参与过渡:transition-property: all——所有可动画属性都过渡:写法简单(任何样式变化都有动画),但代价:第一,性能——非合成器属性(width/height/top/left/margin、颜色(color 走合成但布局类属性触发重排))每帧触发 layout/paint(主线程开销,复杂页面掉帧);第二,意外动画——布局变化(如字体加载、尺寸自适应)也被动画化,产生非预期过渡与闪烁;第三,动画成本——逐属性动画(多属性同时变化)增加每帧工作量。而 transform/opacity 是"合成器友好"属性:transform(位移/缩放/旋转/倾斜)与 opacity 的变化无需 layout/paint(仅合成层变换,GPU 合成、主线程不参与逐帧计算),是高性能动画的标准选择(配合 will-change/translate 等)。工程取舍:第一,白名单化——transition-property 只列需要动画的属性(transform, opacity 为主,必要时 color/filter 等),避免 all 的意外动画与性能损耗;第二,结构性动画优化——需要动尺寸时用 transform: scale 替代 width/height、位移用 translate 替代 top/left(避免 layout);第三,分层——多个过渡属性分组(transition-property: transform, opacity; 与 transition: all 的快捷写法对比);第四,团队规范——设计系统/组件库约定"可动画属性白名单",lint 规则禁止 all(或禁用对布局属性的过渡);第五,特殊场景——原型/动效不确定性高时可临时 all(快速验证),生产收敛白名单;偏好 reduced-motion 时整体关闭。

考察 CSS 过渡性能与语义的工程权衡:all 的便利与性能/意外动画代价、transform/opacity 的合成器路径、白名单规范,回答需给出"默认白名单"的工程纪律。

#
★★

31. CSS font-size-adjust 与排版微调

CSS font-size-adjust 是什么?在排版微调中有何工程价值?

  • font-size-adjust 的语义(x-height 比例)
  • 字体替换时的视觉一致性
  • 兼容性与现代替代(ascent-override 等)

font-size-adjust 控制"字体替换时的视觉尺寸一致":它定义字体的 x-height(小写字母高度)与字号的比例——指定 font-size-adjust: 0.5 后,浏览器按"实际字体 x-height = 0.5 × 字号"缩放替代字体,使 fallback 字体与小写字高一致(解决"字体未加载/缺失回退时,文本视觉大小突变"的问题:如"Helvetica Neue → Arial → sans-serif"回退链中,不同字体的 x-height 差异导致换行/行高跳动)。工程价值:第一,回退一致性——字体加载(font-display: swap 期间)与缺失回退时排版稳定(标题/正文的视觉字号不跳变);第二,数字/图标字体——等宽数字与图标字体的回退对齐;第三,混合字体场景(西文 + 中文字体回退)的视觉协调。边界与注意:第一,兼容性——font-size-adjust 传统实现(chrome 97+ 支持、Firefox 支持、Safari 17 起支持两值语法),老浏览器需 fallback(不考虑);新语法支持两个值(font-size-adjust: 0.4 from-font 等,Safari 17/Chrome 118+ 支持 from-font(取自原字体的 x-height 比例));第二,限制——只调 x-height 不调字宽/字重(标题的宽度感仍有差异)、行高需要配合(ascent/descent 变化);第三,现代替代——@font-face 的 ascent-override/descent-override/line-gap-override(src local 回退时修正度量,Chrome/Firefox 支持)与 size-adjust(整体缩放回退字体,含字宽)更精细(尤其字体子集/回退字体的排版微调);实践——回退链验证(预览 fallback 状态的布局回归)、关键文本(数字表格、标题)的度量对齐测试。现代趋势:用 @font-face 的 override 系列 + size-adjust 做"回退字体度量修正",font-size-adjust 作为简单场景(x-height 对齐)的便捷手段。

考察排版度量的工程细节:font-size-adjust 的 x-height 缩放语义、回退场景的价值、与 @font-face override/size-adjust 的替代关系,回答需给出"字体回退一致性"的完整方案。

#
★★

32. CSS @keyframes 与 WAAPI(Web Animations API)的协作边界

CSS @keyframes 与 WAAPI 的协作边界在哪里?如何选择?

  • 两种动画定义方式的机制
  • 协作模式(JS 控制 CSS 动画)
  • 按场景选择的边界

CSS @keyframes 是声明式动画(样式表定义,浏览器合成器/主线程按规范执行,无需 JS 参与运行时),WAAPI(Element.animate()/Animation 对象)是 JS 命令式动画(可动态创建/控制动画:play/pause/reverse/seek、timeline 绑定、composite、动态 keyframes 与参数)。协作模式:第一,JS 控制 CSS 动画——document.getAnimations()/element.getAnimations() 获取 CSS 动画的 Animation 对象,用 JS 暂停/反转/seek/移除(如"点击触发列表动画回放");第二,WAAPI 触发——事件(animationend/animationstart)与 Promise(finished)对接 JS 编排;第三,CSS 定义、JS 驱动——keyframes 写样式表(可复用、可主题化),JS 用 animate() 同步创建等价动画或操作既有动画实现"动态参数"(JS 无法直接改 CSS 动画的时长?可改 animation-duration 属性或重建);第四,timeline 共享——WAAPI 的 document.timeline 与 CSS 动画默认共享同一时间线(对齐播放);第五,降级——JS 场景用 WAAPI(动态数据、条件动画),静态/品牌动画用 CSS(可被浏览器优化与预加载)。选择边界:需要动态参数(JS 变量、数据驱动)、运行时交互控制(拖拽关联、进度绑定(滚动驱动(scroll-timeline 与 WAAPI timeline 可配))、复杂编排(多动画协调)→ WAAPI;固定效果、纯样式声明、性能优先(浏览器优化路径)、渐进增强(无 JS 可用)→ CSS;混合——CSS 定义 keyframes(资产) + WAAPI 实例化(控制),兼顾复用与控制力;注意 WAAPI 在合成器执行的前提(transform/opacity)与 JS 调用频率(高频控制用 rAF 节流))。

考察动画两种范式的协作边界:声明式 vs 命令式、getAnimations 的控制通道、动态参数的取舍,回答需给出"CSS 资产 + WAAPI 控制"的混合模式。

#
★★

33. CSS 物理引擎(弹簧、阻尼)在现代框架(Framer Motion)

CSS/JS 中的弹簧物理(spring、阻尼)在现代框架(Framer Motion)中如何应用?

  • 弹簧模型(刚度/阻尼/质量)
  • Framer Motion 的 spring 与 useSpring
  • 物理动画的手感与性能

弹簧物理模型描述"物体受弹性力回归目标"的运动:参数——stiffness(刚度:回归力大小,越大越快越硬)、damping(阻尼:能量耗散,越大越不振荡)、mass(质量:惯性,越大越慢越柔);二阶系统(可过阻尼(无振荡慢回)、临界阻尼(最快无振荡)、欠阻尼(带振荡回弹)),相比时间曲线(easing),弹簧动画"自然"(位移相关、可中断、无固定时长——拖拽松手、目标变化时连续过渡不跳变)。Framer Motion 应用:第一,spring 过渡——motion 组件的 transition: { type: 'spring', stiffness, damping }(或 mass)实现弹性质感(按钮、卡片、模态框的"活"感);第二,useSpring——把"数值/状态"经弹簧平滑(useSpring(value) 返回 motionValue,value 变化后平滑过渡,可与其他动画(useTransform/useMotionValue)组合(如滚动进度 → 弹簧 → 位移动画);第三,拖拽释放——drag 松手后 inertia/spring 回位(velocity 保留的惯性滑行);第四,手势联动——hover/tap 的 spring 反馈;底层——Framer Motion 用 rAF 积分器解弹簧方程(每帧更新 motionValue,驱动 transform),性能关键路径在合成器。工程注意:弹簧动画无法预知时长(编排需用 onAnimationComplete 而非固定延迟);高 stiffness + 低 damping 的强振荡在 reduced-motion 下应禁用;大量元素同时弹簧动画注意 rAF 负载(每个动画独立积分);与设计系统结合——定义标准弹簧预设(如"标准/强调/松弛"三档)保证手感一致)。

考察物理动画的工程应用:弹簧参数语义与二阶系统特性、Framer Motion 的 spring/useSpring/拖拽释放、编排与性能注意,回答需给出"预设化 + reduced-motion"的落地。

#
★★

34. BFC 触发与边距折叠 在大型设计系统的工程价值

BFC 触发与边距折叠在大型设计系统中有何工程价值?

  • BFC 的触发条件与布局效果
  • 边距折叠的规则
  • 设计系统中的容器与间距治理

BFC(块级格式化上下文)决定块级盒子内部的布局规则(内部浮动不溢出、不与外部浮动重叠、外边距不与外部折叠、内部垂直方向边距折叠);触发方式:overflow 非 visible(hidden/auto/scroll)、display: flow-root(现代首选,无副作用)、inline-block/flex/grid 容器、position: absolute/fixed、float 等。边距折叠:垂直相邻外边距合并(取较大值)、父子边距折叠(子元素 margin-top 穿透父容器——父无 padding/border/BFC 时)、空块自折叠。设计系统价值:第一,容器封装——组件根元素用 flow-root 建立 BFC:内部浮动/清除(icon 浮动)不外泄、子组件 margin 不穿透父组件(每个组件"自包含"的间距契约——卡片/弹层/工具条嵌套时外边距不再互相污染);第二,间距系统——边距折叠规则与设计令牌(spacing scale)冲突(子 margin 被折叠导致间距失效),用"父 padding + BFC"或"单一方向 margin(margin-bottom)"规范消除折叠(或明确折叠语义:段落间距依赖折叠反而省心,但组件边界应隔离);第三,布局修复——overflow: hidden 的 BFC 副作用(裁剪/滚动)在新布局中用 flow-root 替代;第四,代码审查——lint 规则(禁止子元素穿透 margin、组件根必有 BFC 或 padding 保护)、设计系统文档化"间距在哪一层声明"(组件内 vs 组件间);大型系统收益:组件独立(可组合无布局泄漏)、主题与间距令牌一致、回归测试稳定(快照布局不再被外部影响)。

考察 BFC/折叠与设计系统的工程结合:BFC 的组件自包含价值、折叠对间距契约的破坏、flow-root 与 lint/文档治理,回答需给出"组件边界隔离间距"的设计系统方法论。

#
★★

35. Rive(.riv 文件 + 状态机)作为 Lottie 的现代替代

Rive(.riv 文件 + 状态机)作为 Lottie 的替代方案有哪些优势与边界?

  • Rive 的文件格式与状态机模型
  • 与 Lottie(AE JSON)的对比
  • 适用场景与迁移考虑

Rive 是新一代动效工具链:编辑器产出 .riv 文件(二进制,包含矢量图形、骨骼(articulation)、网格变形、粒子与状态机),运行时(Web/Flutter/React Native/iOS/Android/游戏引擎)原生渲染。核心差异化:第一,状态机(State Machine)——动效内含"输入 → 状态转换"逻辑(trigger/bool/number 输入驱动动画切换),把"交互逻辑"做进文件(悬浮/按下/完成态由状态机管理),前端只需调输入 API(rive 实例的 fireStateEvent/inputs 赋值),无需写动画编排代码——这是 Lottie(纯播放 JSON)不具备的;第二,渲染——自研矢量渲染器 + 骨骼/网格变形(mesh)表现力强(角色动画、可形变 UI),性能好(GPU 加速);第三,体积与工具——.riv 紧凑、编辑器可视化(预览 + 测试状态机)。对比 Lottie:Lottie 优势在 AE 生态(设计师用 AE + Bodymovin 直接导出、插件与社区多)、简单播放场景成熟;Rive 优势在"可交互、骨骼、网格、粒子、多运行时一致、体积"。边界:设计工具切换(Rive 编辑器 vs AE 工作流)、复杂 AE 效果(表达式、复杂混合模式)迁移成本高、生态(教程/模板/招聘)相对 Lottie 少、JSON 生态工具(lottie-web 的调试/回退)缺失;工程选型:纯播放型插画动效(AE 工作流)留 Lottie;交互驱动(按钮态、角色、游戏化 UI、跨端一致)上 Rive;可两者共存(不同动效不同引擎)。集成:@rive-app/canvas(Web)与 react 封装、RN 官方运行时。

考察动效引擎代际差异:状态机与骨骼/网格是 Rive 的核心增量、Lottie 的 AE 生态优势、按"播放型 vs 交互型"选型,回答需给出迁移与共存策略。

#
★★

36. Lottie(AE 导出 JSON、前端渲染动画)的场景

Lottie(AE 导出 JSON、前端渲染动画)适合什么场景?使用要点是什么?

  • Lottie 的渲染方式(SVG/Canvas/WebGL)
  • 适用场景(插画动效、loading、图标)
  • 性能与工程要点

Lottie 工作流:设计师在 AE 制作动效,用 Bodymovin 插件导出 JSON(记录图层、路径、关键帧、缓动、遮罩、效果),前端用 lottie-web(SVG 默认渲染器,可切 Canvas/WebGL)或各平台原生播放器渲染。适用场景:插画风格动效(引导页、空状态、欢迎动画)、loading/进度动效、图标微动效、营销落地页的丰富动画——本质是"复杂逐帧/矢量动画的复用"(无需前端逐帧手写);对比 CSS/JS 动画(简单位移缩放更轻)、视频(Lottie 矢量可缩放、体积小、可交互(播放控制))。使用要点:第一,渲染器选择——SVG(默认:清晰、可 CSS 控制、性能中等)适合图标与中等复杂度;Canvas(大场景/多实例性能好、与 DOM 解耦)适合长动画/后台列表;WebGL(lottie-web 的实验渲染器)极复杂场景;第二,性能——复杂 JSON(多图层/大位图)解析与首帧渲染成本(延迟加载 + 预加载、按需实例化(列表滚动中复用实例)、暂停不可见动画(IntersectionObserver));第三,体积——JSON 含路径数据(图形越复杂越大),设计侧约束(简化图层、位图(img 资源)单独管理),构建期压缩(gzip/brotli 服务端压缩);第四,控制——play/stop/goToAndStop(进度控制)、循环与事件(complete/enterFrame)、与 reduced-motion(禁用/静态首帧)与主题(颜色替换:lottie 的表达式/替代色插件);第五,回退——JSON 加载失败/播放器缺失时的静态图(img)回退;React 封装(lottie-react)的销毁清理。

考察 Lottie 动效工程的场景化应用:工作流与渲染器选择、性能/体积治理、控制与可访问性回退,回答需给出"何时用 Lottie + 如何用"的完整判断。

#
★★

37. Framer Motion 的 useMotionValue 与弹簧物理动画

Framer Motion 的 useMotionValue 如何工作?如何与弹簧物理动画配合?

  • motionValue 的数据模型
  • useTransform/useSpring 的组合
  • 性能关键路径

useMotionValue(initial) 创建 motionValue——一个"可被动画驱动、可驱动渲染"的响应式数值容器:motion 组件的 style/x/scale 等可绑定 motionValue,其值变化时组件以最快路径更新(合成器 transform,不触发 React 重渲染——motionValue 是 React 渲染树之外的独立数据流)。核心 API:.get()/.set()(设置值驱动动画)、.on('change')(订阅)、useTransform(mv, fn)(派生值:把输入值经函数/范围映射为输出(如滚动进度 → 位移/透明度))、useSpring(mv, config)(弹簧平滑派生:输入值突变时输出值以弹簧动力学过渡)。弹簧物理配合:第一,交互联动——指针位置(useMotionValue 存坐标)→ useSpring 平滑 → useTransform 映射为卡片的 rotate/scale(拖拽跟手、松手回弹);第二,数据驱动动画——状态值变化(如计数)经 useSpring 让数字/条幅平滑滚动;第三,与手势/滚动——scroll 的 scrollY 经 useTransform + useSpring 做视差(弹簧缓冲防生硬);第四,动画编排——animate(mv, target, {type:'spring'}) 直接驱动 motionValue 的弹簧动画;性能——motionValue 更新走合成器(transform/opacity),不触发 React 渲染(对比 setState 驱动动画每帧重渲染的高成本),是高性能交互动画的关键;注意:motionValue 脱离 React 渲染(组件卸载需清理)、在动画回调中读取最新值(闭包陷阱用 on('change') 或 useMotionValueEvent)。工程模式:把"输入状态"(motionValue)与"派生显示"(useTransform/useSpring)声明化,动画管线(值流 → 弹簧 → 映射 → 渲染)完全由框架优化。

考察 Framer Motion 的数据流模型:motionValue 的渲染外数值流、useTransform/useSpring 的派生链、合成器性能与 React 渲染的隔离,回答需给出"输入-派生-渲染"管线。

#
★★

38. anime.js、Popmotion、Tone.js 的现代取舍

anime.js、Popmotion、Tone.js 在现代前端动效/音频中如何取舍?

  • 三个库的定位差异
  • anime.js 的轻量补间、Popmotion 的物理与 motionValue、Tone.js 的 Web Audio 编程
  • 按需求选型

三者定位完全不同:anime.js——轻量通用动画库(补间 DOM/CSS/SVG/JS 对象:anime({ targets, translateX: 100, ... })),时间线/交错(stagger)编排、贝塞尔缓动,零依赖、体积小,适合"JS 驱动的通用补间动画"(数据可视化转场、SVG 路径动画、交错列表),现代取舍:API 简洁、社区活跃(2022+ 重新维护),但生态(插件)不如 GSAP、React 集成需自行封装;Popmotion——"运动原语"库(value/spring/decay/pointer 追踪):核心是 motionValue 与物理动画(弹簧、惯性、插值),不绑定渲染层(输出值由用户消费),是 Framer Motion 的地基(其底层库),现代取舍:直接使用适合"自定义渲染管线"(Canvas/SVG 手工绘制 + 物理值驱动)、与框架无关(vanilla 可用),但 API 底层(需要自己接 DOM)学习成本高;Tone.js——Web Audio 框架(合成器、采样、效果器、音序(Transport/Loop)、音频图编排(节点连接)),用于网页音乐/音效生成(交互音效、生成音乐、音频可视化数据源),与动画库不同域(音频而非视觉动画)。选型:通用 DOM/SVG 补间 → anime.js(轻)或 GSAP(重/生态全);自定义/物理驱动的低层动画(游戏 HUD、Canvas 动效)→ Popmotion(或直接用 Framer Motion);音频编程/交互音效 → Tone.js;三者可组合(Tone.js 音频 + anime.js 视觉同步)。工程注意:动画库的 React 集成(useEffect 包装/封装 hook)、性能(rAF 驱动对象数量)、包体积预算。

考察动画/音频库的定位辨析:anime(通用补间)、Popmotion(运动原语/物理值)、Tone.js(Web Audio),回答需按"视觉补间、自定义管线、音频"三个域选型。

#
★★

39. JS 动画库经历了哪些版本演进,与 Web Animations API、CSS 动画、Skia 等替代方案相比各自定位是什么

JS 动画库经历了哪些版本演进?与 WAAPI、CSS 动画、Skia 相比各自定位是什么?

  • 动画库的演进脉络(jQuery → GSAP/TweenMax → 现代库)
  • 四种方案的定位与分工
  • 现代选型框架

演进脉络:早期 jQuery 动画(animate()/fadeIn——基于 JS 定时器逐帧改样式,性能差、卡顿);GSAP(TweenLite/TweenMax → GSAP 3 统一)时代——性能优化(rAF、属性缓存、插件生态(ScrollTrigger/Draggable))成为专业动画事实标准;同期 jQuery 衰落、CSS 动画(transition/keyframes,2012+)把简单动画下沉浏览器;WAAPI(Element.animate,2016+)提供 JS 命令式 + 浏览器原生执行的"中间层";现代库分化——轻量(Motion One 基于 WAAPI)、框架绑定(Framer Motion/React、@vueuse/motion)、物理(Popmotion)、声明式滚动驱动(CSS L2/ScrollTrigger)。定位分工:CSS 动画——静态/可声明动画(性能最优(合成器)、零 JS 依赖、渐进增强),但不支持动态编排;WAAPI——需要 JS 控制的动画(动态参数、播放控制),性能同 CSS(浏览器执行);GSAP 类库——复杂编排(时间线、滚动、拖拽、兼容性兜底(老浏览器))、生态完整,重营销页/复杂交互动效;Skia(React Native Skia 等)——渲染层引擎(Canvas 绘制/着色器),动画需自建或配合 Reanimated(跨端高性能图形动画,属"渲染技术"而非"动画库")。现代选型:简单 UI 动效 → CSS;需要控制/数据驱动 → WAAPI;复杂编排/跨浏览器兼容/专业动效 → GSAP/Motion One;跨端/游戏化图形 → Skia 系;"CSS 优先 + WAAPI 增强 + 库兜底"是渐进增强共识。评估维度:性能路径(合成器)、编排能力、生态、体积、无障碍(reduced-motion)。

考察动画技术栈的全景认知:演进脉络(jQuery→GSAP→现代)、四层定位(CSS/WAAPI/库/Skia 渲染)、渐进增强选型,回答需体现"分层 + 渐进"的方法论。

#
★★

40. OffscreenCanvas 在动画渲染的并行

OffscreenCanvas 如何在动画渲染中实现并行?

  • Worker 内 OffscreenCanvas 的创建与移交
  • 并行渲染的任务划分
  • 主线程协作与通信

OffscreenCanvas 并行渲染模式:主线程创建 OffscreenCanvas(或 transferControlToOffscreen 把已创建画布的控制权移交),经 postMessage 转移到 Worker(转移所有权,零拷贝),Worker 内获得 2D/WebGL 上下文进行渲染,结果经"提交到屏幕"(transferControlToOffscreen 场景:Worker 直接绘制到显示画布)或"渲染到离屏缓冲 → transferToImageBitmap → 主线程 drawImage 上屏"(提交 ImageBitmap)呈现。并行架构:第一,多 Worker 分块——大画布按区域(瓦片/行带)分给多个 Worker 并行绘制(地图瓦片、大图合成、粒子分区),各 Worker 独立渲染后拼接;第二,计算与渲染分离——Worker 内先算(物理/几何/路径)再画(同线程离屏绘制),主线程零阻塞(主线程只做交互与合成);第三,rAF 协作——Worker 内自驱动 requestAnimationFrame(OffscreenCanvas 上下文支持 rAF?实际:Worker 无 rAF,用自循环或主线程按帧率发消息(postMessage 带时间戳)驱动(现代实现中 OffscreenCanvas 的 context 可通过 commit 提交,时序控制常由主线程广播帧时钟));第四,数据流——输入(用户交互、数据更新)经 postMessage 传入(结构化克隆/转移 ArrayBuffer)、输出(ImageBitmap 或绘制结果)回传;注意 SharedArrayBuffer + Atomics 可共享大缓冲区(免克隆)。边界:Worker 无 DOM(字体测量(Canvas 2D 的 fillText 在 Worker 中可用已加载字体?——文本渲染依赖字体系统,Worker 中可用但不完整)、图像解码(createImageBitmap 可用)受限);WebGL 上下文在 Worker 的创建受 GPU 限制(部分设备)——需特性检测与回退(主线程渲染)。

考察离屏并行渲染的架构:所有权转移(transfer 零拷贝)、任务划分(分块/计算分离)、帧时钟与数据流协作、Worker 能力边界,回答需给出"多 Worker 分块 + ImageBitmap 上屏"的范式。

#
★★

41. WebGL 性能瓶颈与 GPU 内存优化

WebGL 的性能瓶颈有哪些?GPU 内存如何优化?

  • 绘制调用(DrawCall)与状态切换
  • 顶点/纹理内存管理与复用
  • 渲染优化(合批、LOD、池化)

WebGL 性能瓶颈:第一,CPU 侧——DrawCall 数量(每次 drawElements 的驱动开销,大量小对象时 CPU 受限)、状态切换(绑定 program/纹理/缓冲的切换成本)、着色器编译与链接(首次慢,需缓存);第二,GPU 侧——像素填充率(overdraw:透明层叠、全屏后处理)、纹理采样带宽(大纹理/多纹理)、顶点处理(顶点数);第三,内存——纹理/缓冲的 GPU 内存占用(超限导致加载失败或系统内存兜底变慢)、纹理上传(CPU→GPU 拷贝)。优化实践:第一,合批——InstancedMesh/实例化(同几何多实例一次调用)、纹理图集(atlas)合并小图(减少纹理切换与绑定)、合并静态几何(BufferGeometryUtils.mergeVertices/合并);第二,DrawCall 治理——按 material/geometry 分组排序、减少 program 切换(材质统一/宏折叠)、剔除(frustum culling 默认、occlusion 可选);第三,内存——纹理压缩(KTX2/Basis、ASTC/ETC 按平台)、mipmap(缩小采样带宽 + 内存(约 1.33 倍但过滤快))、资源池化与复用(粒子几何、共享材质)、及时释放(dispose geometry/material/texture(GPU 资源引用计数))、纹理尺寸约束(power of two 与 NPOT 兼容)、LOD(多级模型/纹理按距离切换)、透明层级(半透明排序与 overdraw 控制(同区域透明对象合并));第四,测量——WebGL 的 debug 扩展(EXT_disjoint_timer_query 测 GPU 时间)、DevTools 的 Render 统计(draw call、textures)、spector.js 抓帧分析。工程上建立"绘制预算"(目标设备 DrawCall/像素成本)并监控。

考察 WebGL 性能工程的全链路:瓶颈分层(CPU DrawCall/GPU 填充/带宽)、合批与图集、纹理压缩与释放、测量手段,回答需落到"预算驱动 + 分层优化"。

#
★★

42. Canvas 2D 与 WebGL 在动画渲染的性能与适用场景的工程取舍

Canvas 2D 与 WebGL 在动画渲染上的性能与适用场景如何取舍?

  • 两种 API 的渲染模型差异
  • 性能特征(2D 状态机 vs GPU 管线)
  • 场景选择与混合使用

渲染模型:Canvas 2D 是"即时模式状态机"(CPU 端指令(fill/stroke/drawImage)→ 浏览器栅格化,简单 API、自动批处理(现代浏览器对同类绘制优化)但复杂场景 CPU 受限);WebGL 是 GPU 管线(顶点/片元着色器、缓冲区、显式绘制调用),数据上载 GPU 后由 GPU 并行处理(大量几何/像素吞吐高),但 API 复杂(着色器/缓冲管理)。性能特征:小规模绘制(< 数千图元、简单图形、图表、UI 装饰)Canvas 2D 足够且简单(自动抗锯齿、文本/图像方便);大规模几何/粒子/复杂效果(数万图元、逐像素滤镜、3D)WebGL 显著更优(GPU 并行、合批、实例化);边界场景——Canvas 2D 的 drawImage(位图合成)性能好(GPU 加速合成),纯文本与简单矢量用 2D 心智负担低。工程取舍:第一,场景——交互图表、地图标注、2D 游戏 UI 用 Canvas 2D(开发效率);2D 游戏本体、大粒子、实时特效、3D 用 WebGL/Three.js;第二,性能预算——目标设备 60fps 下实测(2D 掉帧则评估 WebGL 化热点(大量相同图元→实例化));第三,混合——Canvas 2D 做简单层(文本/标注)+ WebGL 做重层(场景/粒子),离屏互传(2D drawImage(webgl canvas) 或 WebGL 纹理上传 2D 结果)——仪表盘类应用常见(底层 WebGL 渲染大数据点、上层 Canvas 2D 叠加标注/文本);第四,维护——WebGL 代码复杂度与 shader 调试成本高(spector.js、断点),2D 易于维护,按"热点局部优化"原则升级而非全量重构。现代趋势:WebGPU 将进一步替代 WebGL(新项目评估)。

考察 2D/GPU 渲染的取舍:渲染模型与性能边界、按图元规模分档、混合分层架构、热点局部升级,回答需给出"默认 2D + 热点 GPU 化"的务实路径。

#
★★

43. WebGL 的 requestAnimationFrame 与垂直同步(VSync)

WebGL 的 requestAnimationFrame 与垂直同步(VSync)如何配合?

  • rAF 与显示刷新同步
  • VSync 与撕裂
  • 帧率控制与自适应

requestAnimationFrame 的回调在"下一次屏幕刷新(VSync)之前"执行(浏览器调度,通常 60Hz/120Hz 显示对应回调频率),WebGL 在回调中完成"更新 + 提交渲染"后,浏览器在 VSync 时呈现——渲染与刷新同步避免撕裂(tearing:半帧切换)与卡顿;rAF 的节流(后台标签页暂停)省电,是 WebGL 动画的驱动标准(对比 setInterval 无同步、可能撕裂)。VSync 相关:默认浏览器按 VSync 提交(display: swap),60Hz 显示器上限 60fps(高刷 120/144Hz 则按显示频率);rAF 频率与显示器刷新率绑定(120Hz 屏回调 120 次/秒);"VSync off"(渲染快于刷新)在 WebGL 中不可直接控制(浏览器管理提交时机),开发者通过帧预算优化保证"每帧 < 16.7ms"(60Hz)否则掉帧(实际帧率 = 显示频率的整数约分,如 60Hz 下 20ms 帧 → 30fps)。工程要点:第一,帧预算——目标帧率下控制每帧 GPU/CPU 工作量(降低分辨率/复杂度/粒子数),用 DEVTOOLS(FPS 面板)与 performance 测量真实帧率(rAF 时间戳差);第二,自适应——根据实测帧率动态降级(质量档位:分辨率/特效开关),高刷屏可选择性限制(requestAnimationFrame 无法直接限帧,用时间戳节流实现 30fps 模式(省电));第三,渲染策略——"脏检查 + 按需渲染"(无变化不提交,用 rAF 调度但跳过重绘)、后台节流(visibilitychange 暂停);第四,同步与测量——rAF 回调的 timestamp 参数用于帧间 delta(动画时间步进),避免依赖 Date.now 的跳变;多显示器/DPR 场景的坐标适配。

考察 WebGL 渲染节奏的机制:rAF 与 VSync 的同步关系、撕裂与掉帧原理、帧预算与自适应降级,回答需落到"预算控制 + 按需渲染"的工程实践。

#
★★

44. WebGL 上下文丢失(context lost)事件的监听与自动恢复的工程实践

WebGL 上下文丢失(context lost)如何监听与恢复?

  • contextlost/contextrestored 事件
  • 丢失原因与 preventDefault
  • 资源重建策略

WebGL 上下文可能因 GPU 重置(驱动崩溃、休眠恢复、显存压力、多标签页资源竞争)而丢失:canvas 触发 webglcontextlost 事件,随后上下文不可用(所有调用失效、绘制丢失);若事件未 preventDefault,浏览器尝试恢复(触发 webglcontextrestored);preventDefault 后浏览器不自动恢复(由应用决定重建时机)。工程实践:第一,监听——canvas.addEventListener('webglcontextlost', e => { e.preventDefault(); 进入"暂停渲染"状态(停止 rAF、显示加载态/遮罩);});'webglcontextrestored' 时重建;第二,重建——恢复后上下文是全新状态:所有 GPU 资源(program、buffer、texture、framebuffer)全部失效,必须重新编译/上传(材质、几何、纹理、后处理链),实践上把"资源创建"集中为可重放函数(rebuildAll():从应用数据(几何数据/纹理源)重建 GPU 对象)——这也要求应用层持有"源数据"(几何顶点数组、图片/像素源)而非只持有 GPU 句柄;第三,时序——恢复事件可能延迟(数秒),期间 UI 保持可交互(非 WebGL 部分)、恢复后立即重绘一帧;第四,丢失期间的降级——WebGL 不可用(多次丢失/恢复失败)时回退 Canvas 2D 或显示静态内容;第五,测试与监控——DevTools 可模拟(chrome://flags 或调试面板的"模拟 GPU 崩溃")、生产上报 contextlost 事件(频率与原因)观察设备问题。Three.js 的 WebGLRenderer 自带 context lost 处理(receive 事件 + 重建钩子),自定义引擎需自行实现。

考察 WebGL 容错工程:事件语义(preventDefault 控制恢复)、资源可重放化设计、降级与监控,回答需给出"源数据持有 + 重建函数"的架构要点。

#
★★

45. FLIP 技术(First、Last、Invert、Play)

FLIP 技术(First、Last、Invert、Play)是什么?如何实现?

  • FLIP 四步的机制
  • transform 动画的合成器性能
  • 现代替代(View Transitions API)与边界

FLIP 是"用 transform 模拟布局动画"的技术:First——记录元素初始位置/尺寸(getBoundingClientRect);Last——(修改布局后)记录最终位置/尺寸;Invert——计算位移/缩放差,用 transform: translate/scale 把元素"移回"初始视觉位置(此时布局已是最终态,元素视觉不变);Play——把 transform 过渡到 0(动画播放,元素从旧位置"飞"到新位置)——整个过程只有 transform 动画(合成器执行、无重排逐帧),实现"布局变化的平滑过渡"。应用场景:列表重排(拖拽排序)、折叠展开(手风琴)、元素增删的位移动画、React 的列表 diff 动画(react-flip-toolkit 等库封装)。实现要点:First/Last 需在布局变更前后同步测量(注意 forced reflow(批量读写分离:先批量读(Read)再改布局(Write),避免布局抖动));Invert 用 translate/scale 矩阵计算(含尺寸变化的缩放、圆角/透明度的补间可一并动画);Play 用 WAAPI(el.animate)或 CSS 过渡,结束后清理 transform(防样式残留);复杂场景(嵌套/裁剪/固定定位)需处理容器层级(父级 overflow 裁剪导致 transform 视觉异常)。现代替代与边界:View Transitions API(document.startViewTransition)把"DOM 变化 + 快照过渡"原生化(同文档/跨文档(MPA)视图过渡),减少手写 FLIP;但浏览器支持(Chrome/Edge 支持、Safari/Firefox 推进中)与自定义控制(伪元素动画)仍需按需;FLIP 仍是"控制级"方案(可精确干预每一帧、降级简单)。reduced-motion 下应跳过动画。

考察布局动画的经典技术:四步机制与合成器原理、测量时序(读写分离)、现代 View Transitions 替代与边界,回答需给出"FLIP 实现 + 新 API 评估"。

#
★★

46. transform: translateZ(0)/will-change: transform 的图层提升合成与现代取舍

transform: translateZ(0)/will-change: transform 的图层提升有何作用?现代工程如何取舍?

  • 合成层(图层)提升的机制
  • 提升的代价(内存)
  • 现代实践(按需 will-change)

浏览器渲染管线(layout → paint → composite)中,元素被提升为独立合成层(GPU 纹理)后,其 transform/opacity 变化由合成器单独处理(不触发整页重绘)——transform: translateZ(0)/translate3d(0,0,0)(经典 hack)或 will-change: transform 触发提升,用于"固定头部/卡片/动画元素"避免滚动与动画的重复绘制。代价:每个合成层占用 GPU 内存(纹理 = 尺寸 × 4 字节/像素,大元素/多元素提升显著增加内存,移动端显存压力大)、层过多增加合成开销与层树管理成本(浏览器对层数量有上限/合并策略),滥用(全部元素提升)反而性能劣化。现代取舍:第一,默认不提升——现代浏览器已自动为"正在动画的元素"(transform 过渡/动画)临时提升层(动画结束回收),无需 hack;第二,按需使用——仅对"持续/高频合成动画 + 滚动固定"元素显式 will-change(用后移除(动画结束删 will-change 属性,长期保留浪费内存)),或 transform 动画时加、结束移除;第三,替代——用 opacity/transform 本身就是合成属性(不提升也可由合成器处理(若元素在普通层,动画期间浏览器提升)),translateZ hack 属历史遗留(为触发层);第四,判断——DevTools 的 Layer 面板/渲染统计(composited layers 数量、内存)核对提升效果与成本;第五,边界——提升不解决布局/绘制开销(layout 触发仍全量)、含大量背景裁剪/滤镜的元素提升收益有限;现代推荐:让浏览器自动处理(动画属性用合成属性)+ 必要时(卡顿定位后)显式 will-change 并即时回收。

考察合成层优化的现代认知:提升机制与 GPU 内存代价、浏览器自动提升、按需 will-change 与回收、DevTools 验证,回答需纠正"无脑 translateZ(0)"的历史误区。

#
★★

47. Canvas 动画 requestAnimationFrame 主线程释放到 Worker(OffscreenCanvas + Worker)

如何把 Canvas 动画的 rAF 主线程负载释放到 Worker(OffscreenCanvas + Worker)?

  • transferControlToOffscreen 的移交机制
  • Worker 内 rAF 替代(自循环/消息驱动)
  • 交互与状态同步

释放主线程的架构:主线程创建 OffscreenCanvas,canvas.transferControlToOffscreen() 获得 OffscreenCanvas 并随 postMessage 转移给 Worker(主线程保留展示 canvas 的引用(不能再直接操作其 context));Worker 内拿到 OffscreenCanvas 创建渲染上下文(getContext('2d'/'webgl'))持续绘制,结果直接显示(控制权移交模式下 Worker 绘制即上屏)。rAF 驱动:Worker 无 requestAnimationFrame(DOM API),方案——第一,主线程驱动:主线程 rAF 循环中 postMessage 帧时钟(时间戳/序号)给 Worker,Worker 收到后更新 + 渲染(主线程仍有轻量循环(一次 postMessage),但无渲染计算);第二,Worker 自驱动:Worker 内用 setTimeout 模拟帧节流(性能不如 VSync 同步)或 MessageChannel 的精确调度(不可靠);现代 Chromium 支持 Worker 内的 OffscreenCanvas 的 requestAnimationFrame?(部分环境提供(OffscreenCanvas 的 commit 与 rAF 在 worker 的实验支持)——需特性检测,主流仍用消息驱动)。状态同步:交互(鼠标/触摸/拖拽)在主线程监听 → postMessage 传递输入(坐标/事件);数据(数组/图像)经结构化克隆/transfer(ArrayBuffer 转移零拷贝,SharedArrayBuffer 共享);回传(Worker → 主线程):完成信号、ImageBitmap(transferToImageBitmap 后 transfer)、统计。收益:渲染/物理计算(粒子、图表重绘、图像处理)全部移出主线程,主线程专注交互与布局(帧率稳定、滚动流畅);边界:transfer 后主线程不可再绘制该画布(需要双画布或回传 ImageBitmap 方案)、Worker 字体/图像解码限制、多 Worker 分块时的拼接与同步成本、WebGL 在 Worker 的设备兼容(特性检测)。工程模式:重计算动画(大数据可视化、粒子、视频处理预览)采用"Worker 渲染 + 消息输入";轻量动画直接主线程。

考察离屏线程化渲染的工程架构:控制权移交与零拷贝、帧驱动的两种模式、输入/输出消息设计与边界,回答需给出"主线程交互 + Worker 渲染"的分工图。

#
★★

48. CSS Subgrid 在表单/对话框/Popover 的工程价值

CSS Subgrid 在表单、对话框、Popover 中有何工程价值?

  • subgrid 的语义(继承网格轨道)
  • 对齐场景(标签/输入框、弹层头部)
  • 兼容性与降级

CSS Grid 的 subgrid 让嵌套网格继承父网格的轨道定义(grid-template-columns: subgrid——子网格的列轨道与父网格对齐,无需重复声明尺寸),解决"多层嵌套网格的轨道对齐"问题。表单场景:表单项(标签 + 输入框 + 提示/错误列)横排时,每行子网格 subgrid 父网格的列(标签列、输入列、错误列宽度统一),多行/分组的标签对齐(对比重复定义 grid-template-columns 的维护成本与漂移);对话框/Popover:标题区 + 内容区 + 操作区的列对齐(弹层内部不同区块共享轨道:图标列、文本列、按钮列),宽度变化时(响应式弹层)所有行同步;嵌套分组表单(fieldset 分组)中各组间列对齐;配合 grid-template-areas 的语义化布局。价值:单一事实源(轨道在父级定义)、响应式(父级轨道变化自动传播子网格)、代码量减少与可维护性(避免魔法数字)。兼容性与降级:subgrid 支持(Chrome 117+、Firefox 71+、Safari 16+(2023 全面支持)),老浏览器降级——子网格退化为普通网格(布局仍可用但不对齐)或提供 fallback 列定义(@supports (grid-template-columns: subgrid) 分支);工程注意:subgrid 只能继承"一个维度"(行或列)、子网格的轨道数量需匹配父网格跨度(超限忽略)、子网格自己的 gap 可覆盖父(对齐仍按父轨道);组件库中作为"布局原语"封装( 、),统一轨道设计令牌。

考察 subgrid 的布局工程价值:继承轨道实现跨区块对齐、表单/弹层的典型受益、兼容降级与组件封装,回答需落到"单源轨道 + 组件原语"的实践。

#
★★

49. Shared Element Transition 在 SPA 与跨视图共享元素的工程价值

Shared Element Transition 在 SPA 与跨视图共享元素中有何工程价值?

  • View Transitions API 的共享元素机制
  • SPA 路由切换的过渡
  • 工程实现与边界

Shared Element Transition 由 View Transitions API 提供:document.startViewTransition(callback) 捕获"DOM 变更前/后"两帧快照,浏览器自动生成过渡动画(旧快照淡出、新快照淡入),通过视图过渡伪元素(::view-transition-old/new)可定制效果;共享元素(Shared Element)——给新旧 DOM 中的同一元素标记相同 view-transition-name,浏览器对该元素做"位置/尺寸/形状补间"(元素从旧位置"飞"到新位置),实现"跨视图共享元素"的连续动画(列表缩略图 → 详情大图、图标 → 全屏弹层)。SPA 工程价值:第一,路由过渡——在路由切换(React Router/Nuxt 的页面切换)包裹 startViewTransition(框架层封装:拦截导航 → 执行 transition → 更新状态),页面级过渡声明化(默认交叉淡入 + 自定义),替代手写 FLIP/进场出场编排;第二,共享元素——列表-详情、标签页切换、弹层锚定的元素级连续动画(免手写测量与 transform 计算);第三,性能——过渡由浏览器合成器执行(快照 + 合成,无 JS 逐帧),代码量小、性能好。工程要点:第一,快照语义——callback 内同步更新 DOM(React 需 flushSync 强制同步提交使快照正确);第二,伪元素定制——::view-transition-group/old/new 的动画定制(时长、曲线、方向),多个命名元素分组控制;第三,边界——跨文档(MPA 导航)支持(Chrome/Edge 支持、Safari 18+ 支持(部分)、Firefox 推进中)需特性检测;内容差异大的过渡(结构完全变化)快照过渡可能生硬,需配合裁剪(clip-path)定制;无障碍(reduced-motion 自动禁用)、iframe 与跨域资源快照限制;第四,降级——不支持时直接执行 callback(无动画),渐进增强。

考察视图过渡的现代工程化:快照机制与共享元素动画、SPA 路由封装(flushSync)、伪元素定制与特性检测降级,回答需给出"原生过渡 + 路由集成"的落地路径。

#
★★

50. 微交互、视差滚动、骨架屏的体验设计

微交互、视差滚动、骨架屏在体验设计中如何应用?

  • 三种体验手法的设计语义
  • 实现技术与性能边界
  • 组合与降级

三种体验手法各有职责:微交互(Micro-interaction)——"触发 → 反馈"的瞬间动效(按钮按压、开关切换、表单校验、加载图标),传达状态与因果关系,提升操作确定感;设计原则——即时(反馈 < 100ms 感知)、克制(60-200ms、不喧宾夺主)、语义(按压深度、选中高亮)、统一(设计系统内一致(时长/缓动令牌));实现——CSS transition/transform 为主(合成器),复杂用 WAAPI/库;视差滚动(Parallax)——背景/装饰元素以不同速率相对滚动,营造深度感(营销页、故事页);实现——JS 滚动监听(rAF 节流、translate 变换)或 CSS 滚动时间线(scroll-timeline)与 background-attachment(有限);边界——性能(滚动主线程负载(用 transform + 合成层)、大量元素视差掉帧)、移动端禁用/减弱(带宽与电量)、内容可读性(避免文字区视差干扰);骨架屏(Skeleton)——内容加载前的"结构占位"(灰块 + 呼吸光效),降低感知等待(perceived performance);实现——纯 CSS(渐变动画)、按真实布局结构(防跳动)、错误态/超时降级(骨架 + 失败重试);LCP 指标配合(骨架不影响真实内容加载)。组合与降级:加载阶段骨架屏 → 内容入场微交互 → 滚动叙事视差分层;统一受 prefers-reduced-motion 控制(全部降级为静态/短淡入);性能预算——交互反馈走合成器、视差滚动限幅(clamp 位移)、骨架动画低频率(避免大面积闪烁成本)。设计系统化:把微交互参数(时长/缓动)、视差速度系数、骨架组件作为设计令牌与组件资产沉淀。

考察体验手法的系统设计:三种手法的语义与实现(合成器/滚动时间线/骨架 CSS)、性能边界与 reduced-motion 降级、设计系统化沉淀,回答需给出"组合 + 预算"的整体方案。

#
★★

51. CSS Anchor Positioning 与设计系统令牌(Design Token)

CSS Anchor Positioning 如何与设计系统令牌(Design Token)结合?

  • Anchor Positioning 的机制(anchor/position-try)
  • 与 JS 弹层定位(Popper)的对比
  • 令牌化设计

CSS Anchor Positioning(锚点定位)让"浮层元素"相对"锚点元素"定位:锚点声明(anchor-name: --btn)、浮层用 position-anchor: --btn + position-area/inset-area(放置区域:top/right 等)或 anchor() 函数(anchor(--btn 50% 100%) 引用锚点位置);position-try-fallbacks(position-try)定义"放不下时的回退位置"(溢出换边),实现"自适应弹层"(tooltip/下拉/菜单/气泡)的定位逻辑。与 JS 方案(Popper/Floating UI)对比:CSS 原生定位无 JS 测量(快、无闪烁(首帧即正确位置))、响应式自动(容器查询/视口感知的回退)、声明式;边界——浏览器支持(Chrome 125+ 支持(部分语法演进:position-area 从 position-try 演化)、Safari/Firefox 推进中(Safari 26+ 支持)),复杂场景(动态翻转与视口边界联动、多个锚点、滚动容器嵌套)的成熟度仍需验证,JS 库仍是全兼容兜底。与 Design Token 结合:把定位语义令牌化——锚点命名规范(--anchor-* 命名空间)、间距令牌(position-area 的偏移 = spacing 令牌(anchor(--btn 100% 8px) 的 8px 来自 --space-2))、回退顺序令牌(position-try 的 fallback 链(top→bottom→start→end)作为布局策略令牌)、圆角/阴影/动效令牌用于弹层样式;设计系统组件(Tooltip/Dropdown/Popover)内置锚点布局原语(CSS 类 + 令牌映射),主题化时只换令牌;特性检测(@supports (anchor-name: --a))降级到 Floating UI 定位(同一组件双实现)。

考察原生弹层定位的前沿工程:锚点/回退机制、与 JS 定位库的对比、令牌化与降级策略,回答需给出"原生优先 + JS 兜底"的组件化路径。

#
★★

52. SVG SMIL 动画、、路径动画 Morphing

SVG SMIL 动画( 等)与路径动画 Morphing 如何实现?工程上如何取舍?

  • SMIL 动画元素(animate/animatetransform/animateMotion)
  • 路径 Morphing(形状补间)
  • 兼容性与工程取舍

SVG SMIL(Synchronized Multimedia Integration Language)在 SVG 内声明动画: (属性动画:attrName + from/to/values + dur + repeatCount)、 (transform 动画:translate/scale/rotate 等)、 (沿路径运动:path + begin/end 与 rotate 对齐)、 (瞬间属性变化);支持 begin/end 触发(click/事件/时间)与关键帧(keyTimes/keySplines 贝塞尔),是"SVG 内嵌、无需 CSS/JS"的声明动画。路径 Morphing:——两个路径的 d 属性补间(前提:路径命令结构与点数一致(相同 M/C/S 序列),否则浏览器插值行为怪异),用于图标/形状变形(汉堡菜单 → 关闭、图形过渡);现代 CSS 替代——CSS 的 d: path() 属性动画(Chrome/Edge 支持(path 补间)、Firefox 支持、Safari 不支持)与 offset-path(运动路径,Safari 16+ 支持 motion path);WAAPI 也可驱动(el.animate 的 d 动画)。工程取舍:第一,兼容性——SMIL 全浏览器支持(含旧 IE 边缘情况,Chrome 曾弃用又保留、Safari/Firefox 支持),但性能一般(主线程动画、无合成器优化)且调试/测试工具少;第二,现代推荐——简单动画用 CSS(transform/transition,合成器、易维护);路径运动用 CSS offset-path(运动路径)或 WAAPI;仅 SVG 内嵌/无外部依赖场景用 SMIL(如部分旧环境);第三,Morphing 注意——路径点数一致性(设计侧约束:用同一路径模板变换)、复杂形状(结合变换矩阵过渡)或使用 morph 库(flubber 自动插值(形状采样));第四,性能与可访问性——SMIL 动画受 prefers-reduced-motion 影响有限(需手动处理)、多个 SMIL 动画的性能与暂停控制(SVGSVGElement.pauseAnimations)。

考察 SVG 动画技术谱系:SMIL 三元素语义、Morphing 的点数约束、与 CSS/WAAPI 的替代与取舍,回答需给出"CSS 优先 + 特定场景 SMIL/Morphing"的分层。

#
★★

53. Pointer Events 统一鼠标/触摸/手写笔的工程价值与兼容性,touch-action 与 passive listener 的关系

Pointer Events 如何统一鼠标/触摸/手写笔?touch-action 与 passive listener 的关系是什么?

  • Pointer Events 的指针统一模型(pointerType)
  • touch-action 对默认手势的声明
  • passive listener 与 preventDefault 的约束

Pointer Events(pointerdown/move/up/enter/leave/cancel/over/out)把鼠标、触摸、手写笔统一为"指针"抽象:pointerType(mouse/touch/pen)、pressure(压感)、width/height(接触面积)、pointerId(多指针区分,兼容多点触控)、setPointerCapture(捕获:指针事件持续派发给指定元素——拖拽的关键能力)、pointercancel(系统抢占:滚动/手势识别中断);相对 mouse 事件的增强(笔/触摸的倾斜、悬停(pen hover))。与 touch-action:浏览器对触摸有默认行为(滚动、缩放、双击缩放、长按),touch-action 属性声明"该元素区域允许哪些默认手势"(none/pan-x/pan-y/pinch-zoom/manipulation)——自定义触摸交互(拖拽、画板、滑动组件)需 touch-action: none(或 manipulation(允许滚动缩放禁双击缩放))禁止浏览器抢走手势,否则 pointermove 会被 pointercancel 打断;因此"触摸自定义交互 = Pointer Events + touch-action 声明"。与 passive listener:addEventListener('touchstart', fn, { passive: true })(默认 true 于 touchstart/touchmove(chrome 默认))意味着监听器不能 preventDefault(passive 保证滚动性能:浏览器不必等待 JS 决定是否阻止)——所以"需要阻止默认手势(如阻止滚动的滑块容器)"必须显式 passive: false 才能 preventDefault,或改由 touch-action 声明(推荐:声明式优于 JS 阻止)。工程要点:指针事件中判断手势(pointerdown → 移动阈值 → drag vs tap)、pointercancel 处理(手势被系统抢走时的回滚状态)、统一事件减少"mouse + touch + pen 三套监听"的重复;兼容——Pointer Events 全现代浏览器支持(老 IE 用 Pointer Events Polyfill 或回退 mouse/touch)。

考察指针统一模型与手势冲突治理:指针抽象与捕获、touch-action 声明与 pointercancel 的关系、passive 对 preventDefault 的约束,回答需给出"声明式(touch-action)+ 捕获 + cancel 处理"的完整链路。

#
★★

54. HTML5 Drag&Drop 与基于 Pointer Events 自实现拖拽的取舍,原生行为 vs 精细控制

HTML5 Drag&Drop 与基于 Pointer Events 自实现拖拽如何取舍?

  • 原生 DnD 的 API 与限制
  • Pointer Events 自实现的模式
  • 按场景选型

HTML5 Drag&Drop:draggable="true" + dragstart/dragover/drop/dragend 事件,浏览器提供拖拽图像(drag image)与放置反馈(dropEffect);优势——原生拖拽视觉(系统级 drag image)、跨窗口/跨应用(拖文件到页面)、无需自写渲染;限制——触摸设备不支持(拖拽只能鼠标/部分实现)、拖拽图像样式控制弱、dragover 的默认行为需要 preventDefault 才能 drop、跨浏览器行为不一致(Safari 文件拖拽限制)、性能与状态管理(拖拽中样式由类名切换)。Pointer Events 自实现:pointerdown 记录起点 + setPointerCapture + pointermove 计算位移(transform 跟随)+ pointerup/pointercancel 结束(放置/取消);完全控制——自定义拖拽样式(缩放/透明度/占位)、阈值(移动 N px 才进入拖拽态)、方向锁定、动画(FLIP 回位)、触摸/笔全支持、与组件状态深度集成。取舍:文件拖入(外部)→ 必须原生 DnD(drop 事件读 DataTransfer.files);内部元素拖拽(列表排序、面板移动、弹层)→ Pointer Events 自实现(体验与兼容可控);两者可结合——内部拖拽自实现 + 文件落区用原生 DnD(同一组件监听两套事件)。工程要点(自实现):拖拽状态机(候选 → 拖拽中 → 放置/取消)、性能(transform 合成器、占位符布局稳定(FLIP)、列表重排节流)、无障碍(键盘拖拽(按钮 + 上下移)与 aria-grabbed/aria-dropeffect 语义)、测试(Pointer Events 模拟(playwright 的 touch/mouse 模拟))。dnd-kit 等库封装了"指针事件 + 无障碍 + 触摸"的完整方案(现代推荐,内部拖拽)。

考察拖拽实现的两种范式:原生 DnD 的文件/系统能力与触摸限制、指针自实现的精细控制与状态机、选型与库化(dnd-kit),回答需给出"文件用原生、内部用自实现/库"的分工。

#
★★

55. 捏合缩放/滑动手势识别与 touch-action、passive listener 的关系及手势冲突解决

捏合缩放与滑动手势如何识别?touch-action、passive listener 与手势冲突如何解决?

  • 多指手势识别(pointer 追踪)
  • touch-action 的声明式治理
  • 手势冲突的判定与化解

捏合缩放(pinch-zoom)与滑动(pan/scroll)的识别:监听指针事件(pointerdown 记录两个 pointerId 及其距离),pointermove 时计算两指距离变化(scale = 当前距离/初始距离)与中点位移;单指移动判断滑动;识别要点——距离阈值(手指微动不触发)、移动方向(垂直 vs 水平滑动判定:斜率/累积位移比较,超过阈值(如 10px)锁定方向)、惯性(松手后的速度 → 惯性动画(Framer Motion 的 inertia))。touch-action 与手势冲突:浏览器默认触摸行为(页面滚动、浏览器缩放)会抢占手势——touch-action 声明区域允许的手势:需要自定义捏合缩放的容器设 touch-action: none(完全接管)或 pinch-zoom(允许浏览器缩放,自定义部分手势);滑动区域保留 pan-x/pan-y(滚动交给浏览器);列表内横向滑动删除 + 纵向滚动 = touch-action: pan-y(垂直滚动允许、横向手势留给应用);passive listener 关系:touch-action 是"声明式",浏览器无需等待 JS——有 touch-action 时不必 preventDefault(passive 默认即可);若想用 JS 阻止(历史方式 touchmove preventDefault)必须 passive: false(性能风险——每帧等待 JS),现代推荐一律 touch-action 声明 + pointer 事件监听(passive 无冲突)。手势冲突解决:方向锁定(第一次位移方向决定手势归属)、阈值仲裁(手势 A 触发后取消手势 B)、层级(子容器手势优先(stopPropagation/捕获)或父级判负(pointercancel 通知))、长按 vs 拖拽(时间阈值)、双击 vs 缩放(时间间隔)。工程实践:手势库(hammer.js 已老化)或自实现状态机(start/move/end/cancel + 判定器),配合组件 props(direction、threshold、disabled)与 reduced-motion(惯性动画降级)。

考察触摸手势工程的系统设计:多指识别算法、touch-action 声明式治理替代 preventDefault、方向锁定与阈值仲裁的冲突化解,回答需给出"声明式 + 状态机 + 判定器"的实现框架。

#
★★

56. 惯性滚动与橡皮筋效果(overscroll-behavior、-webkit-overflow-scrolling)的实现原理与工程边界

惯性滚动与橡皮筋效果(overscroll-behavior 等)的实现原理与工程边界是什么?

  • 浏览器原生惯性/橡皮筋的机制
  • overscroll-behavior 的链式滚动控制
  • 自定义惯性滚动(JS 方案)的边界

惯性滚动(momentum scrolling)与橡皮筋(rubber-banding/回弹)是触摸/滚轮滚动时浏览器的原生体验:手指/滚轮离开后按速度惯性滑行(随时间衰减)、到达边界时"拉长再回弹"(iOS 橡皮筋、Chrome 的 overscroll 效果)——这些由浏览器(滚动线程/合成器)实现,性能最好(主线程零参与)。overscroll-behavior 控制"滚动越界"的行为:auto(默认:滚动链(propagation)——子容器滚到边界后继续滚动父容器/页面)与 contain(边界停止,不触发父容器滚动)、none(连浏览器的拉拽刷新/橡皮筋也禁用)——用于:弹层内滚动到底不带动背景滚动(contain)、无限滚动容器的边界处理、禁用页面级下拉刷新(none)。-webkit-overflow-scrolling: touch 是 iOS 旧版的"惯性滚动开关"(iOS13 起默认开启,历史遗留属性,无需再设)。工程边界与自定义方案:第一,何时自定义——需要"滚动位置驱动 UI"(滚动进度、视差)、自定义惯性参数(衰减曲线/回弹强度)、与滚动联动的交互(导航高亮)时,用 JS 方案(自维护 scrollTop/transform + rAF 惯性物理);但 JS 滚动失去原生性能与可访问性(滚动事件、滚动条、辅助技术),且与浏览器滚动冲突(需 overflow: hidden + 全量接管),一般仅用于"非标准滚动区域"(图表拖拽缩放、轮播);第二,性能——监听 scroll 用 rAF 节流 + passive(默认);大量元素联动(滚动视差)走合成层;第三,工程注意——overscroll-behavior 的兼容(Chrome/FF/Safari 16+ 支持)、滚动容器嵌套时的链式行为测试、惯性滚动的"速度采样"(自定义方案记录最近 N 次位移估算 velocity)。

考察滚动体验的实现原理:原生惯性/橡皮筋的合成器实现、overscroll-behavior 的链式控制、JS 自定义的适用边界与性能注意,回答需给出"原生优先 + 按需自定义"的判断。

#
★★

57. 拖拽排序在大量列表项下的性能优化,transform 占位、虚拟列表与 FLIP 技术结合

拖拽排序在大量列表项下如何做性能优化(transform 占位、虚拟列表与 FLIP 结合)?

  • 拖拽排序的性能瓶颈
  • transform 跟随与占位策略
  • 虚拟列表 + FLIP 的组合

拖拽排序的性能瓶颈:拖动过程中"被拖项 + 其余项位置变化"的每帧重排/重绘(直接改布局(insertBefore/改 offset)会全量 reflow)、大量列表项(1000+)的 DOM 操作成本。优化手段:第一,transform 跟随——被拖项用 transform: translate 跟随指针(合成器,不触发 layout),其余项先不动;第二,占位策略——拖拽中不真实移动 DOM,计算"被拖项应插入的目标索引"(按各项位置(getBoundingClientRect 缓存)与指针位置二分/线性判断),只在"目标索引变化"时用 FLIP 动画让其他项过渡到新位置(批量测量 → 改顺序 → transform 反算 → 过渡),避免逐帧重排;第三,FLIP 批量——列表重排(拖拽穿越)时:First 记录所有项位置、Last(重排后)记录、Invert(transform 回初始)、Play(过渡)——一次重排一帧动画(合成器),配合 rAF 批处理(一帧内只做一次布局变更);第四,虚拟列表(virtualized)——只渲染可视区项(react-window/自定义),拖拽命中判定用"位置计算"而非 DOM 查询(项高固定/预估 + 索引计算),可视区外项不渲染(拖拽跨区时滚动容器 + 自动滚动(pointer 接近边界自动滚));结合——虚拟列表内拖拽排序(dnd-kit 的 sortable + virtualization 适配):可见项 transform 动画 + 占位索引状态(占位符/空槽表示目标位置);第五,测量缓存——项尺寸缓存(ResizeObserver 失效更新)、避免拖拽中反复 getBoundingClientRect(采样节流)。工程注意:iOS/触摸的 pointercancel 与滚动冲突(方向锁定)、拖拽结束的落位动画(FLIP 回位 + 数据更新(一次 setState))、无障碍(键盘排序)与测试(拖拽模拟)。

考察大数据量拖拽排序的性能工程:transform 跟随 + 索引判定、FLIP 批量重排、虚拟化与自动滚动组合、测量缓存,回答需给出"不重排拖动 + 批量重排动画"的核心范式。

#
★★

58. Pointer Events 的 pointercancel/pointerleave 在拖拽中断场景的处理与恢复策略

Pointer Events 的 pointercancel/pointerleave 在拖拽中断场景如何处理与恢复?

  • pointercancel 的触发条件
  • pointerleave 与捕获的关系
  • 中断恢复策略

拖拽中断的两种事件:pointercancel——浏览器/系统抢走指针(触摸滚动开始(浏览器手势)、弹窗出现、设备失联(来电)、OS 手势、触控板滚动),指针事件流被系统终止,所有后续 move 不再派发;pointerleave——指针离开元素边界(有 setPointerCapture 时捕获会让 leave 不再触发(捕获期间事件持续派发给捕获元素,包括元素外),pointerup/pointercancel 后释放捕获)。处理策略:第一,pointercancel 即"拖拽取消"——回滚到拖拽前状态:移除占位/拖拽样式、transform 回位(动画)、恢复被影响项的布局(如排序恢复到原位或保持"取消"语义)、释放 pointer capture(系统自动)与清理监听;UI 反馈——取消提示(无痕取消 vs 明确提示);业务——拖拽取消与"放置失败"(drop 区外)语义区分(cancel = 系统中断、drop 失败 = 应用判定);第二,pointerleave 处理——捕获模式下列表/容器内的拖拽通常不受 leave 影响(捕获持续派发),但"无捕获"方案(指针在窗口外)需要 window 级监听(pointermove/up 挂 window 防丢失);第三,恢复策略——中断后状态机回"空闲"、记录中断原因(用于统计/重试)、触摸场景的"拖动+滚动"混合(阈值判定:未超阈值的移动视为滚动(不进入拖拽),避免误触发);第四,容错——pointercancel 后确保 DOM 不残留拖拽态(样式清理函数幂等)、边缘情况(cancel 后立刻新 pointerdown(快速重试)状态机正确重置);测试——模拟 cancel(浏览器 DevTools/自动化触发 pointercancel)验证回滚。

考察拖拽中断的容错工程:cancel 触发面、捕获与 leave 的关系、状态回滚与恢复、幂等清理与测试,回答需给出"状态机 + 回滚 + 容错"的完整处理。

#
★★

59. 手势识别库(hammer.js)与原生 Pointer Events 手势检测的工程取舍

hammer.js 与原生 Pointer Events 手势检测如何取舍?

  • hammer.js 的能力与维护状态
  • 原生 Pointer Events 手势识别的实现成本
  • 选型建议

hammer.js 是经典手势库:内置手势识别器(tap/doubletap/swipe/pan/pinch/rotate/press)、多指管理与阈值参数、事件委托(hammer(element) → 绑定手势事件),封装了 touch/mouse/pointer 的归一(其内部基于触摸/鼠标事件,新版本支持 Pointer Events 输入);维护状态——已进入低维护(不再活跃迭代、与 React 严格模式/现代 Pointer Events 有兼容摩擦(事件绑定时序、触摸行为差异)),新项目一般不推荐直接引入。原生 Pointer Events 手势检测:用 pointerdown/move/up/cancel + pointerId 追踪自实现:判定器(阈值、方向、多指(两 pointer 距离/旋转))、状态机(候选 → 识别 → 执行/取消)、各手势独立识别器组合(tap 与 pan 的冲突仲裁(先触发者胜/阈值区分))。取舍:项目需要多种手势且不想维护 → 轻量库(@use-gesture(React/vanilla,现代、基于 Pointer 事件、支持拖拽/滚轮/捏合))或 hammer.js(存量项目);对少量手势(拖拽、滑动、捏合)→ 自实现更可控(无依赖、与现代框架生命周期契合、按需裁剪(性能));场景复杂度——手势库的优势在"多指组合 + 冲突仲裁"的开箱即用,自实现需要完整的状态机设计(易出边界 bug(cancel、多指增删、方向锁定))。工程要点:React 中事件绑定(useEffect 生命周期、严格模式双挂载)、阈值参数(与设计令牌一致)、passive/touch-action 配合(手势容器 touch-action: none)、reduced-motion(惯性动画降级)、测试(手势模拟工具)。现代推荐:复杂交互 @use-gesture/dnd-kit;简单自定义 Pointer Events 自实现。

考察手势识别的工程选型:hammer 的能力与维护风险、原生 Pointer 的实现成本(状态机/仲裁)、现代库(@use-gesture)与自实现的边界,回答需给出"按手势复杂度与维护意愿"的选型。

#

60. WebGL 动画的着色器(GLSL)复杂度与移动端 GPU 性能的工程取舍

WebGL 着色器(GLSL)复杂度与移动端 GPU 性能如何取舍?

  • 着色器复杂度的成本(指令/纹理/分支)
  • 移动端 GPU 的限制(带宽/散热/驱动)
  • 工程优化(简化/分层/LOD)

着色器复杂度直接决定 GPU 成本:片元着色器(Fragment Shader)按像素执行——指令数(ALU 运算)、纹理采样(带宽)、动态分支(divergence,GPU 按分支执行全部路径)、浮点精度(highp/mediump 在移动端差异大)、循环(循环次数放大执行量);顶点着色器按顶点执行(顶点数 × 指令)。移动端 GPU 限制:带宽与功耗(GPU 共享内存带宽、散热约束(长时间高负载降频))、驱动质量差异(低端机/旧机型的编译器优化弱、uniform 数量限制(顶点/片元 uniform 上限))、fillrate(高分屏(DPR 3+)像素量放大)。工程取舍:第一,简化——复杂效果分层(基础光照 + 可选后处理),把逐像素计算移到顶点/预计算(纹理烘焙(法线/光照图));第二,分支控制——避免逐像素动态分支(用数学近似/纹理查找替代 if)、固定循环次数(unroll 友好);第三,精度——移动端用 mediump(精度够则省指令)、根据设备(highp 能力检测(OES_standard_derivatives 等扩展))分级;第四,LOD——按设备档位(高/中/低:着色器变体(宏切换)、纹理分辨率、后处理开关)、帧率自适应(掉帧降档);第五,测量——EXT_disjoint_timer_query 测 GPU 耗时(分 pass)、DevTools/Unity Profiler 类比、真机矩阵(低端安卓重点);第六,工程治理——着色器预算(指令数/采样数限制)、构建期校验(变体裁剪)、着色器缓存(编译缓存(WebGL 的 program 缓存(KHR_parallel_shader_compile 异步编译))减少卡顿)。移动端实践:效果按"性能预算阶梯"交付(全效果 → 中效果 → 基础),以 60fps 为基准、目标设备实测。

考察着色器性能的移动端工程:成本模型(片元 × 像素、带宽、分支)、设备限制(精度/散热/驱动)、优化分层与 LOD 变体,回答需给出"预算阶梯 + 真机矩阵"的治理方法。

#

61. WebGL 的 Instanced Rendering 在大量相似物体动画的工程价值

WebGL 的 Instanced Rendering 在大量相似物体动画中有何工程价值?

  • 实例化绘制(ANGLE_instanced_arrays)的机制
  • 每实例数据(attribute divisor)
  • 工程应用(粒子、植被、群体动画)

Instanced Rendering(实例化绘制)用一次绘制调用渲染大量"相同几何 + 不同实例属性"的对象:扩展(ANGLE_instanced_arrays)提供 drawArraysInstanced/drawElementsInstanced 与 vertexAttribDivisor(指定属性"每实例"还是"每顶点"递增——实例属性(位置/颜色/缩放/旋转/随机种子)存实例缓冲(如 InstancedBufferAttribute),每个实例一次取值)。相比逐对象 draw 调用,实例化把 DrawCall 从 N 降到 1(CPU 驱动开销骤减),GPU 一次遍历几何 × 实例数(顶点着色器按实例属性变换),是"大量相似物体"的标准方案。工程应用:粒子系统(每粒子 = 一个实例(位置/大小/颜色/透明度属性 + 顶点着色器动画(时间驱动))——数万粒子单 DrawCall)、草地/森林/人群(同几何体多实例 + 随机变换)、实例化地图标记/标记点、星群/雨雪、批量网格(Three.js 的 InstancedMesh 封装(setMatrixAt/实例矩阵更新 + instanceMatrix.needsUpdate))。工程要点:第一,属性组织——实例属性按"静态 + 动态"分组(动态属性(位置)单独缓冲减少上传)、实例矩阵(Matrix4 打包 4 个 vec4);第二,更新策略——每帧更新实例缓冲(GPU 上传成本与实例数成正比,用动态绘制(DYNAMIC_DRAW)缓冲 + 批量上传);第三,剔除——实例级剔除(frustum culling 对实例逐个计算,Three.js 支持(frustumCulled + 实例包围))与 LOD(远处实例简化);第四,限制——单次实例数上限(GL_MAX_VERTEX_ATTRIBS 属性槽数限制单实例属性量(可打包到纹理/SSBO(WebGL2)绕过))、实例着色器分支开销;第五,与合批对比——实例化要求几何相同(属性不同),几何不同用合批(合并顶点)或两者组合。移动端实例化性能受顶点着色器复杂度与填充率约束。

考察实例化渲染的工程价值:机制(divisor/一次调用)、典型应用(粒子/群体)、缓冲更新与剔除治理、与合批的边界,回答需给出"同几何多实例用 Instanced、异几何用合批"的判据。

#

62. WebGL 的 Vertex Buffer Object(VBO)

WebGL 的 Vertex Buffer Object(VBO)是什么?工程上如何管理?

  • VBO 的创建与数据上传
  • 顶点属性布局(attribute 指针)
  • 缓冲管理(动态/静态、复用)

VBO(Vertex Buffer Object)是 GPU 显存中的顶点数据缓冲:createBuffer() 创建、bindBuffer(ARRAY_BUFFER) 绑定、bufferData 上传(顶点坐标/法线/UV/颜色/索引),随后用 vertexAttribPointer 声明属性布局(每属性:size/type/stride/offset)并 enableVertexAttribArray 启用,绘制时 GPU 从缓冲读取;索引缓冲(ELEMENT_ARRAY_BUFFER)存三角形索引(drawElements 减少重复顶点)。工程管理:第一,usageHint——bufferData 的 usage(STATIC_DRAW(几何不变:模型)、DYNAMIC_DRAW(每帧更新:粒子位置)、STREAM_DRAW(一次使用)),浏览器据此选择存储(显存 vs 系统内存);第二,更新——动态数据用 bufferSubData 局部更新(避免整体重传),按"偏移 + 大小"更新脏区;第三,布局——多属性交错(interleaved)或分离(separate)布局的选择(交错缓存友好(一次访问多属性)、分离便于部分更新(位置 vs 法线分开更新));第四,复用与生命周期——几何缓存(同几何共享缓冲(合并 DrawCall 前先去重)、组件卸载 dispose(deleteBuffer 释放 GPU 内存)、材质/几何对象的"上传一次"标记;第五,WebGL2——VAO(Vertex Array Object)封装属性指针状态(一次设置多次使用,避免每帧重设属性)、与实例化属性配合;第六,对齐——顶点数据字节对齐(float 4 字节对齐、矩阵的行优先/列优先约定);调试——缓冲区内容检查(spector.js、Draw call 统计)、内存监控(显存占用(任务管理器/平台 API)))。

考察 WebGL 顶点数据的工程管理:VBO 生命周期与 usage、属性布局(交错/分离)、动态更新与复用、VAO 封装,回答需覆盖"上传-布局-更新-释放"闭环。

#

63. WebGL 的纹理压缩(DXT、ETC、ASTC)在移动端内存与带宽的工程价值

WebGL 纹理压缩(DXT、ETC、ASTC)在移动端内存与带宽上有何工程价值?

  • 压缩纹理格式与平台支持
  • 内存与带宽的收益
  • 工具链与加载流程

纹理压缩(GPU 直接采样压缩格式)相比 RGBA 未压缩:内存——压缩比(ASTC 4x4 约 8:1、DXT1/ETC1 约 8:1、ASTC 8x8 约 32:1),同样显存可容纳更多/更大纹理(移动端显存有限,避免 OOM/纹理降级);带宽——GPU 采样时传输数据量小(纹理带宽是移动端 GPU 主要瓶颈之一),渲染速度提升(同场景下 fillrate/带宽友好)。格式与平台:DXT(BC 系列,桌面/高通部分)、ETC1(旧安卓标准、无 alpha(ETC1 RGB))、ETC2(GLES3 标准、安卓/主流(iOS 也支持)、带 alpha)、ASTC(现代标准(iOS/安卓高端、Vulkan 生态)、灵活块尺寸(4x4~12x12 按质量/尺寸权衡)、支持 sRGB/3D 纹理);iOS 的 PVRTC(旧设备)。工程流程:第一,离线压缩——构建期用工具(basisu/KTX 工具、纹理压缩服务(Compressonator/TexConv)、Three.js 的 KTX2Loader(Basis Universal 转目标平台格式))把源图转压缩格式(KTX2/Basis 容器跨平台(运行时解到目标格式或直接采样));第二,加载——压缩纹理需按平台选格式(特性检测(WEBGL_compressed_texture_* 扩展)→ 选可用格式 → 加载对应文件);Basis/KTX2 运行时转码(小体积分发 + 目标格式采样);第三,注意——压缩纹理的"块"特性(尺寸需块对齐(如 4x4)、mipmap 生成在压缩前、压缩格式不能随意局部更新(缓冲直接写限制))、压缩质量与 mipmap 结合(远距 mip 用更小压缩率)、UI 纹理(需要精确色的 UI/字体)压缩伪影(可用未压缩或低压缩率(ASTC 大块))。工程价值总结:显存翻倍容纳 + 带宽减半以上 + 加载更快(文件体积小),是移动端 3D 的必选项(尤其大纹理场景(地图/图集/材质贴图))。

考察移动端纹理内存的工程治理:格式矩阵(DXT/ETC/ASTC)与平台支持、压缩收益(内存/带宽)、离线压缩与特性检测加载,回答需给出"KTX2/Basis + 平台格式检测"的标准流程。

#

64. WebGL 的 Frustum Culling 在大型 3D 场景的渲染优化工程应用

WebGL 的 Frustum Culling 在大型 3D 场景中如何应用?

  • 视锥剔除的原理
  • Three.js 的默认剔除与包围体
  • 剔除治理(空间结构、LOD 联动)

Frustum Culling(视锥剔除):相机视锥(6 个平面)内的物体才提交渲染——对每个对象用包围体(包围盒/包围球)与视锥平面做相交测试(物体包围体完全在视锥外则跳过绘制),大幅减少 DrawCall 与 GPU 顶点处理(大型场景中多数对象不可见)。Three.js:默认开启(WebGLRenderer 的 frustumCulled(对象级)——对象在相机视锥外不绘制),基于对象 geometry 的包围盒(computeBoundingBox);工程要点:第一,包围体质量——包围体应尽可能紧贴对象(包围盒 vs 包围球(旋转友好但可能偏大)),动态更新(对象移动/缩放后 updateMatrixWorld 更新包围);第二,可见性层级——场景按空间结构组织(八叉树/BVH 粗剔除:先剔除节点再剔除子树对象——Three.js 无内置场景树剔除(场景图扁平),大规模场景需自定义空间索引或使用 GPU 剔除(WebGPU/WebGL2 的实例化剔除(实例级视锥测试在 GPU 完成(计算着色器(WebGPU)或 vertex 阶段变通)));第三,与 LOD 联动——近距剔除 + LOD 切换(视距分级)、小物体合并(多对象合批后剔除粒度变大(合批会损失剔除精度,权衡 DrawCall vs 剔除));第四,遮挡剔除(Occlusion Culling)——视锥内的被遮挡对象仍需提交(深度测试丢弃),GPU 遮挡查询(WebGL 无标准、WebGPU 有遮挡查询(occlusion query))或软件近似(遮挡物投影)用于超大场景;第五,测量——DrawCall 统计(可见对象数)、相机参数(fov/远近平面与剔除精度(far 过大导致大包围体误判))与场景遍历成本(万级对象的剔除循环本身有 CPU 成本——空间结构剪枝))。

考察大型场景的可见性治理:视锥剔除原理与包围体质量、Three.js 默认行为、空间索引与 GPU 剔除演进、剔除与合批/LOD 的权衡,回答需给出"层级剔除 + 预算治理"的工程方案。

#

65. Canvas 2D 在 HiDPI 屏幕的清晰渲染与 devicePixelRatio 适配的工程实践

Canvas 2D 在 HiDPI 屏幕如何清晰渲染?devicePixelRatio 适配的工程实践是什么?

  • DPR 与像素/物理像素的关系
  • 画布尺寸与样式尺寸的解耦
  • 缩放适配与重绘成本

HiDPI(DPR 2/3)屏幕下,Canvas 默认按 CSS 像素(逻辑)渲染——画布内部像素数 = 逻辑尺寸,被放大到物理像素时模糊(浏览器按 DPR 插值)。清晰渲染的适配:canvas.width/height 设置为"逻辑尺寸 × devicePixelRatio"(物理像素数),CSS 尺寸保持逻辑尺寸(样式宽高不变),绘制时 ctx.scale(dpr, dpr) 使所有坐标仍按逻辑单位(绘制代码不变),或直接按物理像素设计绘制逻辑(像素级图形(游戏/位图处理)用物理坐标系)。工程实践:第一,DPR 获取与监听——window.devicePixelRatio(缩放/跨屏移动时变化(matchMedia 监听 resolution 变化或 resize 事件重测));第二,重适配流程——尺寸变化(resize)或 DPR 变化时:重设 canvas.width/height(注意:重设会清空画布 + 重置上下文状态(需重新设置 transform 等))、重绘全部内容(必要场景离屏缓存分层(静态层按新 DPR 重建一次,动态层每帧绘制));第三,性能——DPR 3 的像素量是逻辑的 9 倍(填充率/绘制成本上升):权衡清晰度与性能(图表/UI 可接受 2x;大画布/粒子动画可设最大 DPR 上限(如 min(dpr, 2) 或按设备档位));使用 ImageBitmap 与离屏 canvas 在固定 DPR 渲染后缩放上屏;第四,圆角/边框模糊——Canvas 绘制线宽与半像素对齐(0.5px 偏移、奇数线宽)在 DPR 非整数时的处理;第五,组件化——把"DPR 适配"封装进图表/画布组件(resize 观察 + DPR 监听 + 重绘钩子),避免业务重复实现;测试——模拟 DPR(DevTools 设备模拟 + 缩放)验证清晰度与性能。

考察 HiDPI 画布工程的完整适配:物理像素与逻辑尺寸的解耦、缩放绘制、重适配生命周期、性能权衡与封装,回答需给出"DPR 感知组件"的实践。

#

66. WebGL 的 Occlusion Culling 在大型场景的现代工程应用

WebGL 的 Occlusion Culling 在大型场景中有哪些现代工程应用?

  • 遮挡剔除的原理与条件
  • 实现手段(GPU 查询/软件近似)
  • 适用场景与边界

Occlusion Culling(遮挡剔除)跳过"视锥内但被其他物体完全遮挡"的对象(视锥剔除管"看不见",遮挡剔除管"看得见但被挡住"),收益在深度复杂场景(城市、室内、密集植被)——被挡对象不提交绘制,大幅降低 overdraw 与 DrawCall。实现手段:第一,GPU 遮挡查询——WebGL 无标准查询扩展(EXT_disjoint_timer_query 只能计时)、WebGPU 提供 occlusion query(查询集记录绘制是否通过深度测试(任何片段通过 → 可见)),应用策略:上一帧查询结果决定本帧绘制(延迟一帧,快速物体有闪烁风险(可见性滞后))——"查询-渲染两遍":先渲染遮挡物(深度预填充)→ 查询低细节包围体 → 按结果渲染对象;第二,软件近似——CPU 侧用"遮挡体(大建筑物投影)的软件光栅化/层次 z-buffer"剔除(引擎级实现,Three.js 无内置);或保守近似(视野扇区剔除、按楼层/房间剔除(门户剔除(portal culling)));第三,混合——层次化:视锥剔除 → 遮挡剔除 → LOD/合并,配合"遮挡物选择"(大而近的物体当遮挡体)。工程边界:查询本身有 GPU 往返开销(批次与延迟)、快速移动/相机旋转时可见性抖动(用上一帧 + 阈值/保守包围体)、对象稀疏/空旷场景收益小(查询开销反而多余——按场景开关)、合批对象无法部分剔除(合批损失遮挡精度);应用场景——FPS/第三人称 3D 场景、大规模城市/植被渲染(配合实例化)、大场景漫游。现代趋势:WebGPU 的查询 + 计算着色器剔除(GPU 驱动剔除(gpu-driven))在 WASM/WebGPU 引擎(Babylon.js 支持部分)中落地。

考察遮挡剔除的工程认知:原理与收益条件、GPU 查询两遍法与滞后、软件近似、边界(开销/抖动/合批损失),回答需给出"视锥 → 遮挡 → LOD"的层级剔除框架。

#

67. Canvas 2D 的 filter 属性(CSS filter)在 Canvas 滤镜的工程价值

Canvas 2D 的 filter 属性(CSS filter)在 Canvas 滤镜中有何工程价值?

  • ctx.filter 的滤镜链
  • 与像素级操作(getImageData)的对比
  • 性能与工程应用

Canvas 2D 的 ctx.filter 支持 CSS 滤镜语法(blur/grayscale/brightness/contrast/saturate/sepia/hue-rotate/invert/opacity/drop-shadow 及组合),作用于"后续绘制操作"(fill/stroke/drawImage 的输出):声明式、浏览器(GPU 加速(部分滤镜))实现,代码量小(一行设置整段效果),适合"整体风格化"(灰度化图像展示、模糊背景层、明暗调整、阴影)。与像素级操作(getImageData/putImageData 手写卷积)对比:像素操作——逐像素完整控制(任意算法:自定义卷积、直方图、混合)、但要自己写循环(性能取决于 JS(大画布慢、内存拷贝大(getImageData 全量拷贝))、canvas 被污染(跨域图像)时 getImageData 抛错);filter——GPU 加速(现代浏览器)、免拷贝、不触发污染问题(绘制层面),但受限于 CSS 滤镜集合(无任意自定义核)。工程价值与取舍:第一,场景——简单/组合滤镜(图像预览滤镜、主题切换(灰度/模糊)、发光/阴影效果)用 ctx.filter(快、简洁);自定义算法(边缘检测、模糊半径定制、图像分析)用像素操作(或 WebGL/着色器(大规模/实时));第二,性能——filter 与绘制数量相关(每次绘制应用滤镜),大量小绘制逐次滤镜开销(先离屏一次滤镜再 drawImage)、blur 大半径成本高(GPU 内存/带宽)、移动端滤镜支持差异(部分滤镜不支持/降级);第三,重置——ctx.filter = 'none' 恢复(状态机管理(save/restore)),组合链顺序影响结果;第四,工程注意——跨域图像绘制 + 滤镜不触发污染(可放心用)、滤镜与 shadow 等状态组合、测试矩阵(浏览器滤镜实现差异(drop-shadow 参数单位))。总结:滤镜诉求优先 ctx.filter(声明式 + GPU),自定义算法再上像素/着色器。

考察 Canvas 滤镜的两种范式:ctx.filter 的声明式 GPU 滤镜 vs getImageData 的像素级控制,价值(简洁/性能/无污染)与边界(滤镜集合、逐绘制开销),回答需给出按"需求类型"的分层选型。

#

68. WebGL 与 WebGPU 在着色器动画(GLSL vs WGSL)

WebGL(GLSL)与 WebGPU(WGSL)在着色器动画上有何工程取舍?

  • GLSL 与 WGSL 的语法与能力差异
  • 管线/资源的编程模型差异
  • 迁移与共存策略

语言与能力:GLSL(WebGL 1/2 的着色器语言)——类 C 语法、基于版本(#version 100/300 es)、隐式全局状态(uniform 全局绑定、纹理单元)、能力受 WebGL 限制(无计算着色器(WebGL2 无)、UBO 有限、无存储缓冲);WGSL(WebGPU 的着色器语言)——类型严格(显式类型/地址空间(function/private/workgroup/uniform/storage/handle)、指针与引用语义(& 地址空间)、内建函数命名空间)、支持计算着色器(workgroup 共享内存、原子操作)、统一资源模型(BindGroup/绑定布局显式声明)、更现代(模块化、验证严格(编译期检查多))。编程模型差异:WebGL——状态机(当前 program/绑定纹理/属性指针),绘制命令隐式依赖状态;WebGPU——管线对象(RenderPipeline 含 shader + 顶点布局 + 混合状态,创建后不可变(渲染状态预先固化,减少运行时状态切换))、录制(RenderPass)后提交(Queue)、资源绑定显式(BindGroup 一次绑定一组)。着色器动画的工程取舍:第一,新项目——WebGPU 优先(计算着色器做粒子/物理/后处理、更细粒度控制、性能上限高),但浏览器支持(Chrome/Edge 稳定、Safari 26+ 默认、Firefox 推进)需特性检测降级;第二,存量 WebGL 项目——迁移成本(GLSL → WGSL 重写 + 管线重构(从状态机到管线对象)),可先用 WebGL(稳定兼容)再按热点迁移(计算密集粒子/后处理上 WebGPU);第三,工具链——WGSL 的编辑器/调试(Tint 校验器、Naga)在成熟中,WebGL 生态(shader 教程/库)更成熟;第四,抽象层——Three.js 的 WebGPURenderer(TSL 生成 GLSL/WGSL 双后端)与 Babylon.js 的引擎层统一(一套材质语法多后端输出),让业务少写底层着色器;第五,测试矩阵——双后端(GLSL/WGSL 变体)的兼容验证(浮点一致性、精度差异)。结论:能力与性能选 WebGPU/WGSL(新项目/热点),兼容与生态选 WebGL(存量/全平台),框架抽象层降低迁移成本。

考察两大 GPU API 的工程对比:语言(GLSL 隐式 vs WGSL 显式类型)、模型(状态机 vs 管线/绑定)、能力(计算着色器)、迁移策略(抽象层 + 特性检测),回答需给出"新用 WebGPU、存量分层迁移"的路径。

#

69. 触觉反馈(Vibration API)与拖拽反馈

触觉反馈(Vibration API)在拖拽等交互中有哪些工程应用?

  • Vibration API 的用法与权限
  • 触觉反馈的场景设计(拖拽吸附、到达边界)
  • 兼容性与降级

Vibration API(navigator.vibrate(pattern))让移动设备振动:pattern 为毫秒数组([200] 振 200ms、[100,50,100] 振-停-振、[0] 停止),页面处于前台 + 用户已交互(激活)才生效,权限由浏览器/系统管理(部分设备在"省电/勿扰"下静默)。拖拽反馈的触觉设计:拖拽开始(抓取成功振动 10-20ms)、吸附/对齐(到达网格/插槽时短振)、拖拽到可放置区(hover 振动)、放置成功(双脉冲(确认感))、放置失败/越界(长/低强度(pattern 区分));触觉与视觉/声音反馈协同(多模态反馈:视觉(高亮)+ 触觉(振动)+ 声音(可选))。工程要点:第一,场景——移动端原生手感(iOS Safari 不支持 vibrate(navigator.vibrate 未实现)——Android/Chrome 支持),需要降级(无振动则仅视觉反馈,功能不受影响);第二,频率与强度——避免过度振动(可感知性 vs 烦扰,间隔/次数控制(拖拽过程不每帧振(阈值触发(如吸附点变化时))));第三,与手势状态机集成——在拖拽状态机的"事件点"(start/吸附/放置/取消)统一派发振动策略(策略表(设计令牌化:强度/模式按交互类型));第四,系统感知——页面可见性(后台不振)、用户偏好(无障碍(减少动态效果/振动偏好));第五,实现——navigator.vibrate 存在性检测('vibrate' in navigator)再调用、pattern 参数验证;替代——WebHaptics(振动/触觉 API 的演进提案)关注未来。工程价值:反馈即时性(触觉在指尖、无需视线)、复杂交互(拖拽/长按/双指)的操作确认、表单/游戏化反馈;设计原则——"触觉是反馈补充而非必需",核心可用性不依赖振动。

考察触觉反馈的交互工程:Vibration API 语义与平台差异(iOS 缺失)、拖拽状态机的触觉事件点设计、多模态协同与降级,回答需给出"策略表 + 降级"的实现框架。

#

70. SVG SMIL 动画 vs CSS @keyframes vs Web Animations API 的取舍

SVG SMIL 动画、CSS @keyframes 与 WAAPI 三者如何取舍?

  • 三种动画范式的定位
  • 能力/性能/兼容对比
  • 分层选型

三者定位:SVG SMIL(SVG 内 / /)——SVG 原生声明动画(路径动画(animateMotion)、形状变形(d 补间)、SVG 属性动画),全浏览器支持但性能一般(主线程、无合成器优化)、调试与测试工具少、与 Web 动画生态(CSS/JS)割裂;CSS @keyframes(CSS 动画/过渡)——样式表声明动画(transform/opacity 合成器执行、性能好、渐进增强、易维护(设计令牌/主题)、支持 scroll 驱动(CSS 时间线)等新特性),但不能直接动画非 CSS 属性(SVG 的 d?部分支持(d 属性动画(Chromium/FF))与 SMIL 的任意 SVG 属性(fill-rule 等));WAAPI(Element.animate)——JS 命令式创建/控制动画(动态参数、播放控制(seek/pause/reverse)、时间线、composite、keyframes 对象复用),执行在浏览器动画引擎(同 CSS 性能(合成器属性)),弥补 CSS 的"运行时控制"短板。取舍框架:第一,默认——CSS(静态/声明式动画:进入、悬停、微交互,性能与维护最优);第二,需要 JS 控制(动态数据、进度绑定、事件编排)——WAAPI(或库(GSAP/Motion One)封装);第三,SVG 专属动画(路径运动(motion path 用 CSS offset-path 更现代、SMIL animateMotion 兼容面)、SVG 属性(d 变形(CSS/WAAPI 均可(d 动画兼容性检查)、fill/stroke 动画))——SMIL 只在"无 CSS 替代 + 需内嵌 SVG 自包含"时用(老环境/矢量编辑器导出);第四,跨端/框架——库(Framer Motion、@vueuse/motion)基于三者抽象;性能注意——全部受 prefers-reduced-motion 控制(CSS/WAAPI 可媒体查询、SMIL 手动处理)。现代结论:CSS 为主、WAAPI 增强、SMIL 备选(特定 SVG 场景))。

考察动画三范式的分工:定位(声明式 CSS、命令式 WAAPI、SVG 原生 SMIL)、能力与性能差异、分层选型与 reduced-motion,回答需给出"CSS 优先 + WAAPI 控制 + SMIL 特定场景"的共识。

#

71. CSS Grid grid-template-areas 在 RTL/LTR 国际化的取舍

CSS Grid 的 grid-template-areas 在 RTL/LTR 国际化中如何取舍?

  • grid-template-areas 的书写顺序与方向
  • RTL 下的自动镜像行为
  • 国际化布局的工程实践

grid-template-areas 以"ASCII 图"声明布局("header header" "nav main" "aside footer" 等,每行字符串对应一行轨道),网格项用 grid-area: 名称放置;关键语义——网格方向与书写模式(writing-mode/direction)相关:文档方向(direction: rtl)下网格列自动镜像(第一列在右),grid-template-areas 的字符串顺序按"逻辑顺序"(书写方向)解释——因此基于 areas 的布局在 RTL 下自动对称镜像(无需改布局代码),这是 areas 相对"显式数字行列(grid-column: 1/3)"的主要国际化优势(数字定位不随方向镜像,需手工处理 RTL)。工程取舍:第一,默认行为——areas 的镜像自动(列按方向翻转),行方向(row)不受影响;"物理顺序"(不希望镜像的内容(如时间轴方向)需显式处理(direction 局部覆盖或逻辑属性);第二,逻辑属性配合——容器与网格项的 margin/padding 用逻辑属性(margin-inline-start 等)随方向切换、grid-area 命名与语义化(header/main/aside 名称在 RTL 不失效);第三,边界——显式 grid-column/grid-row 数值 + areas 混用时的镜像不一致(列号不镜像)、嵌套网格的继承方向、justify/align 的物理/逻辑(justify-items 在 RTL 下仍按行轴(水平),需用逻辑对齐(text-align 的 start/end 语义));第四,测试——RTL 布局回归(截图对比(LTR/RTL 双基线)、自动测试(切换 dir 后布局断言))、阿拉伯语/希伯来语的真实内容测试(字符串长度与换行);第五,实践——设计系统用 areas + 逻辑属性作为"方向无关布局"的标准(避免每组件写两套 RTL 样式),仅个别视觉元素(箭头、图标方向)用物理处理。取舍总结:areas 天然 RTL 友好(镜像自动),数字定位需手工;复杂嵌套/精确像素控制场景评估两种的混合成本)。

考察国际化的网格布局工程:areas 的方向镜像语义、逻辑属性配合、数值定位的 RTL 陷阱、双方向测试,回答需给出"areas + 逻辑属性 + 双基线测试"的方案。

#

72. Cascade Layers(@layer) 在 RTL/LTR 国际化的取舍

Cascade Layers(@layer)在 RTL/LTR 国际化中有何取舍?

  • @layer 的级联分层机制
  • 与 CSS 变量/逻辑属性的配合
  • 国际化样式治理

Cascade Layers(@layer)显式声明样式层级(@layer reset, base, components, utilities; 层间按声明顺序决定优先级(后声明层优先,替代 !important 与选择器权重博弈)——样式来源(浏览器默认 → 用户 → 作者)内再分作者层,层内按选择器权重);用途——治理大型样式表的优先级(第三方样式、设计系统分层(reset/token/组件/工具类)、覆盖策略(utility 层最后(覆盖组件层)))。RTL/LTR 国际化关联:第一,层与方向无关(层管优先级不管方向),但层的"内容组织"影响国际化维护——把方向相关样式(逻辑属性(margin-inline)、方向媒体查询(dir 属性选择器 [dir='rtl']))收敛在特定层(如 base 的方向适配层/rtl 覆写层),RTL 覆写在"后声明层"(优先级保证),避免 !important 扩散;第二,与 CSS 变量配合——设计令牌(颜色/间距/方向感知令牌)在 token 层定义(方向感知的间距(--space-inline)由逻辑属性表达(无方向分支),仅在确有"物理方向差异"时(图标镜像(transform: scaleX(-1)))在 rtl 层覆写;第三,优先级治理收益——无 @layer 时 RTL 覆写靠"更高权重选择器/!important"(脆弱、难维护),@layer 让"基础层(LTR 默认) → 方向层(rtl 覆写)"的覆盖关系声明化(层序即优先级);第四,工程实践——组件库分层(reset/tokens/base(逻辑属性写基础)/components/direction 层(dir 覆写)/utilities),RTL 测试(双方向快照)、@layer 兼容(现代浏览器支持(Chrome 99+/FF 97+/Safari 15.4+),旧浏览器降级(无层 = 全部同一层(声明顺序语义保留)——需注意降级时的顺序敏感性)。取舍:@layer 解决"覆盖策略",国际化受益在于"方向覆写层"的声明式治理(减少 !important 与权重 hack),配合逻辑属性实现"默认 LTR 无分支 + 方向层定向覆写"))。

考察级联治理与方向适配的结合:@layer 的优先级语义、方向覆写层的组织、与逻辑属性/令牌的配合、兼容降级,回答需给出"基础逻辑属性 + 方向层覆写"的治理模型。

#

73. 触摸滚动与拖拽的冲突解决,方向锁定(directional lock)与阈值检测的工程实现

触摸滚动与拖拽的冲突如何解决?方向锁定与阈值检测如何实现?

  • 冲突的本质(浏览器手势 vs 应用手势)
  • 方向锁定的判定算法
  • 阈值检测与状态机

冲突本质:触摸起点后,浏览器"滚动/缩放"与应用"拖拽/滑动"都等待手势——需要仲裁:判定为应用手势则阻止滚动(touch-action + 捕获)、判定为滚动则取消应用手势(pointercancel)。实现手段:第一,阈值检测(threshold)——pointermove 累积位移超过阈值(如 5-10px)才"激活"手势(拖拽/滑动开始),之前保持"候选"状态(既不动滚也不动拖——实际是触摸点未超过阈值时浏览器也不确定);超阈值的瞬间决定归属(激活后 setPointerCapture 接管);第二,方向锁定(directional lock)——触摸移动的首次显著位移方向(水平 vs 垂直,比较累积 |dx| 与 |dy|(如 |dx| > |dy| × 1.2 判定水平))锁定手势类型:水平手势(横向拖拽/滑动删除)→ 应用接管(touch-action: pan-y(垂直滚动留给浏览器));垂直手势 → 浏览器滚动(应用放弃(不捕获));锁定后忽略垂直分量(水平拖拽中只取 dx(dy 不再影响))避免抖动;第三,touch-action 配合——方向锁定需要浏览器允许"部分手势":横向拖拽容器 touch-action: pan-y(浏览器只管垂直滚动,水平 pointermove 稳定派发);捏合等复杂手势 touch-action: none 全接管;第四,状态机——IDLE → TOUCHING(记录起点)→ 判定(位移/时间)→ DRAGGING(锁定方向、捕获)或 SCROLLING(释放给浏览器)→ 结束(up/cancel:DRAGGING 触发放置/取消回滚,SCROLLING 无操作);移动中"方向变化"(如先横后纵)按锁定规则忽略/重新判定(阈值锁定一次(可配置));第五,实现细节——用 Pointer Events(pointerId 追踪)+ touch-action 声明,避免 touch 事件的兼容分裂;pointercancel(浏览器抢走)时回滚候选/拖拽状态;测试覆盖(横竖滑动、先横后竖、快速滑动(速度判定(fling))、多指)。框架参考:Framer Motion 的 drag(axis 限制(dragDirectionLock)、dragSnapToOrigin)、dnd-kit 的 activator 与碰撞检测。工程要点:阈值与锁定参数令牌化(不同组件(列表/面板/轮播)不同手感)、性能(移动处理在 rAF 内、避免每事件重排)。

考察触摸手势仲裁的实现工程:阈值激活与方向锁定的判定算法、touch-action 声明与捕获接管、状态机与 cancel 回滚、参数令牌化,回答需给出完整的仲裁状态机设计。