Added About Page
This commit is contained in:
242
resources/js/directives/hoverEffects.ts
Normal file
242
resources/js/directives/hoverEffects.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
// directives/hoverEffects.ts
|
||||
import { Directive } from 'vue'
|
||||
|
||||
export interface HoverOptions {
|
||||
type?: 'lift' | 'scale' | 'tilt' | 'glow' | 'rotate' | 'slide-up' | 'slide-down' | 'bounce' | 'shake'
|
||||
duration?: number | string
|
||||
easing?: string
|
||||
intensity?: number // Scale factor for the effect (0.1 = subtle, 1.0 = normal, 2.0 = dramatic)
|
||||
transform?: string // Custom transform for hover state
|
||||
boxShadow?: string // Custom box shadow for hover state
|
||||
backgroundColor?: string // Custom background color for hover state
|
||||
borderColor?: string // Custom border color for hover state
|
||||
color?: string // Custom text color for hover state
|
||||
scale?: number // Custom scale value
|
||||
translateY?: number // Custom Y translation in pixels
|
||||
translateX?: number // Custom X translation in pixels
|
||||
rotateZ?: number // Custom rotation in degrees
|
||||
disabled?: boolean // Disable hover effects
|
||||
}
|
||||
|
||||
export type HoverValue = string | HoverOptions
|
||||
|
||||
interface HoverElement extends HTMLElement {
|
||||
_hoverOptions?: HoverOptions
|
||||
_originalStyles?: {
|
||||
transition?: string
|
||||
transform?: string
|
||||
boxShadow?: string
|
||||
backgroundColor?: string
|
||||
borderColor?: string
|
||||
color?: string
|
||||
}
|
||||
_mouseEnterHandler?: (e: MouseEvent) => void
|
||||
_mouseLeaveHandler?: (e: MouseEvent) => void
|
||||
}
|
||||
|
||||
function setupHoverEffects(el: HoverElement, binding: any) {
|
||||
// Clean up existing handlers
|
||||
if (el._mouseEnterHandler) {
|
||||
el.removeEventListener('mouseenter', el._mouseEnterHandler)
|
||||
el.removeEventListener('mouseleave', el._mouseLeaveHandler!)
|
||||
}
|
||||
|
||||
let options: HoverOptions = {}
|
||||
|
||||
if (typeof binding.value === 'string') {
|
||||
options = { type: binding.value }
|
||||
} else if (typeof binding.value === 'object') {
|
||||
options = { ...binding.value }
|
||||
}
|
||||
|
||||
const {
|
||||
type = 'lift',
|
||||
duration = '0.3s',
|
||||
easing = 'ease',
|
||||
intensity = 1.0,
|
||||
transform,
|
||||
boxShadow,
|
||||
backgroundColor,
|
||||
borderColor,
|
||||
color,
|
||||
scale,
|
||||
translateY,
|
||||
translateX,
|
||||
rotateZ,
|
||||
disabled = false
|
||||
} = options
|
||||
|
||||
el._hoverOptions = options
|
||||
|
||||
if (disabled) return
|
||||
|
||||
// Store original styles
|
||||
const computedStyle = window.getComputedStyle(el)
|
||||
el._originalStyles = {
|
||||
transition: computedStyle.transition,
|
||||
transform: computedStyle.transform,
|
||||
boxShadow: computedStyle.boxShadow,
|
||||
backgroundColor: computedStyle.backgroundColor,
|
||||
borderColor: computedStyle.borderColor,
|
||||
color: computedStyle.color
|
||||
}
|
||||
|
||||
// Set base transition
|
||||
el.style.transition = `all ${duration} ${easing}`
|
||||
|
||||
// Get hover effects based on type
|
||||
const hoverEffects = getHoverEffects(type, intensity, {
|
||||
transform,
|
||||
boxShadow,
|
||||
backgroundColor,
|
||||
borderColor,
|
||||
color,
|
||||
scale,
|
||||
translateY,
|
||||
translateX,
|
||||
rotateZ
|
||||
})
|
||||
|
||||
// Mouse enter handler
|
||||
el._mouseEnterHandler = (e: MouseEvent) => {
|
||||
Object.assign(el.style, hoverEffects.enter)
|
||||
}
|
||||
|
||||
// Mouse leave handler
|
||||
el._mouseLeaveHandler = (e: MouseEvent) => {
|
||||
Object.assign(el.style, hoverEffects.leave)
|
||||
}
|
||||
|
||||
el.addEventListener('mouseenter', el._mouseEnterHandler)
|
||||
el.addEventListener('mouseleave', el._mouseLeaveHandler)
|
||||
}
|
||||
|
||||
function getHoverEffects(
|
||||
type: string,
|
||||
intensity: number,
|
||||
customValues: Partial<HoverOptions>
|
||||
) {
|
||||
const effects = {
|
||||
enter: {} as any,
|
||||
leave: {} as any
|
||||
}
|
||||
|
||||
// Custom values take precedence
|
||||
if (customValues.transform) {
|
||||
effects.enter.transform = customValues.transform
|
||||
effects.leave.transform = 'none'
|
||||
return effects
|
||||
}
|
||||
|
||||
// Preset effects
|
||||
switch (type) {
|
||||
case 'lift':
|
||||
effects.enter.transform = `translateY(${customValues.translateY || -8 * intensity}px) scale(${customValues.scale || 1 + 0.02 * intensity})`
|
||||
effects.enter.boxShadow = customValues.boxShadow || `0 ${10 * intensity}px ${25 * intensity}px rgba(0,0,0,0.15)`
|
||||
break
|
||||
|
||||
case 'scale':
|
||||
effects.enter.transform = `scale(${customValues.scale || 1 + 0.05 * intensity})`
|
||||
break
|
||||
|
||||
case 'tilt':
|
||||
effects.enter.transform = `perspective(1000px) rotateY(${customValues.rotateZ || 5 * intensity}deg) scale(${1 + 0.02 * intensity})`
|
||||
break
|
||||
|
||||
case 'glow':
|
||||
effects.enter.boxShadow = customValues.boxShadow || `0 0 ${20 * intensity}px rgba(59, 130, 246, 0.5)`
|
||||
effects.enter.transform = `scale(${1 + 0.02 * intensity})`
|
||||
break
|
||||
|
||||
case 'rotate':
|
||||
effects.enter.transform = `rotate(${customValues.rotateZ || 5 * intensity}deg) scale(${1 + 0.02 * intensity})`
|
||||
break
|
||||
|
||||
case 'slide-up':
|
||||
effects.enter.transform = `translateY(${customValues.translateY || -5 * intensity}px)`
|
||||
break
|
||||
|
||||
case 'slide-down':
|
||||
effects.enter.transform = `translateY(${customValues.translateY || 5 * intensity}px)`
|
||||
break
|
||||
|
||||
case 'bounce':
|
||||
effects.enter.transform = `translateY(${-3 * intensity}px) scale(${1 + 0.05 * intensity})`
|
||||
effects.enter.transition = `all 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55)`
|
||||
break
|
||||
|
||||
case 'shake':
|
||||
effects.enter.animation = `shake-${intensity} 0.5s ease-in-out`
|
||||
break
|
||||
}
|
||||
|
||||
// Add custom color effects
|
||||
if (customValues.backgroundColor) {
|
||||
effects.enter.backgroundColor = customValues.backgroundColor
|
||||
}
|
||||
if (customValues.borderColor) {
|
||||
effects.enter.borderColor = customValues.borderColor
|
||||
}
|
||||
if (customValues.color) {
|
||||
effects.enter.color = customValues.color
|
||||
}
|
||||
|
||||
// Leave effects (return to original)
|
||||
effects.leave.transform = 'none'
|
||||
effects.leave.boxShadow = ''
|
||||
effects.leave.backgroundColor = ''
|
||||
effects.leave.borderColor = ''
|
||||
effects.leave.color = ''
|
||||
effects.leave.animation = ''
|
||||
|
||||
return effects
|
||||
}
|
||||
|
||||
export const hoverEffects: Directive = {
|
||||
mounted(el: HoverElement, binding) {
|
||||
setupHoverEffects(el, binding)
|
||||
},
|
||||
|
||||
updated(el: HoverElement, binding) {
|
||||
if (JSON.stringify(binding.value) !== JSON.stringify(binding.oldValue)) {
|
||||
setupHoverEffects(el, binding)
|
||||
}
|
||||
},
|
||||
|
||||
unmounted(el: HoverElement) {
|
||||
if (el._mouseEnterHandler) {
|
||||
el.removeEventListener('mouseenter', el._mouseEnterHandler)
|
||||
el.removeEventListener('mouseleave', el._mouseLeaveHandler!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add shake keyframes to document if not already added
|
||||
if (typeof window !== 'undefined') {
|
||||
const addShakeKeyframes = () => {
|
||||
if (document.querySelector('#hover-directive-styles')) return
|
||||
|
||||
const style = document.createElement('style')
|
||||
style.id = 'hover-directive-styles'
|
||||
style.innerHTML = `
|
||||
@keyframes shake-1 {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-2px); }
|
||||
75% { transform: translateX(2px); }
|
||||
}
|
||||
@keyframes shake-2 {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-5px); }
|
||||
75% { transform: translateX(5px); }
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
|
||||
// Add styles when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', addShakeKeyframes)
|
||||
} else {
|
||||
addShakeKeyframes()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user