11<script setup lang="ts">
2+ import { computed , nextTick , onMounted , ref , watch } from ' vue'
23import Button from ' @/components/Button.vue'
4+
5+ interface PerformanceMetrics {
6+ mountTime: number
7+ memoryUsage: number | null
8+ elementCount: number
9+ renderTime: number
10+ averageTooltipShowTime: number
11+ fps: number
12+ }
13+
14+ const tooltipCount = ref (1000 )
15+ const isRendering = ref (false )
16+ const metrics = ref <PerformanceMetrics >({
17+ mountTime: 0 ,
18+ memoryUsage: null ,
19+ elementCount: 0 ,
20+ renderTime: 0 ,
21+ averageTooltipShowTime: 0 ,
22+ fps: 0 ,
23+ })
24+
25+ const buttons = computed (() =>
26+ Array .from ({ length: tooltipCount .value }, (_ , i ) => ` Button ${i + 1 } ` ),
27+ )
28+
29+ // Measure FPS during interaction
30+ let frameCount = 0
31+ let lastFrameTime = performance .now ()
32+ let fpsInterval: number | null = null
33+
34+ function measureFPS() {
35+ frameCount ++
36+ const currentTime = performance .now ()
37+ const elapsed = currentTime - lastFrameTime
38+
39+ if (elapsed >= 1000 ) {
40+ metrics .value .fps = Math .round ((frameCount * 1000 ) / elapsed )
41+ frameCount = 0
42+ lastFrameTime = currentTime
43+ }
44+
45+ if (fpsInterval !== null ) {
46+ requestAnimationFrame (measureFPS )
47+ }
48+ }
49+
50+ function startFPSMonitoring() {
51+ if (fpsInterval === null ) {
52+ frameCount = 0
53+ lastFrameTime = performance .now ()
54+ fpsInterval = requestAnimationFrame (measureFPS ) as unknown as number
55+ }
56+ }
57+
58+ function stopFPSMonitoring() {
59+ if (fpsInterval !== null ) {
60+ cancelAnimationFrame (fpsInterval )
61+ fpsInterval = null
62+ }
63+ }
64+
65+ // Measure tooltip show time
66+ async function measureTooltipShowTime(): Promise <number > {
67+ const testElement = document .querySelector (' [data-benchmark-test]' ) as HTMLElement
68+ if (! testElement )
69+ return 0
70+
71+ const times: number [] = []
72+
73+ for (let i = 0 ; i < 10 ; i ++ ) {
74+ const startTime = performance .now ()
75+
76+ // Trigger tooltip
77+ const mouseEnterEvent = new MouseEvent (' mouseenter' , {
78+ bubbles: true ,
79+ cancelable: true ,
80+ })
81+ testElement .dispatchEvent (mouseEnterEvent )
82+
83+ await nextTick ()
84+ await new Promise (resolve => setTimeout (resolve , 100 ))
85+
86+ const endTime = performance .now ()
87+ times .push (endTime - startTime )
88+
89+ // Hide tooltip
90+ const mouseLeaveEvent = new MouseEvent (' mouseleave' , {
91+ bubbles: true ,
92+ cancelable: true ,
93+ })
94+ testElement .dispatchEvent (mouseLeaveEvent )
95+
96+ await new Promise (resolve => setTimeout (resolve , 50 ))
97+ }
98+
99+ return times .reduce ((a , b ) => a + b , 0 ) / times .length
100+ }
101+
102+ // Measure rendering performance
103+ async function measurePerformance() {
104+ isRendering .value = true
105+
106+ const startTime = performance .now ()
107+
108+ // Force re-render
109+ const currentCount = tooltipCount .value
110+ tooltipCount .value = 0
111+ await nextTick ()
112+
113+ tooltipCount .value = currentCount
114+ await nextTick ()
115+
116+ const mountEndTime = performance .now ()
117+ metrics .value .mountTime = mountEndTime - startTime
118+
119+ // Wait for DOM to settle
120+ await new Promise (resolve => setTimeout (resolve , 100 ))
121+
122+ const renderEndTime = performance .now ()
123+ metrics .value .renderTime = renderEndTime - startTime
124+
125+ // Count elements
126+ const container = document .querySelector (' [data-benchmark-container]' )
127+ metrics .value .elementCount = container ? container .querySelectorAll (' button' ).length : 0
128+
129+ // Measure memory (if available)
130+ if ((performance as any ).memory ) {
131+ metrics .value .memoryUsage = Math .round (
132+ (performance as any ).memory .usedJSHeapSize / 1048576 ,
133+ )
134+ }
135+
136+ // Measure tooltip show time
137+ metrics .value .averageTooltipShowTime = await measureTooltipShowTime ()
138+
139+ isRendering .value = false
140+ }
141+
142+ // Run benchmark on mount and when count changes
143+ onMounted (async () => {
144+ await nextTick ()
145+ await measurePerformance ()
146+ startFPSMonitoring ()
147+ })
148+
149+ watch (tooltipCount , async () => {
150+ if (! isRendering .value ) {
151+ await measurePerformance ()
152+ }
153+ })
154+
155+ // Cleanup
156+ onMounted (() => {
157+ return () => {
158+ stopFPSMonitoring ()
159+ }
160+ })
161+
162+ function handleMouseEnter() {
163+ startFPSMonitoring ()
164+ }
165+
166+ function handleMouseLeave() {
167+ stopFPSMonitoring ()
168+ metrics .value .fps = 0
169+ }
3170 </script >
4171
5172<template >
@@ -8,17 +175,135 @@ import Button from '@/components/Button.vue'
8175 Tooltip Directive Benchmark
9176 </h2 >
10177
11- <!-- Performance test with 100 small buttons with tooltips -->
178+ <!-- Performance Metrics Dashboard -->
179+ <div class =" bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 border border-gray-200 dark:border-gray-700" >
180+ <h3 class =" text-xl font-semibold mb-4 text-gray-800 dark:text-gray-200" >
181+ Performance Metrics
182+ </h3 >
183+
184+ <div class =" grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6" >
185+ <!-- Tooltip Count -->
186+ <div class =" bg-gray-50 dark:bg-gray-900 rounded-lg p-4" >
187+ <div class =" text-sm text-gray-600 dark:text-gray-400 mb-1" >
188+ Tooltip Count
189+ </div >
190+ <div class =" text-2xl font-bold text-blue-600 dark:text-blue-400" >
191+ {{ metrics.elementCount }}
192+ </div >
193+ </div >
194+
195+ <!-- Mount Time -->
196+ <div class =" bg-gray-50 dark:bg-gray-900 rounded-lg p-4" >
197+ <div class =" text-sm text-gray-600 dark:text-gray-400 mb-1" >
198+ Mount Time
199+ </div >
200+ <div class =" text-2xl font-bold text-green-600 dark:text-green-400" >
201+ {{ metrics.mountTime.toFixed(2) }}ms
202+ </div >
203+ </div >
204+
205+ <!-- Render Time -->
206+ <div class =" bg-gray-50 dark:bg-gray-900 rounded-lg p-4" >
207+ <div class =" text-sm text-gray-600 dark:text-gray-400 mb-1" >
208+ Total Render Time
209+ </div >
210+ <div class =" text-2xl font-bold text-purple-600 dark:text-purple-400" >
211+ {{ metrics.renderTime.toFixed(2) }}ms
212+ </div >
213+ </div >
214+
215+ <!-- Average Tooltip Show Time -->
216+ <div class =" bg-gray-50 dark:bg-gray-900 rounded-lg p-4" >
217+ <div class =" text-sm text-gray-600 dark:text-gray-400 mb-1" >
218+ Avg. Show Time
219+ </div >
220+ <div class =" text-2xl font-bold text-orange-600 dark:text-orange-400" >
221+ {{ metrics.averageTooltipShowTime.toFixed(2) }}ms
222+ </div >
223+ </div >
224+
225+ <!-- FPS -->
226+ <div class =" bg-gray-50 dark:bg-gray-900 rounded-lg p-4" >
227+ <div class =" text-sm text-gray-600 dark:text-gray-400 mb-1" >
228+ FPS (hover to measure)
229+ </div >
230+ <div
231+ class =" text-2xl font-bold"
232+ :class =" {
233+ 'text-green-600 dark:text-green-400': metrics.fps >= 55,
234+ 'text-yellow-600 dark:text-yellow-400': metrics.fps >= 30 && metrics.fps < 55,
235+ 'text-red-600 dark:text-red-400': metrics.fps > 0 && metrics.fps < 30,
236+ 'text-gray-600 dark:text-gray-400': metrics.fps === 0,
237+ }"
238+ >
239+ {{ metrics.fps > 0 ? `${metrics.fps} FPS` : 'N/A' }}
240+ </div >
241+ </div >
242+
243+ <!-- Memory Usage -->
244+ <div v-if =" metrics.memoryUsage !== null" class =" bg-gray-50 dark:bg-gray-900 rounded-lg p-4" >
245+ <div class =" text-sm text-gray-600 dark:text-gray-400 mb-1" >
246+ Memory Usage
247+ </div >
248+ <div class =" text-2xl font-bold text-pink-600 dark:text-pink-400" >
249+ {{ metrics.memoryUsage }}MB
250+ </div >
251+ </div >
252+ </div >
253+
254+ <!-- Controls -->
255+ <div class =" flex items-center gap-4 flex-wrap" >
256+ <label class =" flex items-center gap-2" >
257+ <span class =" text-sm text-gray-700 dark:text-gray-300" >Tooltip Count:</span >
258+ <input
259+ v-model.number =" tooltipCount"
260+ type =" number"
261+ min =" 1"
262+ max =" 5000"
263+ step =" 100"
264+ class =" px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
265+ >
266+ </label >
267+
268+ <Button
269+ class =" !px-4 !py-2"
270+ label =" Re-run Benchmark"
271+ :disabled =" isRendering"
272+ @click =" measurePerformance"
273+ />
274+
275+ <span v-if =" isRendering" class =" text-sm text-gray-600 dark:text-gray-400 italic" >
276+ Running benchmark...
277+ </span >
278+ </div >
279+
280+ <!-- Info -->
281+ <div class =" mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded border border-blue-200 dark:border-blue-800" >
282+ <p class =" text-sm text-blue-800 dark:text-blue-300" >
283+ 💡 <strong >Tip:</strong > Hover over the buttons below to measure FPS in real-time.
284+ Lower element counts show better performance metrics.
285+ </p >
286+ </div >
287+ </div >
288+
289+ <!-- Performance test with tooltips -->
12290 <div class =" flex flex-col gap-4" >
13291 <h3 class =" text-lg font-semibold text-gray-800 dark:text-gray-200" >
14- Performance test
292+ Interactive Test Area
15293 </h3 >
16- <div class =" flex flex-wrap gap-4 items-center" >
294+ <div
295+ data-benchmark-container
296+ class =" flex flex-wrap gap-2 items-center p-4 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 max-h-96 overflow-y-auto"
297+ @mouseenter =" handleMouseEnter"
298+ @mouseleave =" handleMouseLeave"
299+ >
17300 <Button
18- v-for =" (label, idx) in Array.from({ length: 1000 }, (_, i) => `Button ${i + 1}`)" :key =" idx"
301+ v-for =" (label, idx) in buttons"
302+ :key =" idx"
19303 v-tooltip =" `Tooltip for ${label}`"
20304 :label =" label"
21305 class =" text-xs px-2 py-1"
306+ :data-benchmark-test =" idx === 0 ? '' : undefined"
22307 />
23308 </div >
24309 </div >
0 commit comments