Any way to mask with UI Toolkit?

This seems like a fundamental feature but I can’t for the life of me figure out how to mask objects in the UI.

How would one even go about making a progress bar that isn’t a rectangle without masking? Or avatar images without it? Surely this has to exist but I can’t see any way to do it.

It’s only possible to use rectangular masks. You have to set overflow: hidden; on the masking element and define height and width. The child elements need to have position: absolute; so that their dimensions go beyond the parent element.

For the progress bar:
If the bar is just a simple rectangle with a background color (maybe with rounded corners), you can set its width in percent.

Take a look at this thread :

If you’re not afraid of adding a dependency to com.unity.vectorgraphics and using SVG assets, this is the kind of thing you can do :

1 Like

If everything goes as planned, we will land an API in 2022.2 that allows to create a VectorImage from a “detached” Painter2D. You will be able to assign it as a background, which can be used to mask with overflow=hidden. This should avoid the package requirement and will allow runtime-generated shapes.

5 Likes

Interesting. Thanks for the suggestion. An SVG will work in the case of a bar, but unfortunately not in the case of an avatar profile (ie think a player’s profile image inside a circle).

-edit- oh wait I think this example is suggesting using the SVG as a mask correct? If so that’s perfect

Use a SVG as a mask, yes :wink:

In the screenshot above, the red parts are just red rectangles being masked by the parent VE.
The first parent VE is a SVG without any filling, whereas the second VE is another SVG using the same shape, but with filling.

For your use case, you should be OK by :

  • giving to Unity the SVG of a filled circle
  • make it the background of a VE
  • set the VE overflow to hidden (it will be the parent)
  • put children you need to mask inside this parent VE

For the round avatars, you mentioned you can also use the nifty radius style element :slight_smile:

The SVG approach works fine (although creates some aliasing which I don’t love). The radius element was actually my first attempt but the issue with that is that it doesn’t scale. I suppose it might if I used a transform scale instead but in my use case that’s not really possible.

1 Like

Did this happen?

Yes:

1 Like

Will anti-aliasing be improved in the near future? Here’s what it looks like now:

source: SVG Masking - Community Wiki - OneJS

i give up and use rendertexture instead.

I’ve successfully followed the examples and I’m creating a polygon with Painer2D, then saving it to be used as the mask. The problem is, the dimensions of the saved mask don’t match what it’s supposed to mask.

Example: You create a triangle to be created from the center of a 1024x1024 VisualElement. This works great, you save that to be used as a mask, but the resulting Vector image is only the size of the triangle. So if I wanted to place it in another container (parent) to be the mask for the children, there is no simple way to center it. The reasonable way is to have the Vector image the same size.

In looking through the api, there isn’t a way to tell it “use the entire bounding area”.

This is especially problematic with a triangle since “drawing from the center” is not like all other poly’s with more sides. And unfortunately for me, I have to support triangles :wink:

You can see in the image the red and green lines - the red line is the actual center of the saved Vector image. The center of the triangle is the green line and that’s where I need it to be for rotation and masking as well as any other transformations I might have to do.

Is there an algorithm to center something like this? Yeah, but 1) it introduces an entire complexity to the system that deals with this 2) it has performance implications written all over it 3) It’s not easily extendable and seems like we’re putting in a hack for just one type of poly - arguably the most used shape unfortunately.

Is it possible to save the Vector image to be identical to the VisualElement it’s been created from?

There’s an issue with the SaveToVectorImage() method where its resulting size is too large. A fix should be available soon:
https://issuetracker.unity3d.com/issues/vectorimage-bounding-box-is-too-small-with-some-shapes

If I understand your use-case properly, I think this will help fix your issues.

@mcoted3d - thanks for the reply! It does sound like it *could be for sure. What my issue is that if I create a triangle that only takes up a fraction of the 1024 VisualElement’s bounding box, will I get a vector image who’s bounding box affords the same room around the poly. Basically, if I add it to a VisualElement after saving it will it be the same size/position as what created it ?

The VectorImage will have the same size as the visible content of your Painter2D. It may not have the same position, however. Since you can draw content in the negative axes, the SaveToVectorImage() method will bring the content back at (0,0) with the size of the visible bounding box. This is documented here:
https://docs.unity3d.com/ScriptReference/UIElements.Painter2D.SaveToVectorImage.html

The other factor is what happens when you assign a VectorImage to a VisualElement background. It will essentially behave like a textured background, and will stretch inside the VisualElements bounds according to the scale mode of that element.

So, if you want to have the VectorImage appear at the same size and location as the original content, you would need to assign it to a VisualElement that has the same size as the resulting VectorImage, and change the position of that VisualElement at the same offset.

We plan to add a variant of SaveToVectorImage() that keeps the offsets intact, which will simplify the process.