-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix tooltip animation when target changes while animating #5005
Conversation
- tooltip in 'index' mode doesn't animate smoothly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jcopperfield
@jcopperfield do you have a fiddle that shows the new behavior ? |
@simonbrunel codepen example of the new behavior. |
src/core/core.tooltip.js
Outdated
} | ||
|
||
// always call me.pivot before returning to | ||
// reset me._start for smooth animations issue #4989 | ||
me.pivot(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@simonbrunel not calling me.pivot()
codepen causes a much weirder experience by resetting all settings to their initial values. (sorry don't have any software to make a fancy gif).
Any suggestions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't debug right now :\ Maybe _active
is different depending on the hovered bar, so the first changed
value is true
because of the array order?! Though, I'm not sure why checking the latest value of changed
doesn't work:
// {...}
if (changed) {
me.pivot();
}
return changed;
}
BTW, the following looks weird (line 869):
if (changed) {
// {...}
// See if our tooltip position changed
changed |= (model.x !== me._model.x) || (model.y !== me._model.y);
}
because if changed
is true
then changed |= true or false
is necessary true
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@simonbrunel I agree that line 869 is obsolete, however it was there already obsolete from a previous pull.
The problem doesn't lay in this handleEvent, but in its caller, which |=
its changed
with the result of the tooltip.handleEvent
(changed |= tooltip && tooltip.handleEvent(e);
). When the caller has changed, but the tooltip hasn't, the bug presents itself, because the animation will run. I don't understand the animation system well enough to prevent the tooltip animation restarting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it should not be changed &= (model.x !== me._model.x) || (model.y !== me._model.y);
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue seems to be that we detect a change for the highlighted item resetting the current animation. Calling pivot()
as you do makes things better in this case but breaks animations when hover interaction mode/intersect are the same as tooltip (master - PR).
mode, when target doesn't change.
@simonbrunel The latest push is in the handler that is causing the problem. I'm not sure if the animation behaviour with |
@simonbrunel The new PR seems to be working a bit better than the previous one. // v2.7
changed |= tooltip && tooltip.handleEvent(e);
// PR
if (tooltip) {
changed = tooltip.handleEvent(e);
} |
It doesn't work, hover events are broken (skipped) as soon as the tooltip reaches its last position (and so returns false to For example: https://codepen.io/anon/pen/mqvKJB tooltips: {
mode: 'index',
intersect: false
},
hover: {
mode: 'index',
intersect: true
}, doesn't work if you move the mouse at the same index but over and outside a bar. |
@simonbrunel I see. So should there be a flag |
If think the problem is that animations are handled globally and thus are not independent from each others, so when there is a change anywhere, we restart all current animations (I'm not even sure we can have animations with different durations?!) Ideally, animations should be handled separately and locally: the tooltip plugin should be able to trigger an animation with its own parameters, stop/restart this animation without impacting others, etc. But I think that would require a rewrite of the animation service which is out the scope of this PR (and could be a bit complicated). Though, I'm investigating this solution to animate stuff in the datalabels plugin. Right now I don't think of a correct way to easily fix that issue, the flag suggestion could "maybe" work but will certainly be hacky to synchronize :\ |
to keep track of tooltip animation.
@simonbrunel I agree that a per element animation service would be the optimal solution. Introducing a tooltips: {
mode: 'index',
intersect: false
},
hover: {
mode: 'index',
intersect: true
} |
src/core/core.element.js
Outdated
view[key] = c1.mix(c0, ease).rgbString(); | ||
continue; | ||
} | ||
c1 = color(target); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why changing that part of the code? Your changes actually decrease performances because c0
and c1
are now always evaluated (color()
), which is a very costly operation while transitioning values.
I would avoid adding a flag to the base element class to workaround an issue with the animation service and especially with the tooltip. It's also semantically incorrect since you force the element to report an animating state while it's not when If you really want to rely on a flag, I would keep that workaround at the highest level, so in the tooltip class and private Don't take care of the newly reported "cognitive complexity" of CodeClimate, I'm going to increase it. |
@simonbrunel the |
The reviewed code was this one, I didn't notice you changed it for |
I will rename the |
What about using (still as dirty workaround) the changed = tooltip._start // === animating
? tooltip.handleEvent(e)
: changed | tooltip.handleEvent(e); That would allow to not touch the element class. |
That might work. I will try that. |
Add: use 'tooltip._start' as workaround check for tooltip animation status
@simonbrunel Is there anything missing from this PR or PR 4959 that prevents merging? |
* Fix issue chartjs#4989 - tooltip in 'index' mode doesn't animate smoothly. * Change: different approach for smooth tooltip animation in 'index' mode, when target doesn't change. * Fix: jslint error * Fix: remove spyOn pivot from test * Add: setAnimating-flag in transition used to set on tooltip.transition to keep track of tooltip animation. * Decrease code complexity * Revert transition and complexity changes Add: use 'tooltip._start' as workaround check for tooltip animation status
* Fix issue chartjs#4989 - tooltip in 'index' mode doesn't animate smoothly. * Change: different approach for smooth tooltip animation in 'index' mode, when target doesn't change. * Fix: jslint error * Fix: remove spyOn pivot from test * Add: setAnimating-flag in transition used to set on tooltip.transition to keep track of tooltip animation. * Decrease code complexity * Revert transition and complexity changes Add: use 'tooltip._start' as workaround check for tooltip animation status
tooltip in 'index' mode doesn't animate smoothly.
Fixes #4989