Dawn 商品媒体:图库、变体联动与放大浏览
本文基于 Dawn v15.3.0,源码固定到 ecb06c3(核验于 2026-09-29)。结论限于此提交,不表示其他版本行为相同;本篇为静态源码分析,未完成店铺运行验证。Dawn 上游已发布 v16.0.0(2026-08-10,GitHub Releases 核对),本库 Dawn 正文统一以 v15.3.0 为基线,升级前需重新核对。
对应哪些通用模式
覆盖 Media Gallery、Modal 与变体媒体模块在 Dawn 商品页上的实现。商品页整体结构见 Dawn 架构,本篇只展开媒体这一条链。
入口与文件
| 文件 | 职责与输出条件 |
|---|---|
| sections/main-product.liquid | 计算 variant_images,渲染图库与 Modal,声明图库设置;product.media.size > 0 才加载 product-modal.js、media-gallery.js |
| snippets/product-media-gallery.liquid、snippets/product-thumbnail.liquid | media-gallery 外壳、幻灯列表、翻页按钮、缩略图;单个媒体的放大触发器与视频 / 3D 海报 |
| snippets/product-media-modal.liquid、snippets/product-media.liquid | 放大浏览的 product-modal 及其中的大图 / 视频 / 3D,始终渲染 |
| assets/media-gallery.js、assets/product-modal.js、assets/product-model.js | 缩略图与播报;Modal 定位;3D 与 AR(有 3D 才加载) |
| assets/global.js | ModalDialog、ModalOpener、DeferredMedia、SliderComponent、trapFocus、pauseAllMedia |
| assets/product-info.js、assets/magnify.js | 变体切换后的 updateMedia;image_zoom == 'hover' 时的悬停放大 |
数据从哪来
- 媒体来自
product.media(同一列表含image、video、external_video、model),用到media_type、alt、preview_image、aspect_ratio,外部视频另用host、external_id。 - 变体与媒体的唯一关联是
selected_or_first_available_variant.featured_media:图库先输出它,再输出其余媒体。此版本没有「变体专属媒体集合」的数据结构。 variant_images取product.images | where: 'attached_to_variant?', true | map: 'src'。设置hide_variants开启时,循环跳过这些图片(当前变体的 featured 除外),顾客看到「当前变体图 + 未关联任何变体的图」。schema 默认false,templates/product.json 设为true。- 图库设置:
gallery_layout(stacked / columns / thumbnail / thumbnail_slider)、media_size、media_position、constrain_to_viewport、media_fit、mobile_thumbnails(columns / show / hide)、image_zoom(lightbox / hover / none)、hide_variants、enable_video_looping。 - 变体切换的数据是同一商品 URL 的 Section Rendering 响应:
<商品 URL>?section_id=…&option_values=…。
交互怎样运行
图库。 条目是 li.slider__slide,slider-component 用 scroll-snap 横向滚动,按钮调用 scrollTo。缩略图点击进入 MediaGallery.setActiveMedia:切换 is-active、滚动到该媒体、pauseAllMedia() 后对 .deferred-media 调 loadContent(false)、更新缩略图 aria-current。滚动触发的 slideChanged(防抖 500ms)反向同步缩略图。
变体联动。 product-info 先 abort() 上一个请求再拉取新分区 HTML,晚返回的旧请求不会覆盖新选择。updateMedia 仅在 variant.featured_media.id 存在时执行:插入响应中新增的 li、删除响应里没有的、按响应顺序重排,再 setActiveMedia(id, true) 把该媒体及其缩略图移到第一位。缩略图列表不按响应重建,靠 CSS 规则 .thumbnail-list_item--variant:not(:first-child)(display: none)隐藏其余变体缩略图;Modal 内容整体用响应的 innerHTML 替换。变体没有 featured 媒体时图库保持原状。
放大 Modal。 主媒体包在 modal-opener 里,真实触发元素是 button.product__media-toggle(aria-haspopup="dialog")。ProductModal.show 继承 ModalDialog.show:body 加 overflow-hidden、设置 open、trapFocus 到 [role="dialog"]、暂停所有媒体,再滚动到被点击媒体。关闭:关闭按钮、keyup Escape、鼠标 pointerup 落在视频 / 3D 之外;焦点还给触发按钮。image_zoom 为 hover 时加载 magnify.js,在支持悬停的设备上隐藏 Modal 触发器(视口 ≤749px 仍显示),点击图片生成背景图覆盖层;none 用 CSS 隐藏图片的触发器。
视频与 3D。 非图片媒体先输出海报 button 与 <template>,模板里才是 media_tag / external_video_tag 产出的元素(视频 preload: 'none')。点击海报由 DeferredMedia.loadContent 克隆模板内容、追加 video / model-viewer / iframe 并聚焦。3D 由 ProductModel 通过 Shopify.loadFeatures 加载 [email protected](该函数在固定提交的主题文件中只有调用点,定义来自 Shopify 平台脚本),AR 按钮走 shopify-xr;有 3D 时才输出 model-viewer-ui.css 的 CDN 链接。
与通用契约的一致与差距
| 通用要求 | Dawn 现状 |
|---|---|
| 缩略图与控制可用键盘、选中态非仅颜色 | 缩略图与翻页是原生 button,选中态写 aria-current 并有聚焦样式;图库无方向键处理(media-gallery.js 无键盘事件),靠 Tab |
| 替代文本按用途 | 缩略图传 alt: media.alt,Modal 大图用 media.alt;主图 image_tag 未显式传 alt,是否自动带出未核验;视频 / iframe 名称由 media_tag 生成,未核验 |
| 切换后对辅助技术可理解 | role="status" 的 GalleryStatus 在图片 onload 后播报「Image N is now available」,仅限图片;视频与 3D 切换无播报 |
| 视频不强制自动播放 | 首次需点海报,但点缩略图或变体切换到视频时,playActiveMedia 调 loadContent(false),模板含 autoplay: true,会加载并按参数尝试自动播放(不移动焦点;浏览器自动播放策略未核验)。图库与 DeferredMedia 的 JS 中无 prefers-reduced-motion 判断 |
| Modal:焦点进入、循环、Escape、返还 | 自定义元素加 role="dialog" aria-modal="true",trapFocus 与返还焦点已实现;全仓库未使用原生 <dialog> 或 inert(搜索词 inert、showModal、<dialog,范围 assets、layout、sections、snippets),背景靠遮罩与 overflow-hidden;Escape 监听 keyup |
| 单项与零项退化 | media_count == 1 或 limit == 1 时隐藏移动端翻页按钮;product.media.size == 0 时布局类为 product--no-media 且不加载图库脚本 |
| 加载优先级与占位 | 首项 lazy_load: false,其余 loading="lazy";--ratio 变量预留比例 |
| 无 JS | 图片仍在列表中可滚动;视频与 3D 在 <template> 内,无 JS 不显示。未在浏览器验证 |
| 变体切换失败 | 请求异常仅 console.error,未见用户可见提示;AbortError 被忽略 |
两处细节:product-media.liquid 的 AR 按钮 data-shopify-title 是字面量 "title"(product-thumbnail.liquid 用 product.title | escape);featured-product 复用同一图库但传 limit: 1:不渲染缩略图,移动端隐藏翻页按钮。
定制入口与风险
商家可调整上文的图库设置;hide_variants 与 gallery_layout 组合会改变可见条目,需同时检查缩略图与 Modal。耦合点:li[data-media-id] 与缩略图 data-target 被 updateMedia、setActiveMedia、ProductModal 共同依赖;Modal 会被 ModalDialog 移到 body 下,样式不能假设它在分区内;Quick Add 弹窗会移除 product-modal 并把图库改成 role="presentation"(assets/quick-add.js),改结构后需连带回归。
建议的验证范围
本篇均未执行:
- 选择 featured 图不同的变体并快速连点,核对主图、缩略图、Modal、URL 的
variant,再加购确认行项目。 hide_variants开 / 关下检查缩略图与计数总数。- 键盘走完「Tab 到缩略图 → 打开 Modal → Escape → 焦点回触发按钮」,并用屏幕阅读器听
GalleryStatus。 - 含视频、外部视频、3D 的商品:点海报、切缩略图、切变体,观察自动播放、暂停与 3D 不支持时的表现。
- 开启系统减少动态、关闭 JavaScript、放慢网络,核对上述行为与首屏比例。