@@ -73,6 +73,14 @@ const props = defineProps({
7373 type: Boolean ,
7474 default: true
7575 },
76+ smoothForce: {
77+ type: Number ,
78+ default: 0.18
79+ },
80+ smoothSnapThreshold: {
81+ type: Number ,
82+ default: 0.25
83+ }
7684});
7785
7886const tooltip = ref (null );
@@ -81,52 +89,86 @@ const { x, y } = useMouse(props.parent);
8189const targetPosition = ref ({ x: 0 , y: 0 });
8290const displayPosition = ref ({ x: 0 , y: 0 });
8391
84- const smoothing = 0.18 ;
8592let animationFrameId = null ;
8693
87- function animate () {
94+ function stepAnimation () {
95+ if (! props .show ) {
96+ cancelAnimation ();
97+ return ;
98+ }
99+
88100 if (! props .smooth ) {
89101 displayPosition .value .x = targetPosition .value .x ;
90102 displayPosition .value .y = targetPosition .value .y ;
103+ cancelAnimation ();
91104 return ;
92105 }
93- displayPosition .value .x += (targetPosition .value .x - displayPosition .value .x ) * smoothing;
94- displayPosition .value .y += (targetPosition .value .y - displayPosition .value .y ) * smoothing;
95- animationFrameId = requestAnimationFrame (animate);
106+
107+ const dx = targetPosition .value .x - displayPosition .value .x ;
108+ const dy = targetPosition .value .y - displayPosition .value .y ;
109+
110+ if (Math .abs (dx) <= props .smoothSnapThreshold && Math .abs (dy) <= props .smoothSnapThreshold ) {
111+ displayPosition .value .x = targetPosition .value .x ;
112+ displayPosition .value .y = targetPosition .value .y ;
113+ cancelAnimation ();
114+ return ;
115+ }
116+
117+ displayPosition .value .x += dx * props .smoothForce ;
118+ displayPosition .value .y += dy * props .smoothForce ;
119+
120+ animationFrameId = requestAnimationFrame (stepAnimation);
121+ }
122+
123+ function ensureAnimationRunning () {
124+ if (animationFrameId == null && props .show && props .smooth ) {
125+ animationFrameId = requestAnimationFrame (stepAnimation);
126+ }
127+ }
128+
129+ function cancelAnimation () {
130+ if (animationFrameId != null ) {
131+ cancelAnimationFrame (animationFrameId);
132+ animationFrameId = null ;
133+ }
96134}
97135
98136watch ([x, y], ([newX , newY ]) => {
99137 targetPosition .value .x = newX;
100138 targetPosition .value .y = newY;
139+
101140 if (! props .smooth ) {
102141 displayPosition .value .x = newX;
103142 displayPosition .value .y = newY;
143+ } else {
144+ ensureAnimationRunning ();
104145 }
105146});
106147
107- watch (() => props .show , async (show ) => {
108- if (show) {
109- const initialX = x .value ;
110- const initialY = y .value ;
111- targetPosition .value .x = initialX;
112- targetPosition .value .y = initialY;
113- displayPosition .value .x = initialX;
114- displayPosition .value .y = initialY;
115- await nextTick ();
116- if (! animationFrameId) animate ();
117- } else {
118- if (animationFrameId) {
119- cancelAnimationFrame (animationFrameId);
120- animationFrameId = null ;
148+ watch (
149+ () => props .show ,
150+ async (show ) => {
151+ if (show) {
152+ const initialX = x .value ;
153+ const initialY = y .value ;
154+ targetPosition .value .x = initialX;
155+ targetPosition .value .y = initialY;
156+ displayPosition .value .x = initialX;
157+ displayPosition .value .y = initialY;
158+
159+ await nextTick ();
160+ ensureAnimationRunning ();
161+ } else {
162+ cancelAnimation ();
121163 }
122164 }
123- } );
165+ );
124166
125167onUnmounted (() => {
126- if (animationFrameId) cancelAnimationFrame (animationFrameId );
168+ cancelAnimation ( );
127169});
128170
129- const position = computed (() => {
171+ const pixelPosition = computed (() => {
130172 const pos = calcTooltipPosition ({
131173 tooltip: tooltip .value ,
132174 chart: props .parent ,
@@ -144,41 +186,61 @@ const position = computed(() => {
144186const convertedBackground = computed (() => {
145187 return setOpacity (props .backgroundColor , props .backgroundOpacity );
146188});
189+
190+ const tooltipStyle = computed (() => {
191+ const base = {
192+ pointerEvents: " none" ,
193+ position: " fixed" ,
194+ top: " 0px" ,
195+ left: " 0px" ,
196+ transform: ` translate3d(${ pixelPosition .value .left } px, ${ pixelPosition .value .top } px, 0)` ,
197+ borderRadius: ` ${ props .borderRadius } px` ,
198+ border: ` ${ props .borderWidth } px solid ${ props .borderColor } ` ,
199+ zIndex: 2147483647
200+ };
201+
202+ if (! props .isCustom ) {
203+ Object .assign (base, {
204+ background: convertedBackground .value ,
205+ color: props .color ,
206+ maxWidth: props .maxWidth ,
207+ fontSize: ` ${ props .fontSize } px`
208+ });
209+ }
210+
211+ return base;
212+ });
147213 </script >
148214
149215<template >
150216 <teleport :to =" isFullscreen ? parent : 'body'" >
151- <div
152- ref =" tooltip"
153- role =" tooltip"
154- :aria-hidden =" !show"
155- aria-live =" polite"
156- data-cy =" tooltip"
157- :class =" {'vue-data-ui-custom-tooltip' : isCustom, 'vue-data-ui-tooltip': !isCustom, 'vue-data-ui-tooltip-backdrop': backdropFilter}"
158- v-if =" show"
159- :style =" `
160- pointer-events:none;
161- top:${position.top}px;
162- left:${position.left}px;
163- ${isCustom ? '' : `background:${convertedBackground};color:${color};max-width:${maxWidth};font-size:${fontSize}px`};
164- border-radius:${borderRadius}px;
165- border:${borderWidth}px solid ${borderColor};
166- z-index:2147483647;
167- `"
217+ <div
218+ ref =" tooltip"
219+ role =" tooltip"
220+ :aria-hidden =" !show"
221+ aria-live =" polite"
222+ data-cy =" tooltip"
223+ v-if =" show"
224+ :class =" {
225+ 'vue-data-ui-custom-tooltip': isCustom,
226+ 'vue-data-ui-tooltip': !isCustom,
227+ 'vue-data-ui-tooltip-backdrop': backdropFilter
228+ }"
229+ :style =" tooltipStyle"
168230 >
169- <slot name =" tooltip-before" />
170- <slot />
171- <div v-html =" content" />
172- <slot name =" tooltip-after" />
231+ <slot name =" tooltip-before" />
232+ <slot />
233+ <div v-html =" content" />
234+ <slot name =" tooltip-after" />
173235 </div >
174236 </teleport >
175237</template >
176238
177239<style >
178240.vue-data-ui-tooltip {
179- box-shadow : 0 6px 12px -6px rgba (0 ,0 , 0 , 0.2 );
241+ box-shadow : 0 6px 12px -6px rgba (0 , 0 , 0 , 0.2 );
180242 position : fixed ;
181- padding :12px ;
243+ padding : 12px ;
182244}
183245
184246.vue-data-ui-tooltip-backdrop {
@@ -190,8 +252,9 @@ const convertedBackground = computed(() => {
190252 position : fixed ;
191253 z-index : 3 ;
192254}
255+
193256.vue-data-ui-tooltip ,
194257.vue-data-ui-custom-tooltip {
195- will-change : top , left ;
258+ will-change : transform ;
196259}
197260 </style >
0 commit comments