通过 Bilibili 观看本期 Shopify 教程通过 Youtube 观看本期 Shopify 教程
前言
短视频展示模块是当今电商网站,尤其是 Shopify 网站上一种非常流行且高效的视觉营销工具。它的核心目的是将社交媒体上引人入胜的短视频体验,无缝集成到在线购物流程中。
其用途和价值主要包括以下几个方面:
动态化产品展示
例如:展示产品如何工作、展示产品实际效果、展示产品材质和细节、展示用户反馈或使用评价,完全超越静态图片。
解决用户 “这个产品实际用起来怎么样?” 的核心疑问,降低不确定性,提高订单转化。
建立社会认同和信任
视频,尤其是用户生成内容 (UGC) 的视频,具有极强的真实性和说服力。
- 客户评价视频:真实客户在使用产品后拍摄的视频,是比任何文字好评都更有力的推荐
- 开箱视频 (Unboxing):展示客户收到产品时的完整体验,营造一种期待感和满足感。
- 网红或 KOL 合作视频:利用影响力人物的背书,快速建立品牌信誉。
打消潜在客户的疑虑,利用口碑效应促进销售。
讲述品牌故事,增加互动
除了以上两个方面之外,这个视频模块还可以用来展示品牌故事、幕后花絮、甚至生产视频。
同时视频也会更加吸引眼球,提升客户互动的同时增加页面停留时间。
此次分享的功能采用直接选择 Shopify 店铺中上传的视频的方式,支持上传任意视频到店铺后台进行使用,自带 Shopify CDN,且无需像 YouTube 嵌入那样引入大量第三方代码。

增强版主题
这个功能原本是我最近添加到增强版主题中的一项更新,不过本篇教程也提供免费版本,这样既照顾了使用增强版主题的用户的权益,也让免费用户可以体验到类似或者说大致的效果,结合视频演示就能知道增强版主题中完整功能的使用效果。
支持设置是否自动播放、视频卡片数量没有限制、手机端支持滑动查看更多视频。
如果你想进一步了解增强版主题,可以查看 主题介绍页面、更新日志、主题使用手册。

免费版本
参考视频演示,创建一个 Section 文件:
video-carousel.liquid
粘贴以下代码并保存之后,即可在主题编辑器中添加模块进行使用(详见视频演示)。
<section class="video-reel-{{ section.id }}"
style="
--pt: {{ section.settings.padding_top | default: 0 }}px;
--pb: {{ section.settings.padding_bottom | default: 0 }}px;
--mt: {{ section.settings.margin_top | default: 0 }}px;
--mb: {{ section.settings.margin_bottom | default: 0 }}px;
"
>
<style>
.video-reel-{{ section.id }}{
width:100%;
margin: var(--mt) auto var(--mb);
max-width: {{ section.settings.max_width | default: 1200 }}px;
padding: var(--pt) 12px var(--pb);
--reel-gap: {{ section.settings.card_gap | default: 20 }}px;
--h-size-m: {{ section.settings.heading_size_mobile | default: 22 }}px;
--h-size-d: {{ section.settings.heading_size_desktop | default: 28 }}px;
--s-size-m: {{ section.settings.subheading_size_mobile | default: 14 }}px;
--s-size-d: {{ section.settings.subheading_size_desktop | default: 16 }}px;
--header-align: {{ section.settings.heading_align | default: 'center' }};
--header-spacing: {{ section.settings.header_spacing | default: 16 }}px;
--vt-size-m: {{ section.settings.video_title_size_mobile | default: 14 }}px;
--vt-size-d: {{ section.settings.video_title_size_desktop | default: 14 }}px;
}
.video-reel-{{ section.id }} .reel-header{
margin-bottom: var(--header-spacing);
text-align: var(--header-align);
}
.video-reel-{{ section.id }} .reel-heading{
margin:0; line-height:1.15; color:#000; font-weight:700;
font-size: var(--h-size-m);
}
.video-reel-{{ section.id }} .reel-subheading{
margin:.25rem 0 0; line-height:1.4; color:#000; font-weight:500;
font-size: var(--s-size-m);
}
@media (min-width:1024px){
.video-reel-{{ section.id }} .reel-heading{ font-size: var(--h-size-d); }
.video-reel-{{ section.id }} .reel-subheading{ font-size: var(--s-size-d); }
}
.video-reel-{{ section.id }} .reel-track{
display:flex; gap:var(--reel-gap);
overflow-x:auto; -webkit-overflow-scrolling:touch;
scroll-snap-type:x mandatory; padding-bottom:6px;
justify-content:flex-start;
cursor:grab; user-select:none;
}
@media (min-width:1024px){
.video-reel-{{ section.id }} .reel-track{ justify-content:center; }
}
.video-reel-{{ section.id }} .reel-track::-webkit-scrollbar{ display:none; }
.video-reel-{{ section.id }} .reel-card{
scroll-snap-align:center; flex:0 0 auto; width:146px;
}
.video-reel-{{ section.id }} .video-box{
position:relative; width:100%;
height:207px;
border-radius:14px; overflow:hidden; background:#111; display:block;
}
.video-reel-{{ section.id }} .video-box img{ width:100%; height:100%; object-fit:cover; display:block; }
.video-reel-{{ section.id }} .reel-play{
position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
width:52px; height:52px; border-radius:9999px;
background:rgba(255,255,255,.94);
display:grid; place-items:center; border:none; cursor:pointer;
box-shadow:0 1px 6px rgba(0,0,0,.25);
}
.video-reel-{{ section.id }} .reel-play svg{ width:22px; height:22px; fill:#000; }
.video-reel-{{ section.id }} .video-title{
text-align:center; color:#000; font-weight:600;
margin-top:12px; font-size:14px;
}
.reel-lightbox[hidden]{ display:none !important; }
.reel-lightbox{
position:fixed; inset:0; z-index:9999; background:rgba(0,0,0,.7);
display:grid; place-items:center; padding:16px;
}
.reel-lightbox .reel-lightbox-inner{
position:relative;
height:min(90vh, 800px);
aspect-ratio: 9 / 16;
display:grid; place-items:center;
max-width:90vw;
}
.reel-lightbox video,
.reel-lightbox .shopify-video{
width:100%; height:100%; object-fit:cover; border-radius:12px; background:#000;
}
.reel-lightbox .close-btn{
position:absolute; top:8px; right:8px; width:40px; height:40px;
border-radius:9999px; display:grid; place-items:center; border:none; cursor:pointer;
background:#fff; box-shadow:0 1px 6px rgba(0,0,0,.2); z-index:2;
}
.reel-lightbox .close-btn svg{ width:18px; height:18px; }
.reel-lightbox .unmute-chip{
position:absolute; left:10px; bottom:10px;
padding:8px 12px; border:0; border-radius:9999px;
background:rgba(0,0,0,.75); color:#fff; font-weight:600;
display:flex; align-items:center; gap:8px; cursor:pointer;
box-shadow:0 2px 8px rgba(0,0,0,.3);
}
.reel-lightbox .unmute-chip svg{ width:16px; height:16px; fill:#fff; }
</style>
{% if section.settings.heading != blank or section.settings.subheading != blank %}
<header class="reel-header">
{% if section.settings.heading != blank %}<h2 class="reel-heading">{{ section.settings.heading }}</h2>{% endif %}
{% if section.settings.subheading != blank %}<p class="reel-subheading">{{ section.settings.subheading }}</p>{% endif %}
</header>
{% endif %}
<div class="reel-track" id="reel-track-{{ section.id }}">
{% for block in section.blocks %}
{% if block.type == 'video_card' %}
{% assign poster = '' %}
{% if block.settings.poster_image != blank %}
{% assign poster = block.settings.poster_image | image_url: width: 900 %}
{% elsif block.settings.video != blank and block.settings.video.preview_image != blank %}
{% assign poster = block.settings.video.preview_image | image_url: width: 900 %}
{% endif %}
<div class="reel-card" {{ block.shopify_attributes }}>
<button class="video-box" type="button" data-modal="video-modal-{{ block.id }}" aria-label="Play {{ block.settings.title | escape }}">
{% if poster != blank %}<img src="{{ poster }}" alt="">{% endif %}
<span class="reel-play" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
</span>
</button>
{% if block.settings.title != blank %}<div class="video-title">{{ block.settings.title }}</div>{% endif %}
</div>
<div class="reel-lightbox" id="video-modal-{{ block.id }}" hidden>
<div class="reel-lightbox-inner">
<button class="close-btn" data-close aria-label="Close video">
<svg viewBox="0 0 24 24"><path d="M18.3 5.7 12 12l6.3 6.3-1.4 1.4L10.6 13.4 4.3 19.7 2.9 18.3 9.2 12 2.9 5.7 4.3 4.3 10.6 10.6 16.9 4.3z"/></svg>
</button>
{% if block.settings.video != blank %}
{{ block.settings.video | media_tag: autoplay: true, controls: true, loop: false, playsinline: true, preload: 'metadata' }}
{% elsif block.settings.video_url != blank %}
<video autoplay muted playsinline controls preload="metadata">
<source src="{{ block.settings.video_url }}" type="video/mp4">
</video>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</div>
<script>
(function(){
const root = document.currentScript.closest('.video-reel-{{ section.id }}');
if(!root) return;
function openIntoBody(modal){
const live = modal.cloneNode(true);
live.id = modal.id + '--live';
live.hidden = false;
document.body.appendChild(live);
const v = live.querySelector('video');
function showUnmute(){
if (live.querySelector('.unmute-chip')) return;
const chip = document.createElement('button');
chip.type = 'button';
chip.className = 'unmute-chip';
chip.setAttribute('aria-label', 'Turn sound on');
chip.innerHTML = '<svg viewBox="0 0 24 24"><path d="M3 10v4h4l5 5V5L7 10H3zM16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.06A4.49 4.49 0 0 0 16.5 12zm2.5 0c0 3.04-1.72 5.64-4.25 6.92v-2.2a5.997 5.997 0 0 0 0-9.44v-2.2C19.28 6.36 21 8.96 21 12z"/></svg><span>Tap for sound</span>';
(live.querySelector('.reel-lightbox-inner') || live).appendChild(chip);
chip.addEventListener('click', () => {
if (!v) return;
try {
v.muted = false;
const p = v.play?.();
if (p && p.catch) p.catch(()=>{});
chip.remove();
} catch(e){}
});
}
if (v) {
try {
v.playsInline = true;
v.muted = false;
let p = v.play?.();
if (p && p.catch){
p.catch(() => {
try { v.muted = true; v.play?.(); } catch(e){}
showUnmute();
});
}
} catch(e) {
try { v.muted = true; v.play?.(); showUnmute(); } catch(e2){}
}
}
function close(){
try{ if(v){ v.pause(); } }catch(e){}
live.remove();
document.documentElement.style.overflow = '';
}
live.addEventListener('click', e=>{
if (e.target === live || e.target.closest('[data-close]')) close();
});
const onKey = (e)=>{ if(e.key === 'Escape'){ close(); document.removeEventListener('keydown', onKey);} };
document.addEventListener('keydown', onKey);
document.documentElement.style.overflow = 'hidden';
}
root.querySelectorAll('.video-box').forEach(btn=>{
btn.addEventListener('click', ()=>{
const m = document.getElementById(btn.getAttribute('data-modal'));
if(!m) return;
openIntoBody(m);
});
});
const track = root.querySelector('#reel-track-{{ section.id }}');
const setStart = () => {
if (window.matchMedia('(max-width:1023px)').matches) track.scrollLeft = 0;
};
setStart(); window.addEventListener('resize', setStart);
let isDown = false, startX = 0, startLeft = 0;
const onDown = (e) => {
isDown = true;
startX = (e.touches ? e.touches[0].pageX : e.pageX);
startLeft = track.scrollLeft;
};
const onMove = (e) => {
if(!isDown) return;
const x = (e.touches ? e.touches[0].pageX : e.pageX);
track.scrollLeft = startLeft - (x - startX);
e.preventDefault();
};
const onUp = () => { isDown = false; };
track.addEventListener('mousedown', onDown);
track.addEventListener('mousemove', onMove);
track.addEventListener('mouseup', onUp);
track.addEventListener('mouseleave', onUp);
track.addEventListener('touchstart', onDown, { passive: true });
track.addEventListener('touchmove', onMove, { passive: false });
track.addEventListener('touchend', onUp);
track.addEventListener('dragstart', (e)=>e.preventDefault());
})();
</script>
</section>
{% schema %}
{
"name": "Video Reel (Carousel)",
"settings": [
{ "type": "text", "id": "heading", "label": "Heading", "default": "Watch more" },
{ "type": "text", "id": "subheading", "label": "Subheading" },
{ "type": "select", "id": "heading_align", "label": "Heading/Subheading alignment", "default": "center",
"options": [ { "value": "left", "label": "Left" }, { "value": "center", "label": "Center" }, { "value": "right", "label": "Right" } ]
},
{ "type": "range", "id": "heading_size_mobile", "label": "Heading size (mobile, px)", "default": 22, "min": 10, "max": 60, "step": 1 },
{ "type": "range", "id": "heading_size_desktop", "label": "Heading size (desktop, px)", "default": 28, "min": 10, "max": 80, "step": 1 },
{ "type": "range", "id": "subheading_size_mobile", "label": "Subheading size (mobile, px)", "default": 14, "min": 8, "max": 40, "step": 1 },
{ "type": "range", "id": "subheading_size_desktop","label": "Subheading size (desktop, px)","default": 16, "min": 8, "max": 48, "step": 1 },
{ "type": "range", "id": "header_spacing", "label": "Space below heading (px)", "default": 16, "min": 0, "max": 64, "step": 1 },
{ "type": "range", "id": "card_gap", "label": "Gap between cards (px)", "default": 20, "min": 8, "max": 40, "step": 1 },
{ "type": "range", "id": "max_width", "label": "Max content width (px)", "default": 1200, "min": 800, "max": 1600, "step": 10 },
{ "type": "header", "content": "Section spacing" },
{ "type": "range", "id": "padding_top", "label": "Padding top", "default": 0, "min": 0, "max": 80, "step": 2 },
{ "type": "range", "id": "padding_bottom", "label": "Padding bottom", "default": 0, "min": 0, "max": 80, "step": 2 },
{ "type": "range", "id": "margin_top", "label": "Margin top", "default": 0, "min": 0, "max": 120, "step": 2 },
{ "type": "range", "id": "margin_bottom", "label": "Margin bottom", "default": 0, "min": 0, "max": 120, "step": 2 },
{ "type": "range", "id": "video_title_size_mobile", "label": "Video title size (mobile, px)", "default": 14, "min": 8, "max": 32, "step": 1 },
{ "type": "range", "id": "video_title_size_desktop", "label": "Video title size (desktop, px)", "default": 14, "min": 8, "max": 40, "step": 1 }
],
"blocks": [
{
"type": "video_card",
"name": "Video card",
"settings": [
{ "type": "text", "id": "title", "label": "Title under video" },
{ "type": "image_picker", "id": "poster_image", "label": "Poster image (thumbnail)" },
{ "type": "video", "id": "video", "label": "Video (select from Files/Library)" },
{ "type": "url", "id": "video_url", "label": "Fallback MP4 URL (if no video selected)" }
]
}
],
"max_blocks": 12,
"presets": [ { "name": "Video Reel (Carousel)" } ]
}
{% endschema %}