Shopify 图库与细节展示
先看整体,再主动查看细节或切换内容,连续动效用于装饰区域。
分组图库揭示
用有先后关系的三张图表达同一个主题,每张图只承担一项说明。
已暂停,点击播放后体验交互。
演示使用抽象图形和示例文案。滚动效果在局部预览区内体验,接入页面时应使用 Section 自身的滚动区间。
适合放在 Shopify 的哪个位置?
品牌或工艺介绍 Section;真实图像使用明确尺寸、恰当替代文本和非首屏懒加载。
交互和代码如何运作?
clip-path 与 opacity 逐张揭示,延迟由图序决定。示例播放按钮启动一次入场;正式页用单次 IntersectionObserver 触发后解除观察。
触摸、键盘和减少动态效果如何处理?
减少动态效果全部静态显示;手机缩小间距或复用已有卡片滑动模式,不依赖依次揭示传递必要信息。
上线前要检查什么?
所有图都有静态可读状态;尺寸预留避免跳动;重播没有残留;非首屏素材不争抢首屏资源。
示例源码 · reveal-grid.html
<section class="fx" data-effect="reveal-grid"><small>13 / A CONTROLLED ENTRANCE</small><h2>DETAILS MATTER.</h2><div class="reveal-grid"><div class="art" data-art="1" aria-label="Abstract concept one" role="img"></div><div class="art" data-art="2" aria-label="Abstract concept two" role="img"></div><div class="art" data-art="3" aria-label="Abstract concept three" role="img"></div></div></section>
示例源码 · reveal-grid.css
*{box-sizing:border-box}body{margin:0;background:#11251f;color:#eef5e6;font:15px/1.5 system-ui,sans-serif}button,input{font:inherit}button{cursor:pointer}button:focus-visible,a:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:3px solid #d5f875;outline-offset:4px}button{border:1px solid #819a8a;border-radius:30px;padding:10px 18px;color:inherit;background:transparent}button:hover{background:#294638}.fx{position:relative;min-height:350px;padding:30px;overflow:hidden;display:grid;align-content:center;gap:20px}.fx h2,.fx p{margin:0}.fx h2{font-size:clamp(32px,7vw,68px);letter-spacing:-.05em;line-height:1.08}.fx small{letter-spacing:.12em;color:#bed0c1}.art{position:relative;min-height:180px;border-radius:14px;overflow:hidden;background:radial-gradient(circle at 70% 25%,#d5f875 0 14%,transparent 14.5%),linear-gradient(135deg,#087e75,#183c30)}.art:before{content:'';position:absolute;width:130px;height:130px;border:25px solid #cde4d0;border-radius:30px;left:20%;top:25%;transform:rotate(-25deg)}.art:after{content:'';position:absolute;inset:16px;border:1px solid #cde4d044;border-radius:8px}.art[data-art="2"]{background:radial-gradient(circle at 25% 70%,#a7b7ff 0 20%,transparent 20.5%),linear-gradient(45deg,#102943,#687ab1)}.art[data-art="3"]{background:radial-gradient(circle at 60% 50%,#ffd9a9 0 24%,transparent 24.5%),linear-gradient(135deg,#492e30,#a97652)}.hint{font-size:12px;color:#b9cbbd}.controls{display:flex;gap:8px;flex-wrap:wrap}.scroller{height:270px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#b5cc91 #18382d;border-radius:10px;position:relative;outline-offset:2px}.spacer{height:100px;display:grid;place-items:center;color:#b9cbbd;font-size:12px}.fx [hidden]{display:none!important}.fx[data-playing="false"] *{animation-play-state:paused!important}.fx button[aria-pressed="true"]{background:#d5f875;color:#11251f}.fx a{color:inherit;text-decoration:underline;text-underline-offset:4px}
@media(max-width:500px){.fx{padding:20px;min-height:330px}.fx h2{font-size:36px}.art{min-height:160px}}
@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
.fx[data-reduced-motion=true] *,.fx[data-reduced-motion=true] *:before,.fx[data-reduced-motion=true] *:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
.fx[data-reduced-motion=true] .stack-card{position:relative;top:0!important}.fx[data-reduced-motion=true] .horizontal-story{height:270px}.fx[data-reduced-motion=true] .horizontal-pin{overflow-x:auto}.fx[data-reduced-motion=true] .horizontal-track{transform:none!important}.fx[data-reduced-motion=true] .story-grid{grid-template-columns:1fr}.fx[data-reduced-motion=true] .story-image{position:static}.fx[data-reduced-motion=true] .loop-window{overflow-x:auto}.fx[data-reduced-motion=true] .loop-set[aria-hidden=true]{display:none}
.reveal-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.reveal-grid .art{min-height:190px}.reveal-grid .art:before{left:5%;width:100px;height:100px}@media(max-width:500px){.reveal-grid{gap:8px}.reveal-grid .art{min-height:150px}.reveal-grid .art:before{width:60px;height:80px;border-width:12px}}
示例源码 · reveal-grid.js
// WESWOO original educational example; no third-party runtime required.
export function mountEffect(root, {reducedMotion=false}={}) {
const reduced=reducedMotion||matchMedia('(prefers-reduced-motion: reduce)').matches;
root.dataset.reducedMotion=String(reduced);
const life=new AbortController(), animations=[];
let active=false, started=false;
const on=(node,type,fn,options={})=>node.addEventListener(type,fn,{...options,signal:life.signal});
const animate=(node,frames,options)=>{
if(reduced)return null;
const animation=node.animate(frames,options);animations.push(animation);return animation;
};
let start=()=>{}, stop=()=>{}, cleanup=()=>{};
start=()=>root.querySelectorAll('.reveal-grid .art').forEach((card,i)=>animate(card,[{clipPath:'inset(100% 0 0 0)',opacity:.3},{clipPath:'inset(0 0 0 0)',opacity:1}],{duration:900,delay:i*160,fill:'both',easing:'cubic-bezier(.2,.7,.2,1)'}));
root.dataset.playing='false';
return {
resume(){active=true;root.dataset.playing='true';if(!started){started=true;start();}animations.forEach(a=>a.play());root.dispatchEvent(new Event('ww:resume'));},
pause(){active=false;root.dataset.playing='false';animations.forEach(a=>a.pause());stop();},
destroy(){active=false;life.abort();animations.forEach(a=>a.cancel());cleanup();}
};
}
案例效果与官方依据
查看 WESWOO 案例站中的参考效果这是根据同类交互重新编写的轻量实现,不复刻参考站主题及其整包源代码。
WESWOO 文章:对应文章经核对匹配后再补充。
细节放大弹层
由用户主动打开细节图,适合工艺、材质和局部构造的进一步确认。
已暂停,点击播放后体验交互。
演示使用抽象图形和示例文案。滚动效果在局部预览区内体验,接入页面时应使用 Section 自身的滚动区间。
适合放在 Shopify 的哪个位置?
商品媒体或工艺 Section;连接真实高分辨率图像,保留当前商品和变体上下文。
交互和代码如何运作?
点击按钮后打开原生 dialog,使用局部缩放入场;关闭按钮、Escape 和遮罩关闭后恢复触发器焦点。大图可在点击后再加载。
触摸、键盘和减少动态效果如何处理?
减少动态效果不做缩放;手机弹层限制为视口宽高并允许滚动。示例为单图放大,不宣称具备触摸捏合缩放。
上线前要检查什么?
焦点进出正确;关闭后滚动位置不丢失;大图有替代文本;图像加载失败时能关掉弹层。
示例源码 · zoom-dialog.html
<section class="fx" data-effect="zoom-dialog"><small>14 / TAKE A CLOSER LOOK</small><button type="button" class="zoom-open" aria-haspopup="dialog" aria-label="Enlarge the abstract detail"><span class="art" aria-hidden="true"></span><span>Explore the detail +</span></button><dialog aria-label="Enlarged abstract detail"><button type="button" data-close>Close ×</button><div class="art" role="img" aria-label="Enlarged abstract construction detail"></div><p>Replace this illustration with a rights-cleared product image and meaningful alternative text.</p></dialog></section>
示例源码 · zoom-dialog.css
*{box-sizing:border-box}body{margin:0;background:#11251f;color:#eef5e6;font:15px/1.5 system-ui,sans-serif}button,input{font:inherit}button{cursor:pointer}button:focus-visible,a:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:3px solid #d5f875;outline-offset:4px}button{border:1px solid #819a8a;border-radius:30px;padding:10px 18px;color:inherit;background:transparent}button:hover{background:#294638}.fx{position:relative;min-height:350px;padding:30px;overflow:hidden;display:grid;align-content:center;gap:20px}.fx h2,.fx p{margin:0}.fx h2{font-size:clamp(32px,7vw,68px);letter-spacing:-.05em;line-height:1.08}.fx small{letter-spacing:.12em;color:#bed0c1}.art{position:relative;min-height:180px;border-radius:14px;overflow:hidden;background:radial-gradient(circle at 70% 25%,#d5f875 0 14%,transparent 14.5%),linear-gradient(135deg,#087e75,#183c30)}.art:before{content:'';position:absolute;width:130px;height:130px;border:25px solid #cde4d0;border-radius:30px;left:20%;top:25%;transform:rotate(-25deg)}.art:after{content:'';position:absolute;inset:16px;border:1px solid #cde4d044;border-radius:8px}.art[data-art="2"]{background:radial-gradient(circle at 25% 70%,#a7b7ff 0 20%,transparent 20.5%),linear-gradient(45deg,#102943,#687ab1)}.art[data-art="3"]{background:radial-gradient(circle at 60% 50%,#ffd9a9 0 24%,transparent 24.5%),linear-gradient(135deg,#492e30,#a97652)}.hint{font-size:12px;color:#b9cbbd}.controls{display:flex;gap:8px;flex-wrap:wrap}.scroller{height:270px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#b5cc91 #18382d;border-radius:10px;position:relative;outline-offset:2px}.spacer{height:100px;display:grid;place-items:center;color:#b9cbbd;font-size:12px}.fx [hidden]{display:none!important}.fx[data-playing="false"] *{animation-play-state:paused!important}.fx button[aria-pressed="true"]{background:#d5f875;color:#11251f}.fx a{color:inherit;text-decoration:underline;text-underline-offset:4px}
@media(max-width:500px){.fx{padding:20px;min-height:330px}.fx h2{font-size:36px}.art{min-height:160px}}
@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
.fx[data-reduced-motion=true] *,.fx[data-reduced-motion=true] *:before,.fx[data-reduced-motion=true] *:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
.fx[data-reduced-motion=true] .stack-card{position:relative;top:0!important}.fx[data-reduced-motion=true] .horizontal-story{height:270px}.fx[data-reduced-motion=true] .horizontal-pin{overflow-x:auto}.fx[data-reduced-motion=true] .horizontal-track{transform:none!important}.fx[data-reduced-motion=true] .story-grid{grid-template-columns:1fr}.fx[data-reduced-motion=true] .story-image{position:static}.fx[data-reduced-motion=true] .loop-window{overflow-x:auto}.fx[data-reduced-motion=true] .loop-set[aria-hidden=true]{display:none}
.zoom-open{padding:0;border:0;border-radius:12px;text-align:left;width:100%}.zoom-open>.art{display:block;min-height:220px}.zoom-open>span:last-child{display:block;padding:12px}dialog{width:calc(100% - 30px);max-width:680px;border:0;border-radius:14px;background:#16392d;color:#eef5e6;padding:20px}dialog::backdrop{background:#000b}dialog .art{margin:15px 0;min-height:230px}dialog p{font-size:12px}dialog[open] .art{animation:detail-in .35s ease-out}@keyframes detail-in{from{transform:scale(.95);opacity:.4}to{transform:none;opacity:1}}
示例源码 · zoom-dialog.js
// WESWOO original educational example; no third-party runtime required.
export function mountEffect(root, {reducedMotion=false}={}) {
const reduced=reducedMotion||matchMedia('(prefers-reduced-motion: reduce)').matches;
root.dataset.reducedMotion=String(reduced);
const life=new AbortController(), animations=[];
let active=false, started=false;
const on=(node,type,fn,options={})=>node.addEventListener(type,fn,{...options,signal:life.signal});
const animate=(node,frames,options)=>{
if(reduced)return null;
const animation=node.animate(frames,options);animations.push(animation);return animation;
};
let start=()=>{}, stop=()=>{}, cleanup=()=>{};
const dialog=root.querySelector('dialog'),open=root.querySelector('.zoom-open');
on(open,'click',()=>{if(active&&!dialog.open)dialog.showModal();});on(root.querySelector('[data-close]'),'click',()=>dialog.close());
on(dialog,'click',event=>{if(event.target===dialog){const r=dialog.getBoundingClientRect();if(event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom)dialog.close();}});
stop=()=>{if(dialog.open)dialog.close();};cleanup=stop;
root.dataset.playing='false';
return {
resume(){active=true;root.dataset.playing='true';if(!started){started=true;start();}animations.forEach(a=>a.play());root.dispatchEvent(new Event('ww:resume'));},
pause(){active=false;root.dataset.playing='false';animations.forEach(a=>a.pause());stop();},
destroy(){active=false;life.abort();animations.forEach(a=>a.cancel());cleanup();}
};
}
案例效果与官方依据
查看 WESWOO 案例站中的参考效果这是根据同类交互重新编写的轻量实现,不复刻参考站主题及其整包源代码。
WESWOO 文章:对应文章经核对匹配后再补充。
主动切换内容轮播
让用户决定阅读速度,保留下一张预览、可选圆点和原生触摸滑动。
已暂停,点击播放后体验交互。
演示使用抽象图形和示例文案。滚动效果在局部预览区内体验,接入页面时应使用 Section 自身的滚动区间。
适合放在 Shopify 的哪个位置?
首页内容、系列介绍或案例 Section;优先整合主题已有轮播,避免同页重复加载轮播库。
交互和代码如何运作?
CSS scroll-snap 提供原生滑动,圆点用 scrollTo 对齐目标起点;滚动时找最接近容器起点的项,更新 aria-pressed 和状态文本。
触摸、键盘和减少动态效果如何处理?
没有自动前进;减少动态效果用即时定位。保留键盘可聚焦滚动区和可点圆点。示例是局部实现,生产中复用现有组件。
上线前要检查什么?
拖动、点击首末圆点和键盘都可用;最后一张能到达;滑动后圆点同步;父页面无水平溢出。
示例源码 · content-slider.html
<section class="fx" data-effect="content-slider"><small>15 / CHOOSE YOUR PACE</small><div class="slides" tabindex="0" aria-label="Swipe the three abstract slides"><div class="slide"><div class="art" data-art="1"></div><strong>01 / The concept</strong></div><div class="slide"><div class="art" data-art="2"></div><strong>02 / The system</strong></div><div class="slide"><div class="art" data-art="3"></div><strong>03 / The experience</strong></div></div><div class="slide-dots" role="group" aria-label="Choose a slide"><button type="button" data-slide="0" aria-pressed="true" aria-label="Slide 1">1</button><button type="button" data-slide="1" aria-pressed="false" aria-label="Slide 2">2</button><button type="button" data-slide="2" aria-pressed="false" aria-label="Slide 3">3</button></div><p class="hint" role="status">Slide 1 of 3. No automatic advancement.</p></section>
示例源码 · content-slider.css
*{box-sizing:border-box}body{margin:0;background:#11251f;color:#eef5e6;font:15px/1.5 system-ui,sans-serif}button,input{font:inherit}button{cursor:pointer}button:focus-visible,a:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:3px solid #d5f875;outline-offset:4px}button{border:1px solid #819a8a;border-radius:30px;padding:10px 18px;color:inherit;background:transparent}button:hover{background:#294638}.fx{position:relative;min-height:350px;padding:30px;overflow:hidden;display:grid;align-content:center;gap:20px}.fx h2,.fx p{margin:0}.fx h2{font-size:clamp(32px,7vw,68px);letter-spacing:-.05em;line-height:1.08}.fx small{letter-spacing:.12em;color:#bed0c1}.art{position:relative;min-height:180px;border-radius:14px;overflow:hidden;background:radial-gradient(circle at 70% 25%,#d5f875 0 14%,transparent 14.5%),linear-gradient(135deg,#087e75,#183c30)}.art:before{content:'';position:absolute;width:130px;height:130px;border:25px solid #cde4d0;border-radius:30px;left:20%;top:25%;transform:rotate(-25deg)}.art:after{content:'';position:absolute;inset:16px;border:1px solid #cde4d044;border-radius:8px}.art[data-art="2"]{background:radial-gradient(circle at 25% 70%,#a7b7ff 0 20%,transparent 20.5%),linear-gradient(45deg,#102943,#687ab1)}.art[data-art="3"]{background:radial-gradient(circle at 60% 50%,#ffd9a9 0 24%,transparent 24.5%),linear-gradient(135deg,#492e30,#a97652)}.hint{font-size:12px;color:#b9cbbd}.controls{display:flex;gap:8px;flex-wrap:wrap}.scroller{height:270px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#b5cc91 #18382d;border-radius:10px;position:relative;outline-offset:2px}.spacer{height:100px;display:grid;place-items:center;color:#b9cbbd;font-size:12px}.fx [hidden]{display:none!important}.fx[data-playing="false"] *{animation-play-state:paused!important}.fx button[aria-pressed="true"]{background:#d5f875;color:#11251f}.fx a{color:inherit;text-decoration:underline;text-underline-offset:4px}
@media(max-width:500px){.fx{padding:20px;min-height:330px}.fx h2{font-size:36px}.art{min-height:160px}}
@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
.fx[data-reduced-motion=true] *,.fx[data-reduced-motion=true] *:before,.fx[data-reduced-motion=true] *:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
.fx[data-reduced-motion=true] .stack-card{position:relative;top:0!important}.fx[data-reduced-motion=true] .horizontal-story{height:270px}.fx[data-reduced-motion=true] .horizontal-pin{overflow-x:auto}.fx[data-reduced-motion=true] .horizontal-track{transform:none!important}.fx[data-reduced-motion=true] .story-grid{grid-template-columns:1fr}.fx[data-reduced-motion=true] .story-image{position:static}.fx[data-reduced-motion=true] .loop-window{overflow-x:auto}.fx[data-reduced-motion=true] .loop-set[aria-hidden=true]{display:none}
.slides{display:flex;gap:14px;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none}.slides::-webkit-scrollbar{display:none}.slide{flex:0 0 88%;scroll-snap-align:start}.slide .art{min-height:210px}.slide strong{display:block;margin-top:10px}.slide-dots{display:flex;gap:10px;justify-content:center}.slide-dots button{width:36px;height:36px;padding:0;border-radius:50%;font-size:12px}
示例源码 · content-slider.js
// WESWOO original educational example; no third-party runtime required.
export function mountEffect(root, {reducedMotion=false}={}) {
const reduced=reducedMotion||matchMedia('(prefers-reduced-motion: reduce)').matches;
root.dataset.reducedMotion=String(reduced);
const life=new AbortController(), animations=[];
let active=false, started=false;
const on=(node,type,fn,options={})=>node.addEventListener(type,fn,{...options,signal:life.signal});
const animate=(node,frames,options)=>{
if(reduced)return null;
const animation=node.animate(frames,options);animations.push(animation);return animation;
};
let start=()=>{}, stop=()=>{}, cleanup=()=>{};
const viewport=root.querySelector('.slides'),slides=[...root.querySelectorAll('.slide')],dots=[...root.querySelectorAll('[data-slide]')];
dots.forEach((button,i)=>on(button,'click',()=>{if(active)viewport.scrollTo({left:slides[i].offsetLeft-slides[0].offsetLeft,behavior:reduced?'instant':'smooth'});}));
let frame=0;function reflect(){frame=0;let index=0,distance=Infinity;slides.forEach((slide,i)=>{const delta=Math.abs(slide.offsetLeft-slides[0].offsetLeft-viewport.scrollLeft);if(delta<distance){distance=delta;index=i;}});dots.forEach((button,i)=>button.setAttribute('aria-pressed',String(i===index)));root.querySelector('[role=status]').textContent=`Slide ${index+1} of ${slides.length}. No automatic advancement.`;}
on(viewport,'scroll',()=>{if(!frame)frame=requestAnimationFrame(reflect);},{passive:true});cleanup=()=>cancelAnimationFrame(frame);
root.dataset.playing='false';
return {
resume(){active=true;root.dataset.playing='true';if(!started){started=true;start();}animations.forEach(a=>a.play());root.dispatchEvent(new Event('ww:resume'));},
pause(){active=false;root.dataset.playing='false';animations.forEach(a=>a.pause());stop();},
destroy(){active=false;life.abort();animations.forEach(a=>a.cancel());cleanup();}
};
}
案例效果与官方依据
查看 WESWOO 案例站中的参考效果这是根据同类交互重新编写的轻量实现,不复刻参考站主题及其整包源代码。
WESWOO 文章:对应文章经核对匹配后再补充。
连续装饰图库
在非关键区域形成持续的视觉带,展示气质或设计方向,实际入口另外提供。
已暂停,点击播放后体验交互。
演示使用抽象图形和示例文案。滚动效果在局部预览区内体验,接入页面时应使用 Section 自身的滚动区间。
适合放在 Shopify 的哪个位置?
品牌气氛或灵感 Section;使用经授权的轻量素材,重要作品链接放在静止列表中。
交互和代码如何运作?
两份等宽图组组成轨道,移动半个总宽度循环;复制组 aria-hidden,内部不放可聚焦控件。暂停和页面隐藏停止动画。
触摸、键盘和减少动态效果如何处理?
减少动态效果隐藏重复组,允许直接横向查看原图组;不用它展示必须逐个确认的商品和法律信息。
上线前要检查什么?
两组含间距后的总宽一致;没有重复焦点;暂停后静止;恢复单一时间轴;真实素材经过尺寸和体积优化。
示例源码 · loop-gallery.html
<section class="fx" data-effect="loop-gallery"><small>16 / A DECORATIVE LOOP</small><h2>KEEP EXPLORING.</h2><div class="loop-window" aria-label="Abstract illustration strip"><div class="loop-track"><div class="loop-set"><div class="art" data-art="1"></div><div class="art" data-art="2"></div><div class="art" data-art="3"></div></div><div class="loop-set" aria-hidden="true"><div class="art" data-art="1"></div><div class="art" data-art="2"></div><div class="art" data-art="3"></div></div></div></div><p class="hint">A decorative loop. Put real links in a separate, stable list.</p></section>
示例源码 · loop-gallery.css
*{box-sizing:border-box}body{margin:0;background:#11251f;color:#eef5e6;font:15px/1.5 system-ui,sans-serif}button,input{font:inherit}button{cursor:pointer}button:focus-visible,a:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:3px solid #d5f875;outline-offset:4px}button{border:1px solid #819a8a;border-radius:30px;padding:10px 18px;color:inherit;background:transparent}button:hover{background:#294638}.fx{position:relative;min-height:350px;padding:30px;overflow:hidden;display:grid;align-content:center;gap:20px}.fx h2,.fx p{margin:0}.fx h2{font-size:clamp(32px,7vw,68px);letter-spacing:-.05em;line-height:1.08}.fx small{letter-spacing:.12em;color:#bed0c1}.art{position:relative;min-height:180px;border-radius:14px;overflow:hidden;background:radial-gradient(circle at 70% 25%,#d5f875 0 14%,transparent 14.5%),linear-gradient(135deg,#087e75,#183c30)}.art:before{content:'';position:absolute;width:130px;height:130px;border:25px solid #cde4d0;border-radius:30px;left:20%;top:25%;transform:rotate(-25deg)}.art:after{content:'';position:absolute;inset:16px;border:1px solid #cde4d044;border-radius:8px}.art[data-art="2"]{background:radial-gradient(circle at 25% 70%,#a7b7ff 0 20%,transparent 20.5%),linear-gradient(45deg,#102943,#687ab1)}.art[data-art="3"]{background:radial-gradient(circle at 60% 50%,#ffd9a9 0 24%,transparent 24.5%),linear-gradient(135deg,#492e30,#a97652)}.hint{font-size:12px;color:#b9cbbd}.controls{display:flex;gap:8px;flex-wrap:wrap}.scroller{height:270px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#b5cc91 #18382d;border-radius:10px;position:relative;outline-offset:2px}.spacer{height:100px;display:grid;place-items:center;color:#b9cbbd;font-size:12px}.fx [hidden]{display:none!important}.fx[data-playing="false"] *{animation-play-state:paused!important}.fx button[aria-pressed="true"]{background:#d5f875;color:#11251f}.fx a{color:inherit;text-decoration:underline;text-underline-offset:4px}
@media(max-width:500px){.fx{padding:20px;min-height:330px}.fx h2{font-size:36px}.art{min-height:160px}}
@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
.fx[data-reduced-motion=true] *,.fx[data-reduced-motion=true] *:before,.fx[data-reduced-motion=true] *:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
.fx[data-reduced-motion=true] .stack-card{position:relative;top:0!important}.fx[data-reduced-motion=true] .horizontal-story{height:270px}.fx[data-reduced-motion=true] .horizontal-pin{overflow-x:auto}.fx[data-reduced-motion=true] .horizontal-track{transform:none!important}.fx[data-reduced-motion=true] .story-grid{grid-template-columns:1fr}.fx[data-reduced-motion=true] .story-image{position:static}.fx[data-reduced-motion=true] .loop-window{overflow-x:auto}.fx[data-reduced-motion=true] .loop-set[aria-hidden=true]{display:none}
.loop-window{overflow:hidden;margin:0 -30px}.loop-track{display:flex;width:max-content}.loop-set{display:flex;gap:16px;padding-right:16px;flex:none}.loop-set .art{width:200px;min-height:180px}.loop-set .art:before{left:10%}@media(prefers-reduced-motion:reduce){.loop-window{overflow-x:auto}.loop-set[aria-hidden=true]{display:none}}
示例源码 · loop-gallery.js
// WESWOO original educational example; no third-party runtime required.
export function mountEffect(root, {reducedMotion=false}={}) {
const reduced=reducedMotion||matchMedia('(prefers-reduced-motion: reduce)').matches;
root.dataset.reducedMotion=String(reduced);
const life=new AbortController(), animations=[];
let active=false, started=false;
const on=(node,type,fn,options={})=>node.addEventListener(type,fn,{...options,signal:life.signal});
const animate=(node,frames,options)=>{
if(reduced)return null;
const animation=node.animate(frames,options);animations.push(animation);return animation;
};
let start=()=>{}, stop=()=>{}, cleanup=()=>{};
start=()=>animate(root.querySelector('.loop-track'),[{transform:'translateX(0)'},{transform:'translateX(-50%)'}],{duration:22000,iterations:Infinity,easing:'linear'});
root.dataset.playing='false';
return {
resume(){active=true;root.dataset.playing='true';if(!started){started=true;start();}animations.forEach(a=>a.play());root.dispatchEvent(new Event('ww:resume'));},
pause(){active=false;root.dataset.playing='false';animations.forEach(a=>a.pause());stop();},
destroy(){active=false;life.abort();animations.forEach(a=>a.cancel());cleanup();}
};
}
案例效果与官方依据
查看 WESWOO 案例站中的参考效果这是根据同类交互重新编写的轻量实现,不复刻参考站主题及其整包源代码。
WESWOO 文章:对应文章经核对匹配后再补充。
内容确定后,再选择特效
先在知识库确定页面逻辑,再在开发知识库连接真实数据。只在有助理解信息的位置增加动效,并在接入后检查性能。
接入 Shopify Section
预览由普通 HTML、CSS 和 JavaScript 组成。先在复制主题中逐个接入,再由 Section 设置提供真实文案和图片。
- 将所选 CSS 保存为 assets/ww-effects.css,JavaScript 保存为 assets/ww-selected-effect.js;与主题整合前,把 CSS 限定在该 Section 内。
- 用所选 .fx 元素替换下方壳层占位,将示例文字、抽象图形改为 Section 设置及有授权的 image_tag 输出,控件标签接入语言文件。
- 用生命周期脚本防止重复挂载,离屏暂停、shopify:section:unload 时销毁;把局部滚动演示改成实际 Section 的区间计算,在主题编辑器和真实设备验收。
接入壳层:先插入所选效果 HTML 才能使用,不是可直接安装的主题或应用。
sections/ww-effect.liquid
{% comment %} WESWOO example shell. Add ONE effect's HTML inside .fx-slot. {% endcomment %}
{{ 'ww-effects.css' | asset_url | stylesheet_tag }}
<section id="ww-effect-{{ section.id }}" data-ww-effect-section>
<h2>{{ section.settings.heading | escape }}</h2>
<button type="button" data-effect-toggle aria-pressed="false">Play / pause effect</button>
<div class="fx-slot">
{% comment %} Insert the chosen .fx element here; read copy/images from section settings. {% endcomment %}
</div>
</section>
<script type="module" src="{{ 'ww-effect-section.js' | asset_url }}"></script>
{% schema %}
{"name":"WESWOO effect","settings":[{"type":"text","id":"heading","label":"Heading","default":"Our story"}],"presets":[{"name":"WESWOO effect"}]}
{% endschema %}
共用初始化:每个模块接入一种所选效果;多种效果需显式建立注册表和 Section 对应关系。
assets/ww-effect-section.js
// Put the chosen example JS in assets/ww-selected-effect.js (rename .mjs to .js).
import {mountEffect} from './ww-selected-effect.js';
const instances=new Map();
function mount(container=document){
const roots=[...(container.matches?.('[data-ww-effect-section]')?[container]:[]),...container.querySelectorAll('[data-ww-effect-section]')];
for(const section of roots){
if(instances.has(section))continue;
const root=section.querySelector('.fx'),button=section.querySelector('[data-effect-toggle]');
if(!root||!button)continue;
const effect=mountEffect(root),events=new AbortController();let playing=false;
const pause=()=>{playing=false;effect.pause();button.setAttribute('aria-pressed','false');};
button.addEventListener('click',()=>{playing=!playing;playing?effect.resume():effect.pause();button.setAttribute('aria-pressed',String(playing));},{signal:events.signal});
const observer=new IntersectionObserver(entries=>{if(!entries[0].isIntersecting)pause();});observer.observe(section);
document.addEventListener('visibilitychange',()=>{if(document.hidden)pause();},{signal:events.signal});
instances.set(section,()=>{observer.disconnect();events.abort();effect.destroy();});
}
}
document.addEventListener('shopify:section:load',event=>mount(event.target));
document.addEventListener('shopify:section:unload',event=>{for(const [section,destroy] of instances)if(section===event.target||event.target.contains(section)){destroy();instances.delete(section);}});
mount();
这些示例为 WESWOO 编写的教学实现,可查看、复制并保留 WESWOO 及本页来源说明。参考主题、第三方库、商标和案例图片仍适用各自权利,未包含在演示下载中。
Shopify Dawn 主题整合参考(固定版本)。Dawn 源码适用其 Shopify 平台使用许可,上述示例未打包该仓库代码。
- Shopify/dawn · modal / slider base classes
- Shopify/dawn · product media gallery
- Shopify/dawn · license
依据核对 2026-11-12