@luanlu/mk-motion 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/package.json +2 -4
  2. package/src/a11y/focus-trap.ts +64 -0
  3. package/src/a11y/keyboard.ts +43 -0
  4. package/src/components/alert/alert.css +111 -0
  5. package/src/components/alert/alert.ts +107 -0
  6. package/src/components/avatar/avatar.css +112 -0
  7. package/src/components/avatar/avatar.ts +175 -0
  8. package/src/components/breadcrumb/breadcrumb.css +31 -0
  9. package/src/components/breadcrumb/breadcrumb.ts +71 -0
  10. package/src/components/button/button.css +108 -0
  11. package/src/components/button/button.ts +140 -0
  12. package/src/components/card/card.css +52 -0
  13. package/src/components/card/card.ts +87 -0
  14. package/src/components/collapse/collapse.css +76 -0
  15. package/src/components/collapse/collapse.ts +168 -0
  16. package/src/components/dialog/dialog.css +78 -0
  17. package/src/components/dialog/dialog.ts +164 -0
  18. package/src/components/drawer/drawer.css +73 -0
  19. package/src/components/drawer/drawer.ts +131 -0
  20. package/src/components/empty/empty.css +36 -0
  21. package/src/components/empty/empty.ts +85 -0
  22. package/src/components/form/checkbox.css +56 -0
  23. package/src/components/form/checkbox.ts +119 -0
  24. package/src/components/form/radio.css +57 -0
  25. package/src/components/form/radio.ts +153 -0
  26. package/src/components/form/select.css +91 -0
  27. package/src/components/form/select.ts +174 -0
  28. package/src/components/form/slider.css +56 -0
  29. package/src/components/form/slider.ts +148 -0
  30. package/src/components/input/input.css +92 -0
  31. package/src/components/input/input.ts +162 -0
  32. package/src/components/layout/divider.css +32 -0
  33. package/src/components/layout/divider.ts +42 -0
  34. package/src/components/layout/row.css +64 -0
  35. package/src/components/layout/row.ts +57 -0
  36. package/src/components/layout/space.css +14 -0
  37. package/src/components/layout/space.ts +48 -0
  38. package/src/components/loading/loading.css +37 -0
  39. package/src/components/loading/loading.ts +46 -0
  40. package/src/components/menu/menu.css +121 -0
  41. package/src/components/menu/menu.ts +187 -0
  42. package/src/components/message/message.css +64 -0
  43. package/src/components/message/message.ts +96 -0
  44. package/src/components/popover/popover.css +73 -0
  45. package/src/components/popover/popover.ts +279 -0
  46. package/src/components/progress/progress.css +112 -0
  47. package/src/components/progress/progress.ts +171 -0
  48. package/src/components/steps/steps.css +127 -0
  49. package/src/components/steps/steps.ts +102 -0
  50. package/src/components/styles/components.css +28 -0
  51. package/src/components/styles/reset.css +24 -0
  52. package/src/components/styles/tokens.css +248 -0
  53. package/src/components/styles/variables.css +24 -0
  54. package/src/components/switch/switch.css +53 -0
  55. package/src/components/switch/switch.ts +103 -0
  56. package/src/components/table/table.css +192 -0
  57. package/src/components/table/table.ts +370 -0
  58. package/src/components/tabs/tabs.css +138 -0
  59. package/src/components/tabs/tabs.ts +211 -0
  60. package/src/components/tag/tag.css +123 -0
  61. package/src/components/tag/tag.ts +112 -0
  62. package/src/components/tooltip/tooltip.css +66 -0
  63. package/src/components/tooltip/tooltip.ts +185 -0
  64. package/src/core/animator.ts +124 -0
  65. package/src/core/timeline.ts +128 -0
  66. package/src/core/utils.ts +47 -0
  67. package/src/effects/glitch.ts +99 -0
  68. package/src/effects/particle.ts +134 -0
  69. package/src/effects/text-split.ts +95 -0
  70. package/src/effects/wave-text.ts +88 -0
  71. package/src/gesture/draggable.ts +130 -0
  72. package/src/gesture/spring.ts +152 -0
  73. package/src/index.ts +162 -0
  74. package/src/interactive/coverflow.ts +100 -0
  75. package/src/interactive/cursor-trail.ts +113 -0
  76. package/src/interactive/flip-card.ts +114 -0
  77. package/src/interactive/magnetic.ts +121 -0
  78. package/src/micro/hover-lift.ts +94 -0
  79. package/src/micro/ripple.ts +130 -0
  80. package/src/motion/component-motion.ts +177 -0
  81. package/src/presets/index.ts +69 -0
  82. package/src/scroll/scroll-trigger.ts +104 -0
  83. package/src/styles/animations.css +135 -0
  84. package/src/styles/element-plus.css +174 -0
  85. package/src/text/count-up.ts +108 -0
  86. package/src/text/typewriter.ts +109 -0
  87. package/src/theme/dark.css +19 -0
  88. package/src/theme/light.css +19 -0
  89. package/src/theme/theme.ts +65 -0
  90. package/src/transitions/blur-reveal.ts +92 -0
  91. package/src/transitions/collapse.ts +112 -0
  92. package/src/transitions/lazy-image.ts +87 -0
  93. package/src/transitions/list.ts +75 -0
  94. package/src/transitions/loading.ts +95 -0
  95. package/src/transitions/parallax.ts +60 -0
  96. package/src/transitions/shimmer.ts +105 -0
  97. package/src/transitions/toast.ts +151 -0
  98. package/src/types.d.ts +4 -0
@@ -0,0 +1,88 @@
1
+ export interface WaveTextOptions {
2
+ amplitude?: number // 波浪幅度像素
3
+ frequency?: number // 波浪频率
4
+ speed?: number // 波动速度
5
+ stagger?: number // 字符间隔
6
+ }
7
+
8
+ const DEFAULT_WAVE: Required<WaveTextOptions> = {
9
+ amplitude: 15,
10
+ frequency: 0.3,
11
+ speed: 0.08,
12
+ stagger: 0.15,
13
+ }
14
+
15
+ export class WaveText {
16
+ private element: HTMLElement
17
+ private spans: HTMLSpanElement[] = []
18
+ private rafId: number | null = null
19
+ private originalHTML: string
20
+
21
+ constructor(element: HTMLElement | string) {
22
+ this.element =
23
+ typeof element === 'string'
24
+ ? document.querySelector(element)!
25
+ : element
26
+
27
+ if (!this.element) {
28
+ throw new Error('WaveText: element not found')
29
+ }
30
+
31
+ this.originalHTML = this.element.innerHTML
32
+ }
33
+
34
+ start(options: WaveTextOptions = {}): void {
35
+ this.stop()
36
+ const opts = { ...DEFAULT_WAVE, ...options }
37
+ const text = this.element.textContent ?? ''
38
+
39
+ this.element.innerHTML = ''
40
+ this.element.style.display = 'inline-block'
41
+
42
+ const chars = text.split('')
43
+ this.spans = chars.map((char) => {
44
+ const span = document.createElement('span')
45
+ span.textContent = char === ' ' ? '\u00A0' : char
46
+ span.style.display = 'inline-block'
47
+ span.style.transition = 'transform 0.1s linear'
48
+ this.element.appendChild(span)
49
+ return span
50
+ })
51
+
52
+ let time = 0
53
+
54
+ const tick = () => {
55
+ time += opts.speed
56
+ this.spans.forEach((span, i) => {
57
+ const offset = i * opts.stagger
58
+ const y = Math.sin(time + offset) * opts.amplitude
59
+ span.style.transform = `translateY(${y}px)`
60
+ })
61
+ this.rafId = requestAnimationFrame(tick)
62
+ }
63
+
64
+ this.rafId = requestAnimationFrame(tick)
65
+ }
66
+
67
+ stop(): void {
68
+ if (this.rafId) {
69
+ cancelAnimationFrame(this.rafId)
70
+ this.rafId = null
71
+ }
72
+ }
73
+
74
+ reset(): void {
75
+ this.stop()
76
+ this.element.innerHTML = this.originalHTML
77
+ this.element.style.display = ''
78
+ }
79
+ }
80
+
81
+ export function waveText(
82
+ element: HTMLElement | string,
83
+ options?: WaveTextOptions
84
+ ): () => void {
85
+ const wt = new WaveText(element)
86
+ wt.start(options)
87
+ return () => wt.stop()
88
+ }
@@ -0,0 +1,130 @@
1
+ export interface DraggableOptions {
2
+ axis?: 'x' | 'y' | 'both'
3
+ bounds?: HTMLElement | string | null // 限制拖拽范围
4
+ onStart?: (e: PointerEvent) => void
5
+ onDrag?: (x: number, y: number) => void
6
+ onEnd?: (x: number, y: number) => void
7
+ }
8
+
9
+ export class Draggable {
10
+ private element: HTMLElement
11
+ private options: Required<Omit<DraggableOptions, 'bounds' | 'onStart' | 'onDrag' | 'onEnd'>> &
12
+ Pick<DraggableOptions, 'bounds' | 'onStart' | 'onDrag' | 'onEnd'>
13
+ private startX = 0
14
+ private startY = 0
15
+ private currentX = 0
16
+ private currentY = 0
17
+ private initialTransformX = 0
18
+ private initialTransformY = 0
19
+ private pointerId: number | null = null
20
+
21
+ constructor(element: HTMLElement | string, options: DraggableOptions = {}) {
22
+ this.element =
23
+ typeof element === 'string'
24
+ ? document.querySelector<HTMLElement>(element)!
25
+ : element
26
+
27
+ if (!this.element) {
28
+ throw new Error('Draggable: element not found')
29
+ }
30
+
31
+ this.options = {
32
+ axis: options.axis ?? 'both',
33
+ bounds: options.bounds ?? null,
34
+ onStart: options.onStart,
35
+ onDrag: options.onDrag,
36
+ onEnd: options.onEnd,
37
+ }
38
+
39
+ this.element.style.touchAction = 'none'
40
+ this.element.addEventListener('pointerdown', this.onPointerDown)
41
+ }
42
+
43
+ private onPointerDown = (e: PointerEvent) => {
44
+ if (this.pointerId !== null) return
45
+
46
+ this.pointerId = e.pointerId
47
+ this.element.setPointerCapture(e.pointerId)
48
+
49
+ this.startX = e.clientX
50
+ this.startY = e.clientY
51
+
52
+ // 读取当前 transform 偏移
53
+ const computed = getComputedStyle(this.element).transform
54
+ if (computed !== 'none') {
55
+ const matrix = new DOMMatrix(computed)
56
+ this.initialTransformX = matrix.m41
57
+ this.initialTransformY = matrix.m42
58
+ } else {
59
+ this.initialTransformX = 0
60
+ this.initialTransformY = 0
61
+ }
62
+
63
+ this.currentX = this.initialTransformX
64
+ this.currentY = this.initialTransformY
65
+
66
+ this.options.onStart?.(e)
67
+
68
+ this.element.addEventListener('pointermove', this.onPointerMove)
69
+ this.element.addEventListener('pointerup', this.onPointerUp)
70
+ }
71
+
72
+ private onPointerMove = (e: PointerEvent) => {
73
+ if (e.pointerId !== this.pointerId) return
74
+
75
+ const deltaX = e.clientX - this.startX
76
+ const deltaY = e.clientY - this.startY
77
+
78
+ let newX = this.initialTransformX + deltaX
79
+ let newY = this.initialTransformY + deltaY
80
+
81
+ // 限制范围
82
+ if (this.options.bounds) {
83
+ const boundsEl =
84
+ typeof this.options.bounds === 'string'
85
+ ? document.querySelector<HTMLElement>(this.options.bounds)!
86
+ : this.options.bounds
87
+
88
+ if (boundsEl) {
89
+ const bRect = boundsEl.getBoundingClientRect()
90
+ const eRect = this.element.getBoundingClientRect()
91
+
92
+ const minX = bRect.left - eRect.left + newX
93
+ const maxX = bRect.right - eRect.right + newX
94
+ const minY = bRect.top - eRect.top + newY
95
+ const maxY = bRect.bottom - eRect.bottom + newY
96
+
97
+ newX = Math.max(minX, Math.min(maxX, newX))
98
+ newY = Math.max(minY, Math.min(maxY, newY))
99
+ }
100
+ }
101
+
102
+ if (this.options.axis === 'x') newY = this.initialTransformY
103
+ if (this.options.axis === 'y') newX = this.initialTransformX
104
+
105
+ this.currentX = newX
106
+ this.currentY = newY
107
+
108
+ this.element.style.transform = `translate3d(${newX}px, ${newY}px, 0)`
109
+ this.options.onDrag?.(newX, newY)
110
+ }
111
+
112
+ private onPointerUp = (e: PointerEvent) => {
113
+ if (e.pointerId !== this.pointerId) return
114
+
115
+ this.pointerId = null
116
+ this.element.releasePointerCapture(e.pointerId)
117
+ this.element.removeEventListener('pointermove', this.onPointerMove)
118
+ this.element.removeEventListener('pointerup', this.onPointerUp)
119
+
120
+ this.options.onEnd?.(this.currentX, this.currentY)
121
+ }
122
+
123
+ destroy(): void {
124
+ this.element.removeEventListener('pointerdown', this.onPointerDown)
125
+ if (this.pointerId !== null) {
126
+ this.element.releasePointerCapture(this.pointerId)
127
+ this.pointerId = null
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,152 @@
1
+ export interface SpringOptions {
2
+ stiffness?: number // 刚度,默认 170
3
+ damping?: number // 阻尼,默认 26
4
+ mass?: number // 质量,默认 1
5
+ precision?: number // 停止精度,默认 0.01
6
+ }
7
+
8
+ const DEFAULT_SPRING: Required<SpringOptions> = {
9
+ stiffness: 170,
10
+ damping: 26,
11
+ mass: 1,
12
+ precision: 0.01,
13
+ }
14
+
15
+ export interface SpringTarget {
16
+ from: number
17
+ to: number
18
+ onUpdate: (value: number) => void
19
+ onComplete?: () => void
20
+ }
21
+
22
+ /**
23
+ * 物理弹簧动画,基于胡克定律
24
+ */
25
+ export function spring(
26
+ target: SpringTarget,
27
+ options: SpringOptions = {}
28
+ ): () => void {
29
+ const opts = { ...DEFAULT_SPRING, ...options }
30
+
31
+ let velocity = 0
32
+ let current = target.from
33
+ let rafId: number
34
+ let running = true
35
+
36
+ const tick = () => {
37
+ if (!running) return
38
+
39
+ const displacement = current - target.to
40
+ const springForce = -opts.stiffness * displacement
41
+ const dampingForce = -opts.damping * velocity
42
+ const acceleration = (springForce + dampingForce) / opts.mass
43
+
44
+ velocity += acceleration * (1 / 60)
45
+ current += velocity * (1 / 60)
46
+
47
+ target.onUpdate(current)
48
+
49
+ if (
50
+ Math.abs(displacement) < opts.precision &&
51
+ Math.abs(velocity) < opts.precision
52
+ ) {
53
+ target.onUpdate(target.to)
54
+ target.onComplete?.()
55
+ running = false
56
+ return
57
+ }
58
+
59
+ rafId = requestAnimationFrame(tick)
60
+ }
61
+
62
+ rafId = requestAnimationFrame(tick)
63
+
64
+ return () => {
65
+ running = false
66
+ cancelAnimationFrame(rafId)
67
+ }
68
+ }
69
+
70
+ /**
71
+ * 弹性缩放:元素像弹簧一样弹到目标大小
72
+ */
73
+ export function elasticScale(
74
+ element: HTMLElement | string,
75
+ toScale: number = 1,
76
+ options?: SpringOptions
77
+ ): () => void {
78
+ const el =
79
+ typeof element === 'string'
80
+ ? document.querySelector<HTMLElement>(element)!
81
+ : element
82
+
83
+ if (!el) throw new Error('elasticScale: element not found')
84
+
85
+ // 获取当前 scale
86
+ const computed = getComputedStyle(el).transform
87
+ let fromScale = 1
88
+ if (computed !== 'none') {
89
+ const matrix = new DOMMatrix(computed)
90
+ fromScale = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b)
91
+ }
92
+
93
+ return spring(
94
+ {
95
+ from: fromScale,
96
+ to: toScale,
97
+ onUpdate: (value) => {
98
+ el.style.transform = `scale(${value})`
99
+ },
100
+ },
101
+ options
102
+ )
103
+ }
104
+
105
+ /**
106
+ * 弹性移动
107
+ */
108
+ export function elasticMove(
109
+ element: HTMLElement | string,
110
+ toX: number,
111
+ toY: number,
112
+ options?: SpringOptions
113
+ ): () => void {
114
+ const el =
115
+ typeof element === 'string'
116
+ ? document.querySelector<HTMLElement>(element)!
117
+ : element
118
+
119
+ if (!el) throw new Error('elasticMove: element not found')
120
+
121
+ let currentX = 0
122
+ let currentY = 0
123
+
124
+ const stopX = spring(
125
+ {
126
+ from: 0,
127
+ to: toX,
128
+ onUpdate: (x) => {
129
+ currentX = x
130
+ el.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`
131
+ },
132
+ },
133
+ options
134
+ )
135
+
136
+ const stopY = spring(
137
+ {
138
+ from: 0,
139
+ to: toY,
140
+ onUpdate: (y) => {
141
+ currentY = y
142
+ el.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`
143
+ },
144
+ },
145
+ options
146
+ )
147
+
148
+ return () => {
149
+ stopX()
150
+ stopY()
151
+ }
152
+ }
package/src/index.ts ADDED
@@ -0,0 +1,162 @@
1
+ import './styles/animations.css'
2
+ import './styles/element-plus.css'
3
+ import './components/styles/variables.css'
4
+ import './components/styles/tokens.css'
5
+ import './components/styles/reset.css'
6
+
7
+ /* Component styles */
8
+ import './components/styles/components.css'
9
+
10
+ /* ===== Core ===== */
11
+ export { Animator } from './core/animator.ts'
12
+ export { Timeline } from './core/timeline.ts'
13
+ export type { AnimationName, AnimationOptions } from './core/utils.ts'
14
+
15
+ /* ===== Presets ===== */
16
+ export {
17
+ fadeIn, fadeOut,
18
+ slideInUp, slideInDown, slideInLeft, slideInRight,
19
+ slideOutUp, slideOutDown,
20
+ zoomIn, zoomOut,
21
+ bounceIn, bounceOut,
22
+ flipInX, flipInY,
23
+ shake, pulse, rotateIn,
24
+ } from './presets/index.ts'
25
+ export * as presets from './presets/index.ts'
26
+
27
+ /* ===== Scroll ===== */
28
+ export { ScrollTrigger, scrollAnimate } from './scroll/scroll-trigger.ts'
29
+ export type { ScrollTriggerOptions } from './scroll/scroll-trigger.ts'
30
+
31
+ /* ===== Text ===== */
32
+ export { Typewriter, typewrite } from './text/typewriter.ts'
33
+ export { CountUp, countUp } from './text/count-up.ts'
34
+ export type { TypewriterOptions } from './text/typewriter.ts'
35
+ export type { CountUpOptions } from './text/count-up.ts'
36
+
37
+ /* ===== Gesture ===== */
38
+ export { spring, elasticScale, elasticMove } from './gesture/spring.ts'
39
+ export { Draggable } from './gesture/draggable.ts'
40
+ export type { SpringOptions, SpringTarget } from './gesture/spring.ts'
41
+ export type { DraggableOptions } from './gesture/draggable.ts'
42
+
43
+ /* ===== Micro ===== */
44
+ export { addRipple, rippleEffect } from './micro/ripple.ts'
45
+ export { hoverLift, hoverGlow } from './micro/hover-lift.ts'
46
+ export type { RippleOptions } from './micro/ripple.ts'
47
+ export type { HoverLiftOptions } from './micro/hover-lift.ts'
48
+
49
+ /* ===== Effects (视觉炸裂) ===== */
50
+ export { particleBurst, particleAt } from './effects/particle.ts'
51
+ export { TextSplit, splitText } from './effects/text-split.ts'
52
+ export { glitch, glitchLoop } from './effects/glitch.ts'
53
+ export { WaveText, waveText } from './effects/wave-text.ts'
54
+ export type { ParticleOptions } from './effects/particle.ts'
55
+ export type { TextSplitOptions } from './effects/text-split.ts'
56
+ export type { GlitchOptions } from './effects/glitch.ts'
57
+ export type { WaveTextOptions } from './effects/wave-text.ts'
58
+
59
+ /* ===== Transitions (实用高级) ===== */
60
+ export { parallax, parallaxGroup } from './transitions/parallax.ts'
61
+ export { shimmer, skeleton } from './transitions/shimmer.ts'
62
+ export { blurReveal, blurRevealChildren } from './transitions/blur-reveal.ts'
63
+ export { lazyImage, lazyImages } from './transitions/lazy-image.ts'
64
+ export type { ParallaxOptions } from './transitions/parallax.ts'
65
+ export type { ShimmerOptions } from './transitions/shimmer.ts'
66
+ export type { BlurRevealOptions } from './transitions/blur-reveal.ts'
67
+ export type { LazyImageOptions } from './transitions/lazy-image.ts'
68
+
69
+ /* ===== Element Plus 风格过渡 ===== */
70
+ export { expand, collapse, toggleCollapse } from './transitions/collapse.ts'
71
+ export { listStagger } from './transitions/list.ts'
72
+ export { showLoading, fullscreenLoading } from './transitions/loading.ts'
73
+ export { toast, toastSuccess, toastError, toastWarning, notify } from './transitions/toast.ts'
74
+ export type { CollapseOptions } from './transitions/collapse.ts'
75
+ export type { ListStaggerOptions } from './transitions/list.ts'
76
+ export type { LoadingOptions } from './transitions/loading.ts'
77
+ export type { ToastOptions } from './transitions/toast.ts'
78
+
79
+ /* ===== Interactive (游戏/交互) ===== */
80
+ export { magnetic, magneticText } from './interactive/magnetic.ts'
81
+ export { CoverFlow, createCoverFlow } from './interactive/coverflow.ts'
82
+ export { FlipCard, createFlipCard } from './interactive/flip-card.ts'
83
+ export { cursorTrail } from './interactive/cursor-trail.ts'
84
+ export type { MagneticOptions } from './interactive/magnetic.ts'
85
+ export type { CoverFlowOptions } from './interactive/coverflow.ts'
86
+ export type { FlipCardOptions } from './interactive/flip-card.ts'
87
+ export type { CursorTrailOptions } from './interactive/cursor-trail.ts'
88
+
89
+ /* ===== Motion System ===== */
90
+ export { withMotion, staggerEnter } from './motion/component-motion.ts'
91
+ export type { MotionOptions, MicroAnimation } from './motion/component-motion.ts'
92
+
93
+ /* ===== A11y ===== */
94
+ export { FocusTrap } from './a11y/focus-trap.ts'
95
+ export { onKey, Keys } from './a11y/keyboard.ts'
96
+
97
+ /* ===== Theme ===== */
98
+ export { theme, ThemeManager } from './theme/theme.ts'
99
+
100
+ /* ===== Layout ===== */
101
+ export { MkRow, createRow } from './components/layout/row.ts'
102
+ export { MkSpace, createSpace } from './components/layout/space.ts'
103
+ export { MkDivider, createDivider } from './components/layout/divider.ts'
104
+
105
+ /* ===== UI Components ===== */
106
+ export { MkButton, createButton } from './components/button/button.ts'
107
+ export { MkInput, createInput } from './components/input/input.ts'
108
+ export { MkCard, createCard } from './components/card/card.ts'
109
+ export { MkDialog, createDialog } from './components/dialog/dialog.ts'
110
+ export { MkDrawer, createDrawer } from './components/drawer/drawer.ts'
111
+ export { message, messageSuccess, messageError, messageWarning } from './components/message/message.ts'
112
+ export { MkSwitch, createSwitch } from './components/switch/switch.ts'
113
+ export { showLoading as showComponentLoading, showFullscreenLoading } from './components/loading/loading.ts'
114
+
115
+ /* ===== Form Components ===== */
116
+ export { MkSelect, createSelect } from './components/form/select.ts'
117
+ export { MkCheckbox, createCheckbox, MkCheckboxGroup } from './components/form/checkbox.ts'
118
+ export { MkRadio, MkRadioGroup } from './components/form/radio.ts'
119
+ export { MkSlider } from './components/form/slider.ts'
120
+
121
+ /* ===== Table ===== */
122
+ export { MkTable } from './components/table/table.ts'
123
+
124
+ /* ===== New Components ===== */
125
+ export { MkTag, createTag } from './components/tag/tag.ts'
126
+ export { createTooltip } from './components/tooltip/tooltip.ts'
127
+ export { MkTabs, createTabs } from './components/tabs/tabs.ts'
128
+ export { MkAvatar, createAvatar } from './components/avatar/avatar.ts'
129
+ export { MkAlert, createAlert } from './components/alert/alert.ts'
130
+ export { MkProgress, createProgress } from './components/progress/progress.ts'
131
+ export { MkCollapse, createCollapse } from './components/collapse/collapse.ts'
132
+ export { MkEmpty, createEmpty } from './components/empty/empty.ts'
133
+ export { MkPopover, createPopover } from './components/popover/popover.ts'
134
+ export { MkMenu, createMenu } from './components/menu/menu.ts'
135
+ export { MkBreadcrumb, createBreadcrumb } from './components/breadcrumb/breadcrumb.ts'
136
+ export { MkSteps, createSteps } from './components/steps/steps.ts'
137
+
138
+ /* ===== Types ===== */
139
+ export type { ButtonOptions } from './components/button/button.ts'
140
+ export type { InputOptions } from './components/input/input.ts'
141
+ export type { CardOptions } from './components/card/card.ts'
142
+ export type { DialogOptions } from './components/dialog/dialog.ts'
143
+ export type { DrawerOptions } from './components/drawer/drawer.ts'
144
+ export type { MessageOptions } from './components/message/message.ts'
145
+ export type { SwitchOptions } from './components/switch/switch.ts'
146
+ export type { SelectOptions, SelectOption } from './components/form/select.ts'
147
+ export type { CheckboxOptions } from './components/form/checkbox.ts'
148
+ export type { RadioOptions } from './components/form/radio.ts'
149
+ export type { SliderOptions } from './components/form/slider.ts'
150
+ export type { TableColumn, TableOptions } from './components/table/table.ts'
151
+ export type { TagOptions } from './components/tag/tag.ts'
152
+ export type { TooltipOptions } from './components/tooltip/tooltip.ts'
153
+ export type { TabsOptions, TabItem } from './components/tabs/tabs.ts'
154
+ export type { AvatarOptions } from './components/avatar/avatar.ts'
155
+ export type { AlertOptions } from './components/alert/alert.ts'
156
+ export type { ProgressOptions } from './components/progress/progress.ts'
157
+ export type { CollapsePanelOptions, CollapseItem } from './components/collapse/collapse.ts'
158
+ export type { EmptyOptions } from './components/empty/empty.ts'
159
+ export type { PopoverOptions } from './components/popover/popover.ts'
160
+ export type { MenuOptions, MenuItem } from './components/menu/menu.ts'
161
+ export type { BreadcrumbOptions, BreadcrumbItem } from './components/breadcrumb/breadcrumb.ts'
162
+ export type { StepsOptions, StepItem } from './components/steps/steps.ts'
@@ -0,0 +1,100 @@
1
+ export interface CoverFlowOptions {
2
+ perspective?: number // 透视距离
3
+ rotateY?: number // 两侧卡片旋转角度
4
+ spacing?: number // 卡片间距
5
+ scale?: number // 中心卡片缩放
6
+ }
7
+
8
+ const DEFAULT_COVERFLOW: Required<CoverFlowOptions> = {
9
+ perspective: 800,
10
+ rotateY: 45,
11
+ spacing: 60,
12
+ scale: 1.15,
13
+ }
14
+
15
+ /**
16
+ * 3D 封面流轮播
17
+ */
18
+ export class CoverFlow {
19
+ private container: HTMLElement
20
+ private cards: HTMLElement[]
21
+ private currentIndex = 0
22
+ private options: Required<CoverFlowOptions>
23
+
24
+ constructor(
25
+ container: HTMLElement | string,
26
+ cardSelector: string,
27
+ options: CoverFlowOptions = {}
28
+ ) {
29
+ this.container =
30
+ typeof container === 'string'
31
+ ? document.querySelector<HTMLElement>(container)!
32
+ : container
33
+
34
+ if (!this.container) {
35
+ throw new Error('CoverFlow: container not found')
36
+ }
37
+
38
+ this.cards = Array.from(this.container.querySelectorAll(cardSelector))
39
+ this.options = { ...DEFAULT_COVERFLOW, ...options }
40
+
41
+ this.container.style.perspective = `${this.options.perspective}px`
42
+ this.container.style.transformStyle = 'preserve-3d'
43
+
44
+ this.update()
45
+ }
46
+
47
+ goTo(index: number): this {
48
+ this.currentIndex = Math.max(0, Math.min(index, this.cards.length - 1))
49
+ this.update()
50
+ return this
51
+ }
52
+
53
+ next(): this {
54
+ return this.goTo(this.currentIndex + 1)
55
+ }
56
+
57
+ prev(): this {
58
+ return this.goTo(this.currentIndex - 1)
59
+ }
60
+
61
+ private update(): void {
62
+ this.cards.forEach((card, i) => {
63
+ const offset = i - this.currentIndex
64
+ const absOffset = Math.abs(offset)
65
+
66
+ let translateX = offset * this.options.spacing
67
+ let rotateY = offset > 0 ? -this.options.rotateY : this.options.rotateY
68
+ let scale = 1
69
+ let zIndex = this.cards.length - absOffset
70
+ let opacity = 1
71
+
72
+ if (offset === 0) {
73
+ rotateY = 0
74
+ scale = this.options.scale
75
+ zIndex = this.cards.length + 1
76
+ } else if (absOffset > 2) {
77
+ opacity = 0
78
+ zIndex = 0
79
+ } else {
80
+ opacity = 1 - absOffset * 0.3
81
+ }
82
+
83
+ card.style.transition = 'all 0.5s cubic-bezier(0.25, 0.1, 0.25, 1)'
84
+ card.style.transform = `translateX(${translateX}px) rotateY(${rotateY}deg) scale(${scale})`
85
+ card.style.zIndex = String(zIndex)
86
+ card.style.opacity = String(opacity)
87
+ })
88
+ }
89
+ }
90
+
91
+ /**
92
+ * 快捷方法创建 CoverFlow
93
+ */
94
+ export function createCoverFlow(
95
+ container: HTMLElement | string,
96
+ cardSelector: string,
97
+ options?: CoverFlowOptions
98
+ ): CoverFlow {
99
+ return new CoverFlow(container, cardSelector, options)
100
+ }