English | 中文文档
A comprehensive, easy-to-use, and high-performance Vue custom directives library supporting both Vue 2 and Vue 3.
- 🎯 Comprehensive - 57 commonly used directives and 57 composables
- 🔄 Vue 2/3 Compatible - Single codebase supports both Vue 2 and Vue 3
- 📦 Tree-shakable - Import only what you need
- 🔒 TypeScript - Full TypeScript support with type definitions
- 🚀 SSR Friendly - Multiple directives support SSR out of the box
- 📦 Multiple Formats - ESM, CJS, and IIFE (CDN) formats available
- ⚡ Zero Dependencies - Lightweight with minimal bundle size
- 🎨 Composables - Every directive has a corresponding composable for Composition API
- 🔧 Utility Exports - Export
configurePermission,getPermissionConfigand other utilities for advanced usage
Try it online with StackBlitz or CodeSandbox:
| Demo | StackBlitz | CodeSandbox |
|---|---|---|
| Vue 3 | ||
| Vue 2 |
Try the interactive Playground to configure directives and generate code:
- 57+ Directives - Full coverage of all Directix directives
- Vue 2 & Vue 3 - Generate code for either version
- Composables - Generate composable API code
- TypeScript Ready - Full type definitions included
- Monaco Editor - Full-featured code editor with syntax highlighting
- Live Preview - See directive effects in real-time
Each directive documentation page also includes a code generator for quick code snippets.
# npm
npm install directix
# yarn
yarn add directix
# pnpm
pnpm add directixFor Vue 2.0-2.6, you need to install @vue/composition-api:
npm install @vue/composition-apiVue 2.7+ has built-in Composition API support, so no additional dependencies are needed.
You can also use Directix via CDN:
<!-- Vue 3 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/directix/dist/index.iife.min.js"></script>
<!-- Vue 2.7+ -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/directix/dist/index.iife.min.js"></script>The CDN build works seamlessly with both Vue 2 and Vue 3.
- Vue 2.0+ or Vue 3.0+
- Node.js 12.20+ (for build tools)
- For Vue 2.0-2.6:
@vue/composition-apiis required
// Vue 3
import { createApp } from 'vue'
import Directix from 'directix'
const app = createApp(App)
app.use(Directix)
// Or register specific directives only
app.use(Directix, {
directives: ['click-outside', 'copy', 'debounce']
})// Vue 2
import Vue from 'vue'
import Directix from 'directix'
Vue.use(Directix)import { vClickOutside, vCopy, vDebounce } from 'directix'
// Vue 3
app.directive('click-outside', vClickOutside)
app.directive('copy', vCopy)
// Vue 2
Vue.directive('click-outside', vClickOutside)Every directive has a corresponding composable for use with the Composition API:
import { useClickOutside, useCopy, useDebounce } from 'directix'
// In setup() or <script setup>
const { copy, copied } = useCopy({ source: textRef })
const { isHovering, bind } = useHover({ onEnter: handleEnter })
const { run: debouncedSearch } = useDebounce({ handler: search, wait: 500 })See the Composables section below for all available composables.
Directix provides a Nuxt module for seamless integration with Nuxt 3 applications.
The Nuxt module is included in the main package. Simply add it to your nuxt.config.ts:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['directix/nuxt'],
directix: {
// Enable/disable the module (default: true)
enabled: true,
// Only include specific directives (optional)
include: ['v-click-outside', 'v-copy', 'v-debounce'],
// Or exclude specific directives (optional)
exclude: ['v-ripple'],
// Default options for directives (optional)
directiveOptions: {
'v-permission': {
config: {
getPermissions: () => ['read', 'write']
}
}
},
// Auto-import composables (default: true)
autoImportComposables: true
}
})Directives are automatically registered and composables are auto-imported:
<template>
<div v-click-outside="handleClose">
<button v-copy="text">Copy</button>
</div>
</template>
<script setup>
// Composables are auto-imported, no need to import manually
const { copy, copied } = useCopy({ source: text })
const { isHovering } = useHover({ onEnter: handleEnter })
</script>Directives that are not SSR-compatible will only run on the client side. The Nuxt module handles this automatically.
| Directive | Description | SSR |
|---|---|---|
v-click-outside |
Detect clicks outside an element | ❌ |
v-click-delay |
Delay click execution to prevent double clicks | ✅ |
v-debounce |
Debounce event handlers | ✅ |
v-throttle |
Throttle event handlers | ✅ |
v-long-press |
Detect long press events | ❌ |
v-hover |
Hover state detection | ❌ |
v-hotkey |
Keyboard shortcut binding | ✅ |
v-touch |
Touch gesture detection (swipe, pinch, rotate) | ❌ |
v-swipe |
Swipe gesture detection with mouse support | ❌ |
| Directive | Description | SSR |
|---|---|---|
v-copy |
Copy text to clipboard | ❌ |
v-focus |
Auto focus an element | ✅ |
v-mask |
Input masking | ❌ |
v-trim |
Trim input whitespace | ✅ |
v-money |
Currency format input | ❌ |
v-number |
Number format input | ❌ |
v-ellipsis |
Text ellipsis overflow | ✅ |
| Directive | Description | SSR |
|---|---|---|
v-uppercase |
Convert text to uppercase | ✅ |
v-lowercase |
Convert text to lowercase | ✅ |
v-capitalcase |
Capitalize first letter | ✅ |
v-truncate |
Truncate text with ellipsis | ✅ |
| Directive | Description | SSR |
|---|---|---|
v-lazy |
Lazy load images | ❌ |
v-intersect |
Detect element intersection | ❌ |
v-visible |
Control element visibility | ✅ |
v-loading |
Show loading overlay | ✅ |
| Directive | Description | SSR |
|---|---|---|
v-scroll |
Scroll event handling | ❌ |
v-infinite-scroll |
Infinite scrolling | ❌ |
v-sticky |
Sticky positioning | ❌ |
v-pull-refresh |
Pull to refresh functionality | ❌ |
v-virtual-list |
Virtual list for large datasets | ❌ |
| Directive | Description | SSR |
|---|---|---|
v-permission |
Permission-based element control | ✅ |
v-sanitize |
Sanitize HTML content | ✅ |
| Directive | Description | SSR |
|---|---|---|
v-ripple |
Material design ripple effect | ❌ |
v-draggable |
Make elements draggable | ❌ |
| Directive | Description | SSR |
|---|---|---|
v-resize |
Element resize observer | ❌ |
v-mutation |
DOM mutation observer | ❌ |
| Directive | Description | SSR |
|---|---|---|
v-tooltip |
Tooltip component | ❌ |
v-image-preview |
Image preview with zoom | ❌ |
v-countdown |
Countdown timer display | ✅ |
v-print |
Print element content | ❌ |
v-watermark |
Watermark overlay | ✅ |
v-skeleton |
Skeleton loading placeholder | ✅ |
v-progress |
Progress bar animation | ❌ |
v-counter |
Animated number counter | ✅ |
| Directive | Description | SSR |
|---|---|---|
v-pan |
Pan/drag gesture | ❌ |
v-pinch |
Pinch/zoom gesture | ❌ |
v-rotate-gesture |
Rotation gesture | ❌ |
| Directive | Description | SSR |
|---|---|---|
v-blur |
Background blur overlay | ❌ |
v-fade |
Fade in/out transition | ✅ |
v-parallax |
Parallax scrolling effect | ❌ |
v-lottie |
Lottie animation player | ❌ |
v-typewriter |
Typewriter animation | ✅ |
v-click-wave |
Click wave effect | ❌ |
| Directive | Description | SSR |
|---|---|---|
v-export |
Export data (CSV/JSON/HTML) | ❌ |
v-highlight |
Keyword highlighting | ✅ |
v-emoji |
Emoji input filter | ❌ |
v-context-menu |
Right-click context menu | ❌ |
v-fullscreen |
Fullscreen toggle | ❌ |
✅ = SSR compatible | ❌ = Not SSR compatible
Every directive has a corresponding composable function for use with the Composition API. All composables are exported from directix:
| Composable | Description |
|---|---|
useClickOutside |
Detect clicks outside an element |
useClickDelay |
Delay click execution |
useDebounce |
Debounce function calls |
useThrottle |
Throttle function calls |
useLongPress |
Detect long press gestures |
useHover |
Track hover state |
useHotkey |
Handle keyboard shortcuts |
useTouch |
Detect touch gestures |
useSwipe |
Detect swipe gestures |
| Composable | Description |
|---|---|
useCopy |
Copy text to clipboard |
useFocus |
Manage element focus |
useMask |
Input masking |
useTrim |
Trim input whitespace |
useMoney |
Currency formatting |
useNumber |
Number formatting |
useEllipsis |
Text ellipsis overflow |
| Composable | Description |
|---|---|
useUppercase |
Transform to uppercase |
useLowercase |
Transform to lowercase |
useCapitalcase |
Capitalize text |
useTruncate |
Truncate text |
| Composable | Description |
|---|---|
useLazy |
Lazy load images |
useIntersect |
Detect element intersection |
useVisible |
Control element visibility |
useLoading |
Show loading overlay |
| Composable | Description |
|---|---|
useScroll |
Track scroll position |
useInfiniteScroll |
Infinite scrolling |
useSticky |
Sticky positioning |
usePullRefresh |
Pull to refresh |
useVirtualList |
Virtual list for large datasets |
| Composable | Description |
|---|---|
usePermission |
Permission checking |
useSanitize |
Sanitize HTML content |
useRipple |
Material design ripple effect |
useDraggable |
Make elements draggable |
useResize |
Element resize observer |
useMutation |
DOM mutation observer |
useTooltip |
Tooltip control |
useImagePreview |
Image preview with zoom |
useCountdown |
Countdown timer |
usePrint |
Print content |
useWatermark |
Watermark overlay |
useSkeleton |
Skeleton loading state |
useProgress |
Progress bar control |
useCounter |
Animated number counter |
usePan |
Pan gesture detection |
usePinch |
Pinch gesture detection |
useRotateGesture |
Rotation gesture detection |
useBlur |
Blur overlay control |
useFade |
Fade transition control |
useParallax |
Parallax scrolling |
useLottie |
Lottie animation control |
useTypewriter |
Typewriter effect |
useExport |
Data export utilities |
useHighlight |
Keyword highlighting |
useEmoji |
Emoji filtering |
useContextMenu |
Context menu control |
useFullscreen |
Fullscreen mode control |
useClickWave |
Click wave effect |
<script setup>
import { ref } from 'vue'
import { useCopy, useHover, useDebounce } from 'directix'
// useCopy
const text = ref('Hello World')
const { copy, copied } = useCopy({ source: text })
// useHover
const buttonRef = ref()
const { isHovering, bind } = useHover({
onEnter: () => console.log('Entered'),
onLeave: () => console.log('Left')
})
// useDebounce
const { run: debouncedSearch } = useDebounce({
handler: (query) => fetchResults(query),
wait: 500
})
</script>
<template>
<button @click="copy()">
{{ copied ? 'Copied!' : 'Copy' }}
</button>
<button ref="buttonRef" :class="{ active: isHovering }">
Hover me
</button>
</template>Detect clicks outside an element, useful for closing dropdowns, modals, etc.
<template>
<div v-click-outside="closeDropdown">
<button @click="show = !show">Toggle</button>
<div v-if="show">Dropdown content</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useClickOutside } from 'directix'
const show = ref(false)
function closeDropdown() {
show.value = false
}
// Composable usage
const containerRef = ref()
useClickOutside(containerRef, () => {
show.value = false
})
</script>Copy text to clipboard with a simple directive.
<template>
<!-- Simple usage -->
<button v-copy="textToCopy">Copy to clipboard</button>
<!-- With callbacks -->
<button v-copy="{ value: text, onSuccess: handleSuccess, onError: handleError }">
Copy with callback
</button>
</template>
<script setup>
import { ref } from 'vue'
import { useCopy } from 'directix'
const textToCopy = 'Hello, World!'
function handleSuccess(text) {
console.log('Copied:', text)
}
function handleError(error) {
console.error('Copy failed:', error)
}
// Composable usage
const sourceText = ref('Hello World')
const { copy, copied } = useCopy({ source: sourceText })
</script>Debounce event handlers to limit execution frequency.
<template>
<!-- Default: 300ms -->
<input v-debounce="handleInput" />
<!-- Custom wait time with modifier -->
<input v-debounce:500ms="handleInput" />
<!-- With options object -->
<input v-debounce="{ handler: handleInput, wait: 500, leading: true }" />
</template>
<script setup>
import { useDebounce } from 'directix'
function handleInput(event) {
console.log('Debounced input:', event.target.value)
}
// Composable usage
const { run: debouncedSearch, cancel } = useDebounce({
handler: (query) => fetchResults(query),
wait: 500
})
</script>Throttle event handlers to limit execution frequency.
<template>
<!-- Default: 300ms -->
<button v-throttle="handleClick">Throttled click</button>
<!-- Custom wait time with modifier -->
<button v-throttle:1s="handleClick">1 second throttle</button>
<!-- With options object -->
<button v-throttle="{ handler: handleClick, wait: 1000, leading: true, trailing: false }">
Throttle with options
</button>
</template>
<script setup>
import { useThrottle } from 'directix'
function handleClick() {
console.log('Throttled click')
}
// Composable usage
const { run: throttledScroll, cancel } = useThrottle({
handler: (position) => updatePosition(position),
wait: 100
})
</script>Auto focus an element when mounted.
<template>
<!-- Simple usage -->
<input v-focus />
<!-- With options -->
<input v-focus="{ focus: true, refocus: true }" />
</template>
<script setup>
import { useFocus } from 'directix'
// Composable usage
const { focus, blur, hasFocus } = useFocus()
</script>Control element visibility based on user permissions.
<template>
<!-- Simple permission check -->
<button v-permission="'admin'">Admin Only</button>
<!-- Multiple permissions (OR logic) -->
<button v-permission="['admin', 'editor']">Admin or Editor</button>
<!-- AND logic -->
<button v-permission="{ value: ['read', 'write'], mode: 'every' }">
Requires both permissions
</button>
<!-- Disable instead of remove -->
<button v-permission="{ value: 'admin', action: 'disable' }">
Disabled for non-admin
</button>
</template>
<script setup>
import { configurePermission, usePermission } from 'directix'
configurePermission({
getPermissions: () => ['read', 'write'],
getRoles: () => ['editor'],
roleMap: {
admin: ['*'],
editor: ['read', 'write', 'edit']
}
})
// Composable usage
const { hasPermission, hasAnyPermission, hasAllPermissions } = usePermission()
const canEdit = hasPermission('edit')
</script>Lazy load images when they enter the viewport.
<template>
<!-- Simple usage -->
<img v-lazy="imageUrl" />
<!-- With placeholder and error image -->
<img v-lazy="{ src: imageUrl, placeholder: '/placeholder.png', error: '/error.png' }" />
</template>
<script setup>
import { useLazy } from 'directix'
// Composable usage
const { load, state, loaded } = useLazy({
src: 'image.jpg',
preload: 100
})
</script>Input masking for formatted input.
<template>
<!-- Phone number -->
<input v-mask="'(###) ###-####'" placeholder="Phone" />
<!-- Date -->
<input v-mask="'##/##/####'" placeholder="MM/DD/YYYY" />
<!-- SSN -->
<input v-mask="{ mask: '###-##-####', placeholder: '_' }" placeholder="SSN" />
</template>
<script setup>
import { useMask } from 'directix'
// Composable usage
const { maskedValue, unmaskedValue, update } = useMask({
mask: '(###) ###-####',
value: '1234567890'
})
</script>Show loading overlay on elements.
<template>
<!-- Simple usage -->
<div v-loading="isLoading">Content</div>
<!-- With options -->
<div v-loading="{ value: isLoading, text: 'Loading...', lock: true }">
Content with locked scroll
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useLoading } from 'directix'
const isLoading = ref(true)
// Composable usage
const { show, hide, setText } = useLoading({
text: 'Loading...',
lock: true
})
</script>Sanitize HTML content to prevent XSS attacks.
<template>
<!-- Simple usage -->
<div v-sanitize="userContent"></div>
<!-- With custom allowed tags -->
<div v-sanitize="{ html: userContent, allowedTags: ['b', 'i', 'u'] }"></div>
</template>
<script setup>
import { useSanitize } from 'directix'
// Composable usage
const { sanitize, setAllowedTags } = useSanitize({
allowedTags: ['b', 'i', 'u', 'a']
})
const cleanHtml = sanitize(dirtyHtml)
</script>Display tooltips on hover or click.
<template>
<!-- Simple usage -->
<button v-tooltip="'Tooltip content'">Hover me</button>
<!-- With options -->
<button v-tooltip="{ content: 'Tooltip', placement: 'bottom', trigger: 'click' }">
Click me
</button>
</template>
<script setup>
import { useTooltip } from 'directix'
// Composable usage
const { show, hide, updateContent, updatePosition } = useTooltip({
content: 'Tooltip content',
placement: 'top'
})
</script>Preview images with zoom and gesture support.
<template>
<!-- Simple usage -->
<img v-image-preview src="thumbnail.jpg" data-preview="full.jpg" />
<!-- With options -->
<img v-image-preview="{ src: 'thumbnail.jpg', previewSrc: 'full.jpg', enablePinchZoom: true }" />
</template>
<script setup>
import { useImagePreview } from 'directix'
// Composable usage
const { open, close, zoom, rotate } = useImagePreview({
enablePinchZoom: true
})
</script>Make elements draggable.
<template>
<!-- Simple usage -->
<div v-draggable>Drag me</div>
<!-- With constraints -->
<div v-draggable="{ axis: 'x', bounds: 'parent' }">Horizontal drag only</div>
</template>
<script setup>
import { useDraggable } from 'directix'
// Composable usage
const { position, isDragging, reset } = useDraggable({
axis: 'x',
bounds: 'parent'
})
</script>Transform text case.
<template>
<input v-uppercase placeholder="Auto uppercase" />
<input v-lowercase placeholder="Auto lowercase" />
<input v-capitalcase placeholder="Capitalize first letter" />
</template>
<script setup>
import { useUppercase, useLowercase, useCapitalcase } from 'directix'
// Composable usage
const { transform: toUppercase } = useUppercase()
const { transform: toLowercase } = useLowercase()
const { transform: toCapitalcase } = useCapitalcase()
</script>Truncate text with ellipsis.
<template>
<!-- Simple usage -->
<p v-truncate="50">Long text here...</p>
<!-- With options -->
<p v-truncate="{ length: 100, suffix: '...', position: 'end' }">Long text...</p>
</template>
<script setup>
import { useTruncate } from 'directix'
// Composable usage
const { truncate } = useTruncate({ length: 100, suffix: '...' })
const shortText = truncate(longText)
</script>Detect touch gestures.
<template>
<div v-touch="{ onSwipe: handleSwipe, onPinch: handlePinch }">
Swipe or pinch here
</div>
</template>
<script setup>
import { useTouch } from 'directix'
function handleSwipe(direction) {
console.log('Swiped:', direction) // 'left', 'right', 'up', 'down'
}
function handlePinch(scale) {
console.log('Pinched:', scale)
}
// Composable usage
const { onSwipe, onPinch, onRotate } = useTouch({
onSwipe: handleSwipe
})
</script>Trim input whitespace.
<template>
<!-- Trim on blur (default) -->
<input v-trim />
<!-- Trim on input -->
<input v-trim="{ position: 'both', event: 'input' }" />
</template>
<script setup>
import { useTrim } from 'directix'
// Composable usage
const { trim, trimLeft, trimRight } = useTrim({ position: 'both' })
</script>Currency format input.
<template>
<input v-money="{ prefix: '$', precision: 2 }" placeholder="Enter amount" />
</template>
<script setup>
import { useMoney } from 'directix'
// Composable usage
const { format, parse } = useMoney({ prefix: '$', precision: 2 })
const formatted = format(1234.56) // "$1,234.56"
</script>Number format input.
<template>
<input v-number="{ precision: 2, min: 0, max: 100 }" placeholder="Enter number" />
</template>
<script setup>
import { useNumber } from 'directix'
// Composable usage
const { format, parse } = useNumber({ precision: 2, min: 0, max: 100 })
</script>Delay click execution to prevent double clicks.
<template>
<!-- Default: 300ms delay -->
<button v-click-delay="handleClick">Click me</button>
<!-- Custom delay time -->
<button v-click-delay="{ handler: handleClick, delay: 500 }">500ms delay</button>
</template>
<script setup>
import { useClickDelay } from 'directix'
function handleClick() {
console.log('Clicked (delayed)')
}
// Composable usage
const { run: delayedClick, cancel } = useClickDelay({
handler: handleClick,
delay: 300
})
</script>Countdown timer display.
<template>
<!-- Simple usage -->
<span v-countdown="{ time: 60 }"></span>
<!-- With callbacks -->
<span v-countdown="{ time: 60, onTick: handleTick, onComplete: handleComplete }"></span>
<!-- Custom format -->
<span v-countdown="{ time: 3600, format: 'mm:ss' }"></span>
</template>
<script setup>
import { useCountdown } from 'directix'
function handleTick(remaining) {
console.log('Remaining:', remaining)
}
function handleComplete() {
console.log('Countdown complete!')
}
// Composable usage
const { start, pause, reset, remaining } = useCountdown({
time: 60,
onTick: handleTick,
onComplete: handleComplete
})
</script>Text ellipsis overflow with tooltip.
<template>
<!-- Simple usage -->
<div v-ellipsis style="width: 200px;">Long text that will be truncated</div>
<!-- With custom lines -->
<div v-ellipsis="{ lines: 2 }">Multi-line text with ellipsis</div>
</template>
<script setup>
import { useEllipsis } from 'directix'
// Composable usage
const { isEllipsisActive, checkEllipsis } = useEllipsis({ lines: 1 })
</script>Keyboard shortcut binding.
<template>
<!-- Simple usage -->
<div v-hotkey="{ 'ctrl+s': handleSave, 'ctrl+c': handleCopy }">
Press Ctrl+S to save
</div>
<!-- With modifiers -->
<input v-hotkey="{ 'enter': submit, 'escape': cancel }" />
</template>
<script setup>
import { useHotkey } from 'directix'
function handleSave() {
console.log('Saving...')
}
function handleCopy() {
console.log('Copying...')
}
// Composable usage
const { bind, unbind, unbindAll } = useHotkey({
'ctrl+s': handleSave
})
</script>Print element content.
<template>
<!-- Simple usage -->
<button v-print="printRef">Print</button>
<div ref="printRef">Content to print</div>
<!-- Print self -->
<div v-print="{ self: true }">Click to print this content</div>
</template>
<script setup>
import { ref } from 'vue'
import { usePrint } from 'directix'
const printRef = ref()
// Composable usage
const { print, printElement } = usePrint({
onBefore: () => console.log('Printing...'),
onComplete: () => console.log('Printed!')
})
</script>Pull to refresh functionality.
<template>
<div v-pull-refresh="handleRefresh" style="height: 400px; overflow: auto;">
Pull down to refresh
</div>
<!-- With options -->
<div v-pull-refresh="{ handler: handleRefresh, threshold: 80, disabled: false }">
Content
</div>
</template>
<script setup>
import { usePullRefresh } from 'directix'
async function handleRefresh() {
// Fetch new data
await fetchData()
}
// Composable usage
const { isLoading, disable, enable } = usePullRefresh({
handler: handleRefresh
})
</script>Swipe gesture detection with mouse support.
<template>
<div v-swipe="handleSwipe" style="height: 200px;">
Swipe in any direction
</div>
<!-- With options -->
<div v-swipe="{ onSwipe: handleSwipe, threshold: 50, enableMouse: true }">
Swipe or drag with mouse
</div>
</template>
<script setup>
import { useSwipe } from 'directix'
function handleSwipe(direction) {
console.log('Swiped:', direction) // 'left', 'right', 'up', 'down'
}
// Composable usage
const { direction, lengthX, lengthY } = useSwipe({
onSwipe: handleSwipe,
threshold: 50
})
</script>Virtual list for rendering large datasets efficiently.
<template>
<div v-virtual-list="{ items: list, itemSize: 50 }" style="height: 500px;">
<template #default="{ item, index }">
<div :key="index">{{ item.name }}</div>
</template>
</div>
</template>
<script setup>
const list = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` }))
import { useVirtualList } from 'directix'
// Composable usage
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
largeList,
{ itemHeight: 50 }
)
</script>Watermark overlay.
<template>
<!-- Simple usage -->
<div v-watermark="'Confidential'" style="width: 100%; height: 400px;">
Protected content
</div>
<!-- With options -->
<div v-watermark="{ content: 'Draft', fontSize: 16, color: '#ccc', rotate: -20 }">
Content with watermark
</div>
</template>
<script setup>
import { useWatermark } from 'directix'
// Composable usage
const { show, hide, update } = useWatermark({
content: 'Confidential',
fontSize: 16,
color: '#ccc'
})
</script>Background blur overlay effect.
<template>
<!-- Simple blur -->
<div v-blur="isBlurred">Content behind blur</div>
<!-- With radius -->
<div v-blur="15">Blur with 15px radius</div>
<!-- With options -->
<div v-blur="{
visible: isBlurred,
radius: 20,
overlayColor: 'rgba(255, 255, 255, 0.3)',
lockScroll: true
}">
Content
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useBlur } from 'directix'
const isBlurred = ref(false)
// Composable usage
const { show, hide, toggle } = useBlur({
radius: 10,
overlayColor: 'rgba(0, 0, 0, 0.5)'
})
</script>Fade in/out transition effect.
<template>
<!-- Toggle visibility with fade -->
<div v-fade="isVisible">Fade content</div>
<!-- Fade in only -->
<div v-fade="'in'">Fade in</div>
<!-- With options -->
<div v-fade="{
visible: isVisible,
duration: 500,
easing: 'ease-in-out',
onComplete: () => console.log('Fade complete')
}">
Content
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useFade } from 'directix'
const isVisible = ref(true)
// Composable usage
const { fadeIn, fadeOut, toggle } = useFade({
duration: 300,
easing: 'ease'
})
</script>Parallax scrolling effect.
<template>
<!-- Simple parallax -->
<div v-parallax>Parallax content</div>
<!-- With speed factor -->
<div v-parallax="0.3">Slower parallax</div>
<!-- With options -->
<div v-parallax="{
speed: 0.5,
reverse: true,
mobileBreakpoint: 768
}">
Reverse parallax, disabled on mobile
</div>
</template>
<script setup>
import { useParallax } from 'directix'
// Composable usage
const { offset, progress, enabled } = useParallax({
speed: 0.5,
reverse: false
})
</script>Lottie animation player.
<template>
<!-- With URL -->
<div v-lottie="'https://assets.example.com/animation.json'"></div>
<!-- With animation data -->
<div v-lottie="animationData"></div>
<!-- With options -->
<div v-lottie="{
animationData: animationData,
autoplay: true,
loop: true,
speed: 1.5,
onComplete: () => console.log('Done')
}"></div>
</template>
<script setup>
import animationData from './animation.json'
import { useLottie } from 'directix'
// Composable usage
const { play, pause, stop, setSpeed, setDirection } = useLottie({
animationData,
autoplay: true,
loop: true
})
</script>Typewriter animation effect.
<template>
<!-- Simple usage -->
<span v-typewriter="'Hello, World!'"></span>
<!-- With options -->
<span v-typewriter="{
text: 'Typing animation',
speed: 100,
cursor: '_',
onComplete: () => console.log('Done!')
}"></span>
<!-- Loop mode -->
<span v-typewriter="{
text: 'Loop animation',
loop: true,
deleteDelay: 1000
}"></span>
</template>
<script setup>
import { useTypewriter } from 'directix'
// Composable usage
const { start, stop, pause, resume } = useTypewriter({
text: 'Hello World',
speed: 50,
loop: false
})
</script>Export data (CSV/JSON/HTML/TXT).
<template>
<button v-export="exportData">Export CSV</button>
<button v-export="{ data: tableData, format: 'json', filename: 'my-data' }">
Export JSON
</button>
<button v-export="{
data: tableData,
format: 'csv',
columns: ['name', 'email'],
headers: { name: 'Name', email: 'Email Address' }
}">
Export with custom columns
</button>
</template>
<script setup>
const tableData = [
{ name: 'John', email: 'john@example.com', age: 30 },
{ name: 'Jane', email: 'jane@example.com', age: 25 }
]
import { useExport } from 'directix'
// Composable usage
const { exportCSV, exportJSON, exportHTML } = useExport()
</script>Keyword highlighting.
<template>
<p v-highlight="'important'">This is an important message.</p>
<p v-highlight="['Vue', 'React']">Vue and React are popular frameworks.</p>
<p v-highlight="{
keywords: 'highlight',
className: 'my-highlight',
style: 'background: yellow; color: black;',
caseSensitive: true
}">
This will highlight the word.
</p>
</template>
<script setup>
import { useHighlight } from 'directix'
// Composable usage
const { highlight, clear } = useHighlight({
keywords: ['important', 'key'],
className: 'highlight'
})
</script>Emoji input filter.
<template>
<!-- Strip all emojis -->
<input v-emoji type="text" />
<!-- Strip emojis with replacement -->
<input v-emoji="{ strip: true, replacement: '*' }" type="text" />
<!-- Allow specific emojis -->
<input v-emoji="{ allowList: ['😊', '👍'] }" type="text" />
<!-- Block specific emojis -->
<input v-emoji="{ blockList: ['🚫', '❌'] }" type="text" />
</template>
<script setup>
import { useEmoji } from 'directix'
// Composable usage
const { stripEmojis, containsEmoji } = useEmoji({
strip: true,
allowList: ['😊', '👍']
})
</script>Right-click context menu.
<template>
<div v-context-menu="menuItems">Right click here</div>
<div v-context-menu="{ items: menuItems, width: 200 }">Custom width</div>
</template>
<script setup>
import { useContextMenu } from 'directix'
const menuItems = [
{ label: 'Copy', handler: () => console.log('Copy') },
{ label: 'Paste', handler: () => console.log('Paste') },
{ divider: true, label: '' },
{ label: 'Delete', handler: () => console.log('Delete') }
]
// Composable usage
const { show, hide, setItems } = useContextMenu({
items: menuItems
})
</script>Fullscreen toggle.
<template>
<div v-fullscreen>
Content to show in fullscreen
<button @click="$el.toggleFullscreen()">Toggle</button>
</div>
<div v-fullscreen="{ fullscreenClass: 'my-fullscreen' }">
Custom fullscreen class
</div>
</template>
<script setup>
import { useFullscreen } from 'directix'
// Composable usage
const { isFullscreen, enter, exit, toggle } = useFullscreen({
onEnter: () => console.log('Entered fullscreen'),
onExit: () => console.log('Exited fullscreen')
})
</script>Skeleton loading placeholder.
<template>
<!-- Basic usage -->
<div v-skeleton="isLoading">Content here</div>
<!-- With options -->
<div v-skeleton="{ loading: isLoading, animation: 'pulse', width: 200, height: 20 }">
Content here
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useSkeleton } from 'directix'
const isLoading = ref(true)
// Composable usage
const { show, hide, update } = useSkeleton({
animation: 'wave',
color: '#e8e8e8'
})
</script>Progress bar animation.
<template>
<div v-progress="50">Progress at 50%</div>
<div v-progress="{
value: progressValue,
color: '#42b883',
height: 8,
showText: true
}">
Content
</div>
<div v-progress="{ indeterminate: true }">
Loading...
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useProgress } from 'directix'
const progressValue = ref(50)
// Composable usage
const { setProgress, start, finish, fail } = useProgress({
color: '#42b883',
height: 4
})
</script>Animated number counter.
<template>
<span v-counter="1000">0</span>
<span v-counter="{
value: 10000,
duration: 3000,
decimals: 2,
useGrouping: true
}">0</span>
<span v-counter="{
value: targetValue,
formatter: (v) => '$' + v.toFixed(2)
}">0</span>
</template>
<script setup>
import { ref } from 'vue'
import { useCounter } from 'directix'
const targetValue = ref(1000)
// Composable usage
const { start, pause, reset, update } = useCounter({
startValue: 0,
endValue: 1000,
duration: 2000,
formatter: (v) => `$${v.toFixed(2)}`
})
</script>Click wave effect.
<template>
<button v-click-wave>Click me</button>
<button v-click-wave="'rgba(255, 255, 255, 0.3)'">Custom color</button>
<button v-click-wave="{ color: 'red', duration: 400 }">Custom options</button>
</template>
<script setup>
import { ref } from 'vue'
import { useClickWave } from 'directix'
// Composable usage
const buttonRef = ref(null)
const { bind, trigger } = useClickWave({
color: 'currentColor',
duration: 500
})
// Bind to element on mount
onMounted(() => bind(buttonRef.value))
</script>Pan/drag gesture.
<template>
<div v-pan="handlePan">Swipe me</div>
<div v-pan="{
onPan: handlePan,
direction: 'horizontal',
threshold: 20
}">
Horizontal only
</div>
</template>
<script setup>
import { usePan } from 'directix'
function handlePan(e) {
console.log('Direction:', e.direction)
console.log('Distance:', e.distance)
}
// Composable usage
const { isPanning, deltaX, deltaY } = usePan({
onPan: handlePan,
direction: 'horizontal'
})
</script>Pinch/zoom gesture.
<template>
<div v-pinch="handlePinch">Pinch to zoom</div>
<div v-pinch="{
onPinch: handlePinch,
enableTransform: true,
minScale: 0.5,
maxScale: 3
}">
Pinch to scale
</div>
</template>
<script setup>
import { usePinch } from 'directix'
function handlePinch(e) {
console.log('Scale:', e.scale)
console.log('Center:', e.centerX, e.centerY)
}
// Composable usage
const { scale, isPinching } = usePinch({
onPinch: handlePinch,
minScale: 0.5,
maxScale: 3
})
</script>Rotation gesture.
<template>
<div v-rotate-gesture="handleRotate">Rotate with two fingers</div>
<div v-rotate-gesture="{
onRotate: handleRotate,
enableTransform: true
}">
Rotate with transform
</div>
</template>
<script setup>
import { useRotateGesture } from 'directix'
function handleRotate(e) {
console.log('Rotation:', e.rotation)
console.log('Angle:', e.angle)
}
// Composable usage
const { angle, isRotating } = useRotateGesture({
onRotate: handleRotate,
enableTransform: true
})
</script>interface DirectiveInstallOptions {
/** Register specific directives only */
directives?: string[]
/** Register all directives (default: true) */
all?: boolean
/** Global configuration for directives */
config?: Record<string, any>
}Each directive accepts different options. See the documentation for detailed API.
- Online Playground - Live editing environment with Vue 2/3 support
- Visual Configurator - Interactive parameter configuration panel
- Code Generator - Generate Vue 2/3/Composable/Nuxt code snippets
- Configuration Presets - Quick-start templates for common use cases
- Monaco Editor - CDN-loaded code editor with syntax highlighting
- Live Preview - Real-time directive effect preview
- Test Coverage - 90%+ unit test coverage, E2E testing with Playwright
- Performance Optimization - Bundle size optimization, tree-shaking improvements
- VS Code Extension - Autocompletion, hover documentation, code snippets
- CLI Tool -
directix create,directix init,directix doctor,directix migrate
- Interactive Documentation - Live editing with instant preview
- Real-world Examples - 10+ practical scenario examples
- i18n Support - English, Chinese, Japanese documentation
- Developer Experience - Improved error messages, DevTools integration
- Plugin System - Community extension support
- Vue 3 Optimization Preview - Suspense, Teleport support
- Mobile Optimization - Touch gestures, PWA support
- Accessibility (A11y) - ARIA attributes, keyboard navigation
- Security Enhancements - XSS protection, CSP compatibility
- Stability - Browser compatibility, edge case fixes
- Performance Limits - Bundle ≤ 25KB, memory optimization
- Enterprise Features - Permissions, audit logs, config center
- v2.0 Migration Prep - Migration tool, breaking changes warnings
- Vue 3 exclusive optimizations
- Web Components support
| Browser | Version |
|---|---|
| Chrome | Latest |
| Firefox | Latest |
| Safari | Latest |
| Edge | Latest |
Contributions are welcome! Please read our Contributing Guide for details.
MIT © 2024-present saqqdy