Open kanitw opened 5 years ago
After looking into this issue, I think the problem occurs whenever you are hovering over a region where there is no data point (i.e. a region that is in the middle of a line connecting two points). For some reason, the code automatically assumes the value of this region to be the same as the value as the very right-most data point, causing the highlighting jump since it is stored into 'highlighted_store.'
I'm not really sure what the best way to approach fixing this is. I've been looking at the compiled Vega code and the values being generated, and one heuristic I see that we may be able to use is the fact that when you hover over an existing point there is no datum
field under the unit
signal. However, when you hover in this middle region where no data point exists, there datum
field under the unit
signal is non-null. I tried to have a conditional check under the highlighted_modify
signal for
"on": [{
"update": "!unit.datum ? modify(\"highlighted_store\", highlighted_tuple, true) : null"
}]
but this doesn't seem to fix the problem fully. If we do a check for the value in highlighted_tuple
such that it is not equivalent to the right most value, the problem goes away. However, this is not a good solution for the problem and it remains how to handle when the user wants to examine the right most value as well.
"on": [
{
"events": {"signal": "highlighted_tuple"},
"update": "highlighted_tuple.values[0] == 1267419600000 ? null : modify(\"highlighted_store\", highlighted_tuple, true)"
}
]
Any advice?
This is the same problem as in issue #4260
@allenjlee Your intention is to solve the issue or you want something that works? For the latter, as kanitw mentions, move the selection to the part of the point (or rule).
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"description": "Stock prices of 5 Tech Companies over Time.",
"data": {"url": "data/stocks.csv"},
"transform": [{"filter": {"field": "symbol", "equal": "AAPL"}}],
"encoding": {
"x": {"field": "date", "type": "temporal"},
"color": {"field": "symbol", "type": "nominal"}
},
"layer": [
{
"mark": "line",
"encoding": {"y": {"field": "price", "type": "quantitative"}}
},
{
"mark": {"type": "rule", "opacity": 1},
"selection": {
"highlighted": {
"type": "single",
"on": "mouseover",
"encodings": ["x"],
"empty": "none",
"nearest": true
}
},
"encoding": {
"opacity": {
"condition": {"selection": "highlighted", "value": 1},
"value": 0
}
}
},
{
"mark": {"type": "point", "opacity": 1, "size": 100, "fill": "white"},
"encoding": {
"y": {"field": "price", "type": "quantitative"},
"opacity": {
"condition": {"selection": "highlighted", "value": 1},
"value": 0
}
}
}
]
}
or use a higher size value in the rule
mark (eg. "size": 2
)
"mark": {"type": "rule", "opacity": 1, "size": 2,"tooltip": false},
EDIT: This only works when the else value
is higher than 0..
{
"mark": {"type": "rule", "opacity": 1, "size": 2,"tooltip": false},
"encoding": {
"opacity": {
"condition": {"selection": "highlighted", "value": 1},
"value": 0.1
}
}
}
@allenjlee -- more context for you from our discussion on Slack that you may not see:
We think there is perhaps an underlying problem that leads to this issue, #4553, and #4260, namely:
The selection logic is capturing all mouseover events regardless of which mark it comes from, which seems problematic in general.
Here are some more useful comments from slack:
Hover-encoding hit testing for a line is for the whole line not individual data points (same for area). Tooltip hit testing for lines should work per-point, such that the mouse cursor must be on the line and nearby the sampled data point
FYI, if I set
interactive: false
on the line mark and use only the point mark then the example behaves itself. I think what is happening is that mouseover on the line is always resolving to the last datum, which is consistent with what I said earlier (it is representing the “whole line”).
I'd look into the signal flow for selections to see if we can capture more mark-specific events or check if the interaction is from the mark of interests.
Another alternative is to output interactive: true
only for marks that should be interactive. That said, I think we should look into the event capturing first since that seems more like the root cause. The latter solution is more like a hack and may be limiting our interaction capability in the future.
(Btw, I assume you're working with Arvind at MIT? If so, I can add you to our team so you can assign issues that you plan to work on to you.)
@mattijn Oh ok thanks. I missed his last sentence at the end. It seems like this jump problem no longer exists with this solution. Also maybe I'm not understanding that the single selection is shaky, but it looks smooth to me with the example code you posted. So is this issue still open? Or is the issue trying to be solved the underlying problem you mentioned:
The selection logic is capturing all mouseover events regardless of which mark it comes from, which seems problematic in general.
@kanitw Yes, sorry for not providing context. I recently started working with Arvind and he showed me these issues ( #4553 and #4260 ) to work on. It would be great if you could add me to the team so that issues can be assigned.
Also, I was playing around and I think one potential solution may be to, as you said, add a check on the mark of interest. For example, it seems that the mark of interest is usually the mark with the name layer_0_marks
(something like: item().mark.name == 'layer_0_marks'
), so I added this check when updating for #4553 and the problem seemed to go away. Thanks.
@allenjlee No problem. Just send an invitation. Welcome to the team!
(something like:
item().mark.name == 'layer_0_marks'
)
Yeah if that generally works, let's make VL always add that :)
Ah, I now recall why I didn't have a more precise check (e.g., for the specific mark name) in there to begin with.
If you try it out with our interactive_query_widgets
example and deselect all points, a specific mark name test will cause the selection to no longer ever trigger. This is because it's defined on the lower (grey) layer and, when empty, points in the upper (colored layer) fully occlude it.
One alternative might be to test if the fields a selection is operating are over are all defined on the datum
. Need to think through what happens if a particular field has an undefined
(or otherwise falsy) value.
If interactive_query_widgets
is the rational behind non-precise check, I wonder if the right solution is to support setting selection at the layer level.
So we can define the selection once at the layer level, but let it propagates and applies to multiple layers. Then we can have a stricter check.
Copying from #4885:
@jheer:
Perhaps an appropriate solution is to have a policy that any marks not implicated in event processing have interactive: false set in the output Vega? One complication might be the Vega 3+ behavior of automatically enabling tooltips, in which case everything is implicated in event processing. I'm already skeptical of having tooltips always-on by default, but this seems to suggest that they might be harmful to interaction design.
@arvind:
My preference would be to use
interactive: false
because all the other options come with a whole set of idiosyncratic edge cases when considering multiview composition. However,interactive: false
is tricky if tooltips are enabled by default. I've never been thrilled about tooltips being enabled by default so I'd be supportive of making them opt-in.
Using single selection with mouseover on the line mark is not smooth.
From this spec with
"nearest": true
, the GIF below shows that the selection can be quite "flashy". We can even produce a state where there is no "nearest" point selected.Fortunately, move the selection to the "point" layer makes it smooth.