Infinite Drag Wall
介绍
错落排布的作品卡片铺成无边画布,按住即可向任意方向拖动并带惯性滑行,滚轮也能平移;卡片按距离依次淡入,点击放大查看并可左右切换。适合作品集首页与灵感墙。
拆自整页模板 砚白 独立设计师作品集,可以单独使用,也能和同一模板拆出的其他组件拼回整页。
纯 HTML、CSS 与少量原生 JS,没有框架依赖。复制提示词交给 Claude Code、Cursor、Trae 或 Codex,模型会把 Infinite Drag Wall 加进你的项目并换成你的配色。
适用场景
图片对比、视频展示、画廊、轮播和媒体卡片。
安装
订阅 Builder 后,用 8pt CLI 装进项目,或在已接入 8pt MCP 的编辑器里直接调用。
$ npm i -g @8pt-dev/cli && 8pt add infinite-drag-wall
mcp › 使用 8pt MCP 调用 get_component("infinite-drag-wall")
代码
Infinite Drag Wall 的源码,动效只用 transform 与 opacity,并尊重减少动态效果设置。
<div class="x-idw"><div class="x-idw__wall" aria-label="作品画布"><div class="x-idw__world"></div></div><div class="x-idw__view" role="dialog" aria-label="作品大图"><div class="x-idw__stage"><button type="button" class="x-idw__btn x-idw__x" aria-label="关闭"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6.5 6.5l11 11"/><path d="M17.5 6.5l-11 11"/></svg></button><span class="tin"></span></div><div class="x-idw__bar"><button type="button" class="x-idw__btn" data-step="-1" aria-label="上一张"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 5.5L8 12l6.5 6.5"/></svg></button><span class="x-idw__cnt">1 / 9</span><button type="button" class="x-idw__btn" data-step="1" aria-label="下一张"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 5.5L16 12l-6.5 6.5"/></svg></button></div></div></div>
.x-idw{box-sizing:border-box;position:relative;width:100%;max-width:1200px;height:820px;margin:0 auto;border-radius:24px;overflow:hidden;background:radial-gradient(120% 90% at 60% 40%,#1b1816 0%,#100e0d 70%);font-family:"PingFang SC","Noto Sans SC","Noto Sans CJK SC",system-ui,sans-serif;color:#efe9de;box-sizing:border-box}.x-idw *,.x-idw *::before,.x-idw *::after{box-sizing:border-box}.x-idw__wall{position:absolute;inset:0;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}.x-idw__wall.is-drag{cursor:grabbing}.x-idw__world{position:absolute;left:0;top:0;will-change:transform}.x-idw .tile{position:absolute;padding:5px;border:0;border-radius:19px;background:#2a2725;cursor:inherit;box-shadow:0 0 0 1px rgba(255,255,255,.04),0 18px 40px -18px rgba(0,0,0,.7);opacity:0;transform:scale(.94);transition:opacity .7s cubic-bezier(.2,.7,.2,1),transform .7s cubic-bezier(.2,.7,.2,1),background .3s}.x-idw .tile.in{opacity:1;transform:none}.x-idw .tile:hover{background:#34302d}.x-idw .tile:focus-visible{outline:2px solid #f0b07a;outline-offset:2px}.x-idw .tin{position:relative;display:block;width:100%;height:100%;border-radius:14px;overflow:hidden;background:#0e0d0c;container-type:inline-size}.x-idw .tin img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;-webkit-user-drag:none;transition:transform 1.2s cubic-bezier(.2,.7,.2,1)}.x-idw .tile:hover .tin img{transform:scale(1.035)}.x-idw .tin::after{content:"";position:absolute;inset:0;border-radius:inherit;box-shadow:inset 0 0 0 1px rgba(255,255,255,.06);pointer-events:none}.x-idw .d .tin::before{content:"";position:absolute;inset:0;z-index:1;background:linear-gradient(180deg,rgba(0,0,0,.28),transparent 30%,transparent 55%,rgba(0,0,0,.45))}.x-idw .site{z-index:2}.x-idw__view{position:absolute;inset:0;z-index:5;display:grid;place-items:center;padding:40px 40px 96px;background:rgba(10,9,8,.6);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);opacity:0;visibility:hidden;transition:opacity .35s,visibility .35s}.x-idw__view.is-open{opacity:1;visibility:visible}.x-idw__stage{position:relative;width:min(100%,calc(640px * var(--ar,1.7778)));padding:6px;border-radius:22px;background:#2a2725;box-shadow:0 0 0 1px rgba(239,233,222,.09),0 40px 120px -20px rgba(0,0,0,.9);transform:translateY(14px) scale(.98);transition:transform .45s cubic-bezier(.2,.8,.2,1)}.x-idw__view.is-open .x-idw__stage{transform:none}.x-idw__stage .tin{height:auto;aspect-ratio:var(--ar,1.7778);border-radius:17px}.x-idw__btn{display:grid;place-items:center;width:36px;height:36px;border:0;border-radius:50%;background:#2c2927;color:#efe9de;box-shadow:inset 0 0 0 1px rgba(239,233,222,.09);font-size:16px;cursor:pointer;transition:background .2s}.x-idw__btn:hover{background:#38332f}.x-idw__btn svg{width:1em;height:1em}.x-idw__x{position:absolute;top:-14px;right:-14px;z-index:3}.x-idw__bar{position:absolute;left:50%;bottom:28px;display:flex;align-items:center;gap:6px;height:46px;padding:0 6px;border-radius:99px;background:rgba(32,29,27,.88);box-shadow:inset 0 0 0 1px rgba(239,233,222,.09);transform:translateX(-50%);font-size:13px;font-variant-numeric:tabular-nums}.x-idw__bar .x-idw__btn{width:34px;height:34px;background:transparent;box-shadow:none}.x-idw__bar .x-idw__btn:hover{background:rgba(255,255,255,.08)}.x-idw__cnt{min-width:54px;text-align:center;color:#cfc6ba}@media (max-width:640px){.x-idw{height:620px}.x-idw__view{padding:20px 16px 90px}}@media (prefers-reduced-motion:reduce){.x-idw .tile,.x-idw .tin img,.x-idw__view,.x-idw__stage{transition:none}.x-idw .tile{opacity:1;transform:none}}.x-idw .site{position:absolute;inset:0;padding:5.2cqw 5.6cqw;display:flex;flex-direction:column;color:#fff;font-size:1.7cqw;line-height:1.4;text-align:left;pointer-events:none}.x-idw .lt .site{color:#1f1b17}.x-idw .s-top{display:flex;justify-content:space-between;align-items:flex-start;gap:3cqw;font-size:1.8cqw}.x-idw .s-logo{font-weight:700;letter-spacing:.06em}.x-idw .s-nav{display:flex;gap:2.8cqw;opacity:.78}.x-idw .s-nav i,.x-idw .s-cols i,.x-idw .s-foot i{font-style:normal}.x-idw .s-h{margin-top:auto;font-size:5.6cqw;line-height:1.14;font-weight:650;letter-spacing:.01em;text-shadow:0 .4cqw 2cqw rgba(0,0,0,.25)}.x-idw .lt .s-h{text-shadow:none}.x-idw .s-h2{font-size:3.4cqw;line-height:1.18;font-weight:650}.x-idw .s-cols{display:grid;grid-template-columns:repeat(3,auto);gap:.8cqw 4cqw;font-size:1.6cqw;opacity:.8}.x-idw .s-p{margin-top:1.4cqw;font-size:1.75cqw;opacity:.72;max-width:52%}.x-idw .s-btn{align-self:flex-start;margin-top:2.4cqw;padding:1.1cqw 2.5cqw;border-radius:99px;background:rgba(255,255,255,.94);color:#151311;font-size:1.65cqw;font-weight:600}.x-idw .s-dk{background:#1f1b17;color:#f4efe6}.x-idw .s-r{align-self:flex-end;text-align:right}.x-idw .s-c{align-self:center;text-align:center;max-width:none}.x-idw .s-cb{align-self:center}.x-idw .s-up{margin-top:6cqw;margin-bottom:auto}.x-idw .s-big{margin-top:auto;align-self:center;font-size:21cqw;font-weight:800;line-height:.9;color:#fff;letter-spacing:.04em;text-shadow:0 1cqw 4cqw rgba(30,60,20,.25)}.x-idw .s-foot{display:flex;justify-content:space-between;margin-top:2cqw;font-size:1.5cqw;opacity:.75;color:#fff}.x-idw .s-404{margin:auto auto 0;font-size:17cqw;font-weight:800;line-height:1;letter-spacing:.04em;color:rgba(255,255,255,.9);text-shadow:0 0 6cqw rgba(120,220,255,.35)}.x-idw .s-link{margin-top:2.4cqw;font-size:1.7cqw;opacity:.85}.x-idw .s-pill{padding:.8cqw 2cqw;border:1px solid rgba(255,255,255,.35);border-radius:99px;font-size:1.5cqw}.x-idw .s-hint{align-self:flex-end;margin-top:-2cqw;font-size:1.4cqw;opacity:.55}(root) => {
const C = [{"k": "lantern", "ar": 1.7778, "t": "d", "name": "灯屿 · 夜读", "img": "https://8pt.dev/api/media/596ca18adba2d1c8eb2b5f28.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">灯屿</b><span class=\"s-nav\"><i>夜读</i><i>札记</i><i>关于</i></span></span><span class=\"s-h\">把夜晚<br>留给慢下来的人</span><span class=\"s-p\">收藏值得读的文字,等夜深时再慢慢翻开</span><span class=\"s-btn\">进入夜读</span>"}, {"k": "bamboo", "ar": 1.7778, "t": "l", "name": "竹息 · 林间", "img": "https://8pt.dev/api/media/2a48eefd24317a44b0258c79.webp", "site": "<span class=\"s-top\"><span class=\"s-h2\">一处安静的<br>林间角落</span><span class=\"s-cols\"><i>白噪音</i><i>专注</i><i>日志</i><i>花园</i><i>关于</i><i>联系</i></span></span><span class=\"s-big\">竹息</span><span class=\"s-foot\"><i>© 竹息</i><i>隐私 · 条款</i></span>"}, {"k": "door", "ar": 1.7778, "t": "d", "name": "沙门 · 出口", "img": "https://8pt.dev/api/media/1a24aa1e7d9544d8f8a6da70.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">沙门</b><span class=\"s-nav\"><i>故事</i><i>出口</i></span></span><span class=\"s-h\">走出<br>信息流</span><span class=\"s-p\">推开门,世界会安静一点</span><span class=\"s-link\">点击进入 →</span>"}, {"k": "telescope", "ar": 1.3333, "t": "l", "name": "云镜 · 观测", "img": "https://8pt.dev/api/media/ec31ce9b0b61db0d2b2d1f92.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">云镜</b><span class=\"s-nav\"><i>观测</i><i>星图</i><i>订阅</i></span></span><span class=\"s-h s-r\">看得更远<br>一点点</span><span class=\"s-p s-r\">把日落之后的天空装进口袋</span>"}, {"k": "sunken", "ar": 1.7778, "t": "d", "name": "沉舟 · 404", "img": "https://8pt.dev/api/media/2a2e69f3d62322e9318bb4c6.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">沉舟</b><span class=\"s-nav\"><i>返回</i></span></span><span class=\"s-404\">404</span><span class=\"s-p s-c\">这一页沉到海底了</span><span class=\"s-btn s-cb\">浮回首页</span>"}, {"k": "globe", "ar": 1.3333, "t": "l", "name": "雪屋 · 冬日", "img": "https://8pt.dev/api/media/18decab4de8eda12bcd7ab66.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">雪屋</b><span class=\"s-nav\"><i>小屋</i><i>手作</i><i>候补</i></span></span><span class=\"s-h\">冬天的<br>小屋</span><span class=\"s-btn s-dk\">加入候补</span>"}, {"k": "clay", "ar": 0.75, "t": "l", "name": "陶序 · 器物", "img": "https://8pt.dev/api/media/b72329bcdf3f85eb9382a8ad.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">陶序</b><span class=\"s-nav\"><i>器物</i><i>工坊</i></span></span><span class=\"s-h\">器物<br>与留白</span><span class=\"s-p\">手捏的形状,安静的颜色</span>"}, {"k": "book", "ar": 1.7778, "t": "d", "name": "星页 · 阅读", "img": "https://8pt.dev/api/media/6e610c02b7c385e2fd9b5975.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">星页</b><span class=\"s-nav\"><i>书架</i><i>星图</i><i>我的</i></span></span><span class=\"s-h s-c s-up\">每一页<br>都是一片星空</span><span class=\"s-btn s-cb\">开始阅读</span>"}, {"k": "glass", "ar": 1.7778, "t": "d", "name": "棱光 · 发布", "img": "https://8pt.dev/api/media/44f2c620b7b4377af63ad5b3.webp", "site": "<span class=\"s-top\"><b class=\"s-logo\">棱光</b><span class=\"s-pill\">预约体验</span></span><span class=\"s-h\">一束光,<br>万种颜色</span><span class=\"s-p\">透过一块玻璃,看见光的全部</span><span class=\"s-hint\">向下浏览</span>"}];
const N = C.length;
const wall = root.querySelector('.x-idw__wall');
const world = root.querySelector('.x-idw__world');
const view = root.querySelector('.x-idw__view');
const stage = root.querySelector('.x-idw__stage');
const vtin = stage.querySelector('.tin');
const cnt = root.querySelector('.x-idw__cnt');
const mod = (a, b) => ((a % b) + b) % b;
const timers = new Set();
let TW, G, H, P, cam = { x: 0, y: 0 }, vel = { x: 0, y: 0 }, raf = 0, drag = null, moved = 0, cur = 0, t0 = performance.now();
…订阅 Builder 查看完整源码。查看价格