BeforeAfter Comparison Slider
Neobrutalist-style image sliding comparison component. Used to visually compare the difference between the same image before (Before) and after (After) modification. Built on native <input type="range"> combined with CSS clip-path masking, zero dependencies, touch-friendly.
Demo
水平方向
垂直方向
提示:拖拽滑块的位置过渡尊重系统的「减少动态效果」设置。启用后分割线与裁剪区域将即时跟随,不再带有缓动动画。
Installation
pnpm dlx brutx-vue@latest add before-afterUsage
<script setup>
import { BeforeAfter } from 'brutx-ui-vue'
const original = '/images/before.jpg'
const modified = '/images/after.jpg'
</script>
<template>
<BeforeAfter
:before="original"
:after="modified"
:default-value="50"
/>
</template>Vertical Orientation
Switch the divider direction via the orientation prop. Default horizontal is left-right dragging; when set to vertical, the divider becomes horizontal with up-down dragging, clipping proceeds from bottom to top (clip-path: inset(0 0 ...)), and the handle icon rotates 90 degrees automatically.
<BeforeAfter
:before="original"
:after="modified"
orientation="vertical"
:default-value="50"
/>Variants
| Variant | Description |
|---|---|
horizontal | Default direction, left-right drag divider |
vertical | Vertical direction, up-down drag divider, clipping proceeds from bottom to top |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
before | string | — | URL of the left/bottom original image (required) |
after | string | — | URL of the right/top comparison image (required) |
beforeAlt | string | locale: beforeAfter.before | alt attribute for the original image |
afterAlt | string | locale: beforeAfter.after | alt attribute for the comparison image |
modelValue | number | — | Divider position (v-model, 0-100) |
defaultValue | number | 50 | Initial percentage position of the divider (0-100) |
disabled | boolean | false | Whether to disable drag interaction |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Divider direction; vertical clips from bottom to top |
iconSize | 'xs' | 'sm' | 'default' | 'lg' | 'xl' | '2xl' | 'default' | Size of the drag handle icon |
class | string | "" | Custom CSS class for the container |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | number | Divider position changed |
Accessibility
- Keyboard control: Supports keyboard arrow keys to control divider position; keyboard interaction is disabled in
disabledstate - Motion reduction: The component respects the
prefers-reduced-motionsystem setting. When the user enables "reduce motion" (viauseReducedMotionlistening forprefers-reduced-motion: reduce):- Slider transition removal: The divider, drag handle, and
clip-pathclipping layer no longer apply transition styles. Position changes take effect immediately during dragging without easing animations - Interaction preserved: Dragging, keyboard arrow keys,
disabledand other behaviors are completely unaffected; only visual transitions are removed - Real-time response: Preference changes take effect immediately without remounting the component
- This mechanism is implemented through a reactive
motionTransitioncomputed property: in motion reduction mode it returns an empty string, causing related elements to skip transition styles
- Slider transition removal: The divider, drag handle, and
FAQ
Q: What is the technical approach of this component?
A: This component is built on the native browser <input type="range"> combined with CSS clip-path masking, with the following advantages:
- Touch-friendly: Perfectly adapts to swipe gestures on mobile phones, tablets, and other mobile devices
- Zero dependencies: No need to load any additional external gesture interaction JS libraries, package size is nearly zero
- Perfect layout: The underlying
clip-path: inset(...)clipping method does not break the image aspect ratio, ensuring alignment across platforms - Adaptive ratio: The container automatically calculates
aspect-ratiobased on the actual image dimensions, no need to manually specify the ratio. Before images load, default ratios are used as placeholders (horizontal 16:9, vertical 9:16)