vega
vega copied to clipboard
Allow marks to be skipped for rendering
Many examples use transparency to hide the marks when not needed, but this is a hack - the object is still there, and it still reacts to events. I would like to propose a new mark parameter to allow for mark's "skipping", e.g. "skip": {"expr": "..."} (or disable) During rendering of each mark, if skip is set to true, mark will not be rendered.
This graph represents a common scenario of showing all data (green box), but when clicked, show a drill-down data (red). The drill down mode has a "show all" button. In order to show and hide this button, I create a dummy data source with a single element, and filter that element out if the button should not be shown.
In Vega 2.0 it was possible to do this without creating an extra wrapping group, but Vega 3 does not allow filtering.
{
"$schema": "https://vega.github.io/schema/vega/v3.0.json",
"width":400,
"height":100,
"signals": [
{
"name": "isEnabled",
"value": false,
"on": [
{"events": "@mygraph:click", "update": "true"},
{"events": "@button:click", "update": "false"}
]
}
],
"marks": [
{
"type": "rect",
"name": "mygraph",
"encode": {
"update": {
"width": {"value": 100},
"height": {"value": 100},
"fill": {"signal": "isEnabled ? '#f00' : '#0f0'"}
}
}
},
{
"type": "group",
"data": [
{
"name": "dataForShowAll",
"values": [{}],
"transform": [{"type": "filter", "expr": "isEnabled"}]
}
],
"encode": {
"enter": {
"xc": {"signal": "width/2"},
"y": {"value": 30},
"width": {"value": 80},
"height": {"value": 30}
}
},
"marks": [
{
"type": "group",
"name": "button",
"from": {"data": "dataForShowAll"},
"encode": {
"enter": {
"cornerRadius": {"value": 6},
"fill": {"value": "#f5f5f5"},
"stroke": {"value": "#c1c1c1"},
"strokeWidth": {"value": 2},
"height": {"signal": "item.mark.group.height"},
"width": {"signal": "item.mark.group.width"}
},
"update": {"opacity": {"value": 1}},
"hover": {"opacity": {"value": 0.7}}
},
"marks": [
{
"type": "text",
"interactive": false,
"encode": {
"enter": {
"xc": {"signal": "item.mark.group.width/2"},
"yc": {"signal": "item.mark.group.height/2 + 2"},
"align": {"value": "center"},
"baseline": {"value": "middle"},
"fontWeight": {"value": "bold"},
"text": {"value": "Show All"}
}
}
}
]
}
]
}
]
}
Vega 2 did create a wrapping group internally (and not always in a 100% correct manner). Vega 3 makes this more explicit (and correct) at the cost of some extra specification work.
Is the desired behavior here similar to display: none in CSS, such that the object does not participate in layout or bounds calculation either?
Labeling as a feature request. This will have to wait to post 3.1. release. The work involved should be primarily at the scenegraph level only. An encoder might then set an enabled (or disabled?) encoding channel and the scenegraph (+ bounds calculation) should respond appropriately.
Funny, I actually was considering putting display: none in the title of this ticket, so yes, that's a good analogy. Thanks!
Given that it's so similar to display in CSS -- I wonder if the right encoding channel name should be display (rather than enabled / disabled)?
I know that it's not exactly the same as we won't have display: block, etc. but I still like it as a name better than enabled / disabled
So are we good with display: true|false API for this feature? This would be awesome for anything dynamic, thanks!!!
I just realized that the trail mark has a custom implementation of this feature called defined -- https://vega.github.io/vega/docs/marks/trail/ -- should this be joined?
Ok, upon further reflection, I think this feature should be implemented as defined (boolean). The reason for it is it actually works identically to the trail mark -- it hides a single datum, not the whole mark. For discrete marks (symbols/rect/arc/text), this works similar to CSS display:none. For continuous marks line line/trail, it simply removes a single point from the line. This behavior would be identical to being able to add filter transform to the mark's local data source.
I kinda find the term defined a bit misleading for enabling/disabled other marks though.
For line/trail/area, skipping a point because it is not "defined" sound reasonable.
But say if you create interactive visualization that toggles to show/hide some points, using "defined" to specify whether it is enabled/displayed doesn't sound right.
@kanitw I agree that "defined" could be misleading, but so could be "display". Defined implies if the DATUM should be processed or skipped, so from that perspective it is consistent for both the line nodes and rectangles. Display could be confusing because I would think it applies to the whole feature, which becomes weird if you base it on datum rather than just signals. Naming is hard :)
Display could be confusing because I would think it applies to the whole feature, which becomes weird if you base it on datum rather than just signals.
I don't understand what you mean "it applies to the whole feature". Can you explain more?
if you have a line, you may want to make it disappear as a single object. Setting defined to false on every point of that line accomplishes that goal, but in a way its confusing, because it could depend on datum.
BTW, you may also want to hide line segments, but that's beyond the scope of this issue.
Regardless of name, the exact semantics for this are a bit tricky. For line/area/trail marks, defined=false still involves a corresponding scenegraph item, representing a point that participates in the overall layout even though the point itself is not drawn. So, this is definitely not equivalent to filtering out objects.
If we add support for display='none', I'm still unsure of the desired behavior. One option would be for this to act like a filter that ensures the corresponding scenegraph item is not included. However, since display would be an encoding property, the dataflow would involve a data join, encoding, then a filter prior to attaching items to the scenegraph. The same behavior (with less internal processing overhead) is already available if you create a derived dataset with a filter transform. (Note that even in this case items with display='none' might still participate in the layout if they are used as input to a layout transform such as tree or voronoi prior to any filtering.)
The other option would be to include all items in the scenegraph, but toggle their visibility and interactivity in response to the display property. In this case, the items would participate in the total bounds calculcation and so could affect view layout and autosizing (which might be desirable). However, this behavior can already be achieved by setting the fill and/or stroke to "none".
So, it seems that (1) it's not clear which semantics should be used, and (2) in any case those semantics are already achievable by other means. Even if we have a good answer for (1), I don't know if the extra syntactic sugar is worth the additional complexity it would add to the encoding/scenegraph implementation.
Jeff, if setting stroke and fill to "none" makes the element not just transparent, but actually disappear from event handling, than this is perfect for block marks (e.g. rect/arc/text). I tried to find this in the doc, but didn't see it. I think we should update examples to use this method instead of setting opacity, as it is more appropriate. (BTW, I think all docs about colors should link to a common color specification section, plus we may want to mention this effect).
Skipping nodes within a line is not as important for my usecase, so this is less of an issue, but we could add it for consistency between different mark types. (can use class inheritance :))
There is one exception to the above: groups. It would have solve many of my use cases to show/hide the entire content of a group, without adding show?"none":"some_color" to stroke & fill of every element inside the group. The current work around is shown in the task description above -- creating a group with a dummy data and a dynamic filter, and inside create another group based on the first group's data, and inside the second group draw the needed elements. Having a defined channel to hide the whole group without all that confusing (to novices) nesting would be very helpful.
Hi, I am experiencing a similar problem as @nyurik in that I want to hide text labels for piecharts for which the slices are smaller than a certain angle. I was hoping to be able achieve that with a similar function as "filter" in Vega 2, just for Vega 3. The explicit method using group is problematic because the cutting criteria depends on the size of the slice angle and the length of my text element. I would therefore appreciate something like option (1) as explained by @jheer.
Hi @derdem, an alternative solution for your case might be to set the text fillOpacity to zero when the slices are small. If helpful, you could use reactive geometry (that is, have the arc mark for the pie slices serve as the backing data for the text slice labels), such that your text mark has direct access to the start and end angle values.
@jheer I recommended setting text conditionally based on the slice size -- I suspect this is more performant than actually doing a transparent rendering. I haven't done any performance tests for this, but this is something we should be couscous about - we did get a few overall performance questions about Vega.
Is reactive access of the text mark to arc mark a better approach than direct access to the same data source? Thx
Yes, setting the text content to the empty string is a perfectly reasonable alternative. However, I would be rather surprised to see a perceptible performance difference between the options here. In the case of Canvas, the renderer will not draw text if it is the empty string or if there is zero opacity, so the end result is the same. In the case of SVG, an element will be added to the DOM regardless.
From a discussion in https://github.com/vega/vega/issues/1367, it seems like "none" is not a good option as it is not supported in HTML5 Canvas (at least on Chrome).
Thanks @kanitw, setting to null in Vega should be used instead of "none".
Hello, I also have a problem with hiding items in vega-label.
- vega-label hides label by changing
fillandstroketo'none'. In an interactive plot, The problem is vega-label cannot make hidden label visible again becausefillandstrokeis overwritten to’none'. The solution right now is that vega-label derives another field to keep the originalfillandstroke, and vega-label uses information from that field to place the hidden label. - Another useful use case for having a hiding property is when users want some labels to not be shown, they can flag those labels to be hidden before using label transform. Right now, since there are many ways to hide labels, vega-label may place a label that ends up will not show because some field makes the label invisible. Having a one unique way to hide item would help identifying which label is meant to be hidden.
Such a Vega feature on the mark level would be very useful. Almost all my Vega-hacks revolve around not being able to disable or display:none elements using a signal.
The question is: do you guys want to toggle the visibility or the display? In CSS there are 2 properties: "display" (block, none, flex, etc) and "visibility" (visible, hidden)
My use case is as in CSS visibility property. And as I understand I could achieve the same effect setting fill to none. The problem is that if I have a mark of type group setting fill property to none will only affect that group mark and not it's children.
I was thinking to an API like this:
"marks": [{ "name": "carGroup", "type": "group", "visible": {"signal": "isCarGroupVisible"} }]
The property would be called "visible" and could take a value of "true" or "false" or a signal that represents a boolean. If visible = false -> any marks that's inside this mark should not be interactive. If visible = false -> any marks that's inside this mark should not be visible. The elements should be drawn on the scene and should affect the layout.
Is there any way to hide all the children from a group mark as of v5?
Hi @TaridaGeorge ,
Thanks for the question: we would rather have a display-type property (as to a CSS-visibility type) that will disable any sort of rendering and processing for the mark and/or group mark children, including any side effects the mark would otherwise generate (like influencing the layout). This would greatly help in making more complex interactive charts and keeping performance up; also for charts that automatically reconfigure based on the data.
I'm not convinced "display" would be the best name for such a property, maybe "enabled"?
I would also be happy with your "visible" proposal, but could use the fully disabled mark setting much more.
RE "Is there any way to hide all the children from a group mark as of v5?": According to the docs not officially. See https://vega.github.io/vega/docs/marks/group/ and https://github.com/vega/vega/blob/master/packages/vega-scenegraph/src/marks/group.js.
Hello from 2024 - is this achievable in some way yet? I'd love to disable a mark or a group of marks together. Right now I'm doing fiddly things with opacities or zero heights and clipping - it really doesn't seem the best approach.
Hello 4 years on, we'd still really love to have this feature. Especially after 4 more years of continued ugly hacks ;). It's really ruined our vega markup.
I would propose a new mark level property called "disabled". If its value or expression resolves to true, then its processing is ignored and it isn't rendered.
#We'd be happy to consider a pull request for this feature. If that's not an option, I'd be happy to chat about lab sponsorship for my students who could potentially work on this. Right now, we don't have a developer for hire on staff for the Vega organization but it's something we have considered.
Thanks @domoritz - I've tried to implement it previously but failed to find the proper installation places. I will give it another go in the next weeks, but probably need some guidance from you though. Best, -dan