before-after-sliderBefore / after slider
前后对比滑杆A draggable divider that wipes between two overlaid images to compare them.
通过可拖动分割线在两张叠放图片之间擦拭切换,以便对比。
Compare before and after states of the same visual with a draggable divider.
A draggable divider that wipes between two overlaid images to compare them.
English definitions are preserved verbatim from Animation Vocabulary; translations and distinctions help you choose precisely.
before-after-sliderA draggable divider that wipes between two overlaid images to compare them.
通过可拖动分割线在两张叠放图片之间擦拭切换,以便对比。
Purpose, frequency, trigger, timing, interruptibility, and gesture rules shape the implementation.
Compare two overlaid states of the same visual through a draggable divider.
Use for low-frequency, user-driven comparison with a default position that makes the difference immediately clear.
Trigger from divider drag, track click, or keyboard arrow keys.
Show both states immediately and let dragging change only the clipping boundary, with no automatic entrance.
Stop at the current position on interruption and continue from that ratio on new input.
Use pointer capture, a 44px handle, arrow-key steps, Home/End, and labels describing both sides.
Keep the operable divider while removing position tweening; keyboard and pointer update the static clip immediately.
Current parameters update visual-state CSS, semantic HTML, the required vanilla JS runtime, and an interaction prompt.
.motion-demo.motion-demo--before-after-slider {
position: relative;
display: grid;
place-items: center;
width: min(100%, 34rem);
min-height: 15rem;
padding: 2rem;
overflow: hidden;
color: #18181b;
background: #fafafa;
border: 1px solid #e4e4e7;
border-radius: 12px;
isolation: isolate;
}
.motion-demo.motion-demo--before-after-slider *, .motion-demo.motion-demo--before-after-slider *::before, .motion-demo.motion-demo--before-after-slider *::after {
box-sizing: border-box;
}
.motion-demo.motion-demo--before-after-slider .motion-surface {
position: relative;
display: grid;
gap: 0.65rem;
width: min(100%, 18rem);
padding: 1.25rem;
background: #ffffff;
border: 1px solid #e4e4e7;
border-radius: 8px;
box-shadow: 0 1px 2px rgb(0 0 0 / 6%), 0 10px 28px rgb(0 0 0 / 7%);
}
.motion-demo.motion-demo--before-after-slider .motion-line {
display: block;
width: 100%;
height: 0.5rem;
background: #e4e4e7;
border-radius: 999px;
}
.motion-demo.motion-demo--before-after-slider .motion-line:last-child { width: 62%; }
.motion-demo.motion-demo--before-after-slider .motion-button {
position: relative;
min-height: 2.75rem;
padding: 0.75rem 1.1rem;
color: white;
font: inherit;
font-weight: 700;
background: #18181b;
border: 0;
border-radius: 7px;
cursor: pointer;
}
.motion-demo.motion-demo--before-after-slider .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }
.motion-demo.motion-demo--before-after-slider .motion-action-row {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 0.85rem;
}
.motion-demo.motion-demo--before-after-slider .motion-replay-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--before-after-slider .motion-pausable-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--before-after-slider button.motion-surface {
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
.motion-demo.motion-demo--before-after-slider .motion-replay-button,
.motion-demo.motion-demo--before-after-slider .motion-undo-button,
.motion-demo.motion-demo--before-after-slider .motion-dismiss-button,
.motion-demo.motion-demo--before-after-slider .motion-pause-button {
min-height: 2.75rem;
padding: 0.65rem 0.9rem;
color: #18181b;
font: 650 0.78rem/1 ui-sans-serif, system-ui, sans-serif;
background: #ffffff;
border: 1px solid #d4d4d8;
border-radius: 7px;
cursor: pointer;
}
.motion-demo.motion-demo--before-after-slider .motion-replay-button:focus-visible,
.motion-demo.motion-demo--before-after-slider .motion-undo-button:focus-visible,
.motion-demo.motion-demo--before-after-slider .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--before-after-slider .motion-pause-button:focus-visible,
.motion-demo.motion-demo--before-after-slider [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--before-after-slider [data-swipe-target]:focus-visible {
outline: 3px solid rgb(37 99 235 / 32%);
outline-offset: 3px;
}
.motion-demo.motion-demo--before-after-slider .motion-status {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.motion-demo.motion-demo--before-after-slider .motion-ripple-ink {
position: absolute;
width: 12px;
height: 12px;
pointer-events: none;
background: rgb(255 255 255 / 72%);
border-radius: 50%;
transform: translate(-50%, -50%) scale(0.95);
opacity: 0;
}
.motion-demo.motion-demo--before-after-slider .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--before-after-slider .motion-state { position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; background: #ffffff; border: 1px solid #e4e4e7; border-radius: 8px; }
.motion-demo.motion-demo--before-after-slider .motion-state--to { background: #18181b; color: white; }
.motion-demo.motion-demo--before-after-slider .motion-scroll-track > span { color: #71717a; font: 600 0.72rem/1.2 ui-monospace, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
.motion-demo.motion-demo--before-after-slider .motion-orbit-item { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; color: white; background: #2563eb; border-radius: 50%; }
.motion-demo.motion-demo--before-after-slider .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--before-after-slider svg { width: min(100%, 20rem); height: auto; }
.motion-demo.motion-demo--before-after-slider .motion-comparison { position: relative; width: min(100%, 22rem); aspect-ratio: 16 / 9; overflow: hidden; border-radius: 8px; background: #52525b; }
.motion-demo.motion-demo--before-after-slider .motion-before, .motion-demo.motion-demo--before-after-slider .motion-after { position: absolute; inset: 0; display: grid; place-items: center; color: white; }
.motion-demo.motion-demo--before-after-slider .motion-after { overflow: hidden; background: #2563eb; clip-path: inset(0 50% 0 0); }
.motion-demo.motion-demo--before-after-slider .motion-divider { position: absolute; inset: 0 auto 0 0; width: 2px; pointer-events: none; background: white; box-shadow: 0 0 0 1px rgb(0 0 0 / 18%); transform: translate3d(0, 0, 0); }
.motion-demo.motion-demo--before-after-slider .motion-divider::after { content: "↔"; position: absolute; top: 50%; left: 50%; width: 2.25rem; height: 2.25rem; display: grid; place-items: center; color: #18181b; background: white; border-radius: 50%; transform: translate(-50%, -50%); box-shadow: 0 2px 8px rgb(0 0 0 / 18%); }
.motion-demo.motion-demo--before-after-slider .motion-comparison-control { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%; margin: 0; cursor: ew-resize; opacity: 0; }
@keyframes motion-before-after-slider-reduced-fade {
from { opacity: 0.84; }
to { opacity: 1; }
}
.force-reduced-motion .motion-demo.motion-demo--before-after-slider {
animation: motion-before-after-slider-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.force-reduced-motion .motion-demo.motion-demo--before-after-slider *,
.force-reduced-motion .motion-demo.motion-demo--before-after-slider *::before,
.force-reduced-motion .motion-demo.motion-demo--before-after-slider *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.force-reduced-motion .motion-demo.motion-demo--before-after-slider .motion-button,
.force-reduced-motion .motion-demo.motion-demo--before-after-slider [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--before-after-slider [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--before-after-slider [data-spring-target] {
transform: none !important;
}
.force-reduced-motion .motion-demo.motion-demo--before-after-slider .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--before-after-slider .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--before-after-slider .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
@media (prefers-reduced-motion: reduce) {
.motion-demo.motion-demo--before-after-slider {
animation: motion-before-after-slider-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.motion-demo.motion-demo--before-after-slider *, .motion-demo.motion-demo--before-after-slider *::before, .motion-demo.motion-demo--before-after-slider *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.motion-demo.motion-demo--before-after-slider .motion-button,
.motion-demo.motion-demo--before-after-slider [data-reorder-item],
.motion-demo.motion-demo--before-after-slider [data-swipe-target],
.motion-demo.motion-demo--before-after-slider [data-spring-target] {
transform: none !important;
}
.motion-demo.motion-demo--before-after-slider .motion-path { stroke-dashoffset: 0 !important; }
.motion-demo.motion-demo--before-after-slider .motion-word--from { opacity: 0 !important; visibility: hidden; }
.motion-demo.motion-demo--before-after-slider .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}Keep the operable divider while removing position tweening; keyboard and pointer update the static clip immediately.
| Parameter | Range / options | Default | Purpose |
|---|---|---|---|
position | 10%–90% | 50% | Adjusts divider position. |
duration | 80ms–1200ms | 180ms | Controls the time required for one motion cycle. |