SlideVerify

View source
A smooth slide-to-verify component.

Introduction

MSlideVerify is a slide-to-verify component with drag interaction and state transition animations. Built on motion-v for smooth transitions, it is suitable for form verification, sensitive operation confirmation, and similar scenarios.

Uses the Motion animation library for drag interaction and state transition animations

Usage

Hold and drag the slider to the right to reach the threshold and pass verification:

请向右滑动验证
<script setup lang="ts">
const value = ref(false)
</script>

<template>
  <MSlideVerify v-model="value" />
</template>

threshold Pass Threshold

threshold determines the drag ratio required to pass. Defaults to 0.9. Lowering it relaxes the requirement:

Drag halfway to pass
<template>
  <MSlideVerify :threshold="0.5" text="Drag halfway to pass" />
</template>

text Hint Text

text sets the hint before verification; successText sets the text displayed after passing:

Hold and drag right
<template>
  <MSlideVerify text="Hold and drag right" success-text="Human verification passed" />
</template>

icon Slider Icon

icon sets the icon before verification; successIcon sets the icon displayed after passing:

请向右滑动验证
<template>
  <MSlideVerify icon="i-lucide-arrow-right" success-icon="i-lucide-shield-check" />
</template>

size Size

Use size to adjust the component size:

请向右滑动验证
<template>
  <MSlideVerify size="md" />
</template>

disabled Disabled State

disabled freezes the slider. The cursor cannot drag and the unverified state is preserved:

请向右滑动验证
<template>
  <MSlideVerify disabled />
</template>

Examples

Inheriting Field Context

When placed inside UFormField, it receives field size and error state, and the slider renders according to the form state:

请向右滑动验证
Example error state
<template>
  <UFormField label="Slide Verification" size="xs" error="Example error state">
    <MSlideVerify />
  </UFormField>
</template>

Inside UFieldGroup

When combined with a reset button, they share the UFieldGroup size. The slider area and button maintain a unified height:

请向右滑动验证
<template>
  <UFieldGroup size="xs">
    <MSlideVerify class="flex-1" />
    <UButton icon="i-lucide-rotate-ccw" color="neutral" variant="subtle" />
  </UFieldGroup>
</template>

Custom Slider Content

The slider slot takes over the slider's inner rendering. Read verified and progress to dynamically display progress:

请向右滑动验证
0%
<script setup lang="ts">
const isVerified = ref(false)
</script>

<template>
  <MSlideVerify v-model="isVerified" class="w-sm">
    <template #slider="{ verified, progress }">
      <UIcon v-if="verified" name="i-lucide-check" />
      <span v-else class="text-xs font-medium">{{ Math.round(progress * 100) }}%</span>
    </template>
  </MSlideVerify>
</template>

Event Callbacks

Drag start fires dragStart. Releasing fires dragEnd with a success boolean. Reaching the threshold additionally fires success:

请向右滑动验证
<script setup lang="ts">
const isVerified = ref(false)
const slideVerifyRef = useTemplateRef('slideVerifyRef')
const toast = useToast()

function handleSuccess() {
  toast.add({
    title: '验证成功',
    description: '已触发 success 事件',
    color: 'success'
  })
}

function handleDragEnd(success: boolean) {
  if (!success) {
    toast.add({
      title: '验证失败',
      description: '请继续滑动',
      color: 'neutral'
    })
  }
}

function handleReset() {
  slideVerifyRef.value?.reset()
  toast.add({
    title: '已重置',
    color: 'neutral'
  })
}
</script>

<template>
  <div class="w-sm flex gap-4">
    <MSlideVerify
      ref="slideVerifyRef"
      v-model="isVerified"
      class="flex-1"
      @success="handleSuccess"
      @drag-end="handleDragEnd"
    />
    <UButton size="sm" color="neutral" variant="outline" @click="handleReset"> 重置验证 </UButton>
  </div>
</template>

API

Props

Prop Default Type
text'请向右滑动验证'string

待滑动时的提示文本

icon'i-lucide-chevrons-right'any

滑块图标

successText'验证成功'string

验证成功时的提示文本

successIcon'i-lucide-check'any

验证成功时的图标

threshold0.9number

完成验证所需的阈值百分比(0-1)

size'md'"md" | "xs" | "sm" | "lg" | "xl"

尺寸大小

idstring
namestring
disabledboolean

是否禁用

modelValuefalseboolean
uiRecord<string, ClassNameValue> & { root?: SlotClass; track?: SlotClass; fill?: SlotClass; text?: SlotClass; slider?: SlotClass; icon?: SlotClass; }

Emits

Event Type
update:modelValue[value: boolean]
success[]
dragStart[]
dragEnd[success: boolean]

Slots

Slot Type
slider{ verified?: boolean; progress: number; }

Expose

You can access the typed component instance via useTemplateRef.

NameType
reset()Promise<void>

Reset the verification state

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    slideVerify: {
      slots: {
        root: 'w-full relative flex items-center select-none overflow-hidden rounded-md border transition-colors duration-300',
        track: 'absolute inset-0',
        fill: 'absolute inset-y-0 left-0 bg-primary/20 opacity-60',
        text: 'absolute inset-0 flex items-center justify-center font-medium pointer-events-none',
        slider: 'relative z-10 flex shrink-0 touch-none items-center justify-center rounded-md shadow-sm transition-colors',
        icon: 'shrink-0'
      },
      variants: {
        size: {
          xs: {
            root: 'p-1',
            text: 'text-xs',
            slider: 'px-2 py-1',
            icon: 'size-4'
          },
          sm: {
            root: 'p-1.5',
            text: 'text-xs',
            slider: 'px-2.5 py-1.5',
            icon: 'size-4'
          },
          md: {
            root: 'p-1.5',
            text: 'text-sm',
            slider: 'px-2.5 py-1.5',
            icon: 'size-5'
          },
          lg: {
            root: 'p-2',
            text: 'text-sm',
            slider: 'px-3 py-2',
            icon: 'size-5'
          },
          xl: {
            root: 'p-2',
            text: 'text-base',
            slider: 'px-3 py-2',
            icon: 'size-6'
          }
        },
        disabled: {
          true: {
            root: 'opacity-50 cursor-not-allowed'
          }
        },
        verified: {
          true: {
            root: 'bg-success border-transparent',
            slider: 'bg-white/90',
            icon: 'text-success'
          },
          false: {
            root: 'bg-elevated border-default',
            slider: 'bg-default cursor-grab active:cursor-grabbing ring-1 ring-default',
            icon: 'text-primary'
          }
        },
        fieldGroup: {
          horizontal: {
            root: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]'
          },
          vertical: {
            root: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
          }
        }
      },
      defaultVariants: {
        size: 'md'
      }
    }
  }
})

Changelog

No recent changes
Copyright © 2025 - 2026 YiXuan - MIT License