Search Unity

Feature Request Background gradients

Discussion in 'UI Toolkit' started by HrabiaWrzontq, Sep 25, 2020.

  1. HrabiaWrzontq

    HrabiaWrzontq

    Joined:
    Nov 28, 2016
    Posts:
    16
    Hello,

    it would be great if we could define gradients for backgrounds in the style sheets. Something like:

    background-image: linear-gradient(to right, rgba(255, 255, 255, 0) 98%, #ffffff 100%);


    Are you guys planning on adding this feature?
     
  2. MoruganKodi

    MoruganKodi

    Joined:
    Feb 11, 2015
    Posts:
    79
    It would also be usefull to be able to assign a texture 2D directly via code.
     
  3. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    397
    You can do that like this:

    pictureElement.style.backgroundImage = new StyleBackground(image);
     
  4. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    The only way to acheive gradients right now is through custom drawing using the mesh api. If you install the com.unity.ui package, you can then use the PackageManager window to install usage examples.
    upload_2020-9-25_13-49-0.png You can launch this sample using window>UI Toolkit> Examples> Rendering> Mesh API
     
    mikejm_, DrViJ, Kirsche and 1 other person like this.
  5. HrabiaWrzontq

    HrabiaWrzontq

    Joined:
    Nov 28, 2016
    Posts:
    16
    Thanks for the tip. I am giving it a try, but it is quite cumbersome and I don't have much experience with creating meshes. It will have to do for now or I will just ask our designer for the png with gradient.
    However I would still like to know if you are planning on ever adding the css property for gradients.
     
  6. HrabiaWrzontq

    HrabiaWrzontq

    Joined:
    Nov 28, 2016
    Posts:
    16
    I have an issue with the uxml traits. When I set them in the UIBuilder, they apply fine and the mesh is drawn. However when I change focus to any other VisualElement and return back to the Gradient, the values for colors are reset - the mesh is unchanged however.

    Here's how it looks:
    upload_2020-9-28_10-36-37.png

    And here's the code:
    Code (CSharp):
    1. public class Gradient : VisualElement {
    2.         public new class UxmlFactory : UxmlFactory<Gradient, GradientUxmlTraits> { }
    3.  
    4.         public class GradientUxmlTraits : UxmlTraits {
    5.             UxmlColorAttributeDescription leftColor = new UxmlColorAttributeDescription {name = "left-color", defaultValue = Color.red};
    6.             UxmlColorAttributeDescription rightColor = new UxmlColorAttributeDescription {name = "right-color", defaultValue = Color.black};
    7.  
    8.  
    9.             public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) {
    10.                 base.Init(ve, bag, cc);
    11.  
    12.                 if(ve == null)
    13.                     throw new ArgumentNullException(nameof(ve));
    14.  
    15.                 var grad = (Gradient) ve;
    16.                 grad.leftColor = leftColor.GetValueFromBag(bag, cc);
    17.                 grad.rightColor = rightColor.GetValueFromBag(bag, cc);
    18.             }
    19.         }
    20.  
    21.         public Gradient() {
    22.             generateVisualContent += GenerateVisualContent;
    23.         }
    24.  
    25.         public Color leftColor;
    26.         public Color rightColor;
    27.  
    28.         static readonly Vertex[] vertices = new Vertex[4];
    29.         static readonly ushort[] indices = {0, 1, 2, 2, 3, 0};
    30.  
    31.         void GenerateVisualContent(MeshGenerationContext mgc) {
    32.             var rect = contentRect;
    33.             if (rect.width < 0.1f || rect.height < 0.1f)
    34.                 return;
    35.             vertices[0].tint = leftColor;
    36.             vertices[1].tint = leftColor;
    37.             vertices[2].tint = rightColor;
    38.             vertices[3].tint = rightColor;
    39.  
    40.             var left = 0f;
    41.             var right = rect.width;
    42.             var top = 0f;
    43.             var bottom = rect.height;
    44.  
    45.             vertices[0].position = new Vector3(left, bottom, Vertex.nearZ);
    46.             vertices[1].position = new Vector3(left, top, Vertex.nearZ);
    47.             vertices[2].position = new Vector3(right, top, Vertex.nearZ);
    48.             vertices[3].position = new Vector3(right, bottom, Vertex.nearZ);
    49.  
    50.             MeshWriteData mwd = mgc.Allocate(vertices.Length, indices.Length);
    51.             mwd.SetAllVertices(vertices);
    52.             mwd.SetAllIndices(indices);
    53.         }
    Am I missing anything?
     
    Last edited: Sep 28, 2020
  7. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    leftColor and rightColor fields on your element need to be C# properties in order for UI Builder to be able to read your UXML attribute values currently set on your element. UI Builder relies on reflection to find get-properties on an element based on the name of the custom attribute. In your case, the names are good (they match the attribute names) but they are not C# properties.
     
    RunninglVlan likes this.
  8. RunninglVlan

    RunninglVlan

    Joined:
    Nov 6, 2018
    Posts:
    182
    Wow, is it documented somewhere?
    BTW, I'd prefer C# attributes instead of extending
    UxmlTraits
    just to define some attributes. Something like this:
    Code (CSharp):
    1. [UxmlString("custom-attribute")]
    2. private string MyCustomAttribute { get; set; }
    If this would be possible, I'd bet the names shouldn't necessary match too.
    And why do we need to have our own inner
    UxmlFactory
    ? Why can't it just be a C# attribute too?
    Something like this:
    Code (CSharp):
    1. [UxmlElement]
    2. public class MyCustomElement : VisualElement {
     
    Last edited: Jul 22, 2021
  9. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    376
    An alternative to the mesh API is to use the Vector Graphics package
    com.unity.vectorgraphics
    . You can define a gradient in an SVG asset. Make sure you set the "Generated Asset Type" to "UI Toolkit Vector Image" in the importer.
     
  10. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    @RunninglVlan This is indeed cumbersome and what you described is exactly where we want to go in future releases
     
    florianBrn and RunninglVlan like this.
  11. flintmech

    flintmech

    Joined:
    Sep 29, 2011
    Posts:
    32
    +1 to adding linear gradient support to USS!

    Using the Vector Graphics package is also extremely cumbersome. Despite having preview packages enabled, it wouldn't show up in my Package Manager so I had to add it to my manifest manually, and then I had to install Inkscape and figure out how to make the gradient I wanted in it. Finally exported a rectangle with my gradient to SVG and imported it in Unity as a "UI Toolkit Vector Image". My objective was to use the gradient as background image for a button with curved border radius, but no matter what I did I couldn't get the gradient image to honor the border mask (Overflow set to hidden) so.. for now I'm kicking this down the road and just using a PNG image containing my gradient. It won't scale well, but it should get the job done until this can be achieved with the stylesheet.
     
    florianBrn likes this.
  12. jpvanoosten

    jpvanoosten

    Joined:
    Nov 20, 2009
    Posts:
    50
    More than a year later, and the
    com.unity.vectorgraphics
    package is still not coming up in the Unity Registry packages...
     
  13. jpvanoosten

    jpvanoosten

    Joined:
    Nov 20, 2009
    Posts:
    50
    How does the
    unity-slice
    attribute work with vector graphics?
    I have an SVG file that looks like this (couldn't upload the actual SVG file because it's not an accepted extension.)
    upload_2022-11-23_12-26-32.png
    But since it's an SVG file, I'm not sure what the values for the
    unity-slice
    attribute should be? It should be possible to express it in "relative units", but the attribute only accepts integers (I assume it's the pixel units of a statically sized texture).
     
  14. Midiphony-panda

    Midiphony-panda

    Joined:
    Feb 10, 2020
    Posts:
    243
  15. jpvanoosten

    jpvanoosten

    Joined:
    Nov 20, 2009
    Posts:
    50
  16. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    699
    This trick works with UI Toolkit:



    I applied it successfully in my project:

    upload_2022-12-12_22-24-10.png
     
    MurphyMurph_21 likes this.
  17. jpvanoosten

    jpvanoosten

    Joined:
    Nov 20, 2009
    Posts:
    50
    Awesome! I'll mention this to my art team (guy)
     
  18. jpvanoosten

    jpvanoosten

    Joined:
    Nov 20, 2009
    Posts:
    50
    I noticed you're using square corners. Are there any issues when using rounded corners?
     
    dlorre likes this.
  19. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    699
    Several of my elements have round corners however I didn't plan to make rounded windows, though now that you mention it I might try it.
     
  20. poprev-3d

    poprev-3d

    Joined:
    Apr 12, 2019
    Posts:
    71
    Modern UIs require gradient (with parsimony). Being able to easily generate gradients at runtime using linear-gradient would be extremely valuable.
     
    VenetianFox, fxlange and StPaulStylee like this.
  21. dmitry_unity246

    dmitry_unity246

    Joined:
    Jul 29, 2022
    Posts:
    1
    nope, image backs are cropped correctly with border radius (almost perfect except some white outline on corners (idk what's that)

    upload_2023-5-4_9-52-34.png

    The only problem is vector graphics package gives me such weird result on gradients when i try to import them as vector images, details under spoiler

    on both importers same 2x2 gradient vector image from FFFF(up) to 888F(down)

    upload_2023-5-4_9-55-2.png

    using directly vector data is not possible yet for gradients, also for radial gradients larger texture need to be generated on svg import
    in any case i used vectors in project as sources and assigned them through styles so import type change will not affect any UXML or USS code.
     
  22. duckreaction

    duckreaction

    Joined:
    Mar 27, 2022
    Posts:
    2
    Hi!
    Another solution inspired by HrabiaWrzontq's solution.
    This code allow to control gradient from uss properties, usage :

    Code (CSharp):
    1. // uxml
    2.     <DuckReaction.Ui.GradientElement name="GradientElement" class="gradient" style="width: 100%; height: 100%;" />
    3.  
    4. // uss
    5. .gradient {
    6.     --gradient-from: var(--color-moon-1);
    7.     --gradient-to: var(--color-moon-2);
    8.     --gradient-direction: vertical; // or horizontal
    9. }
    Source :

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.UIElements;
    4.  
    5. namespace DuckReaction.Ui
    6. {
    7.     public enum GradientDirection
    8.     {
    9.         Horizontal,
    10.         Vertical
    11.     }
    12.  
    13.     public class GradientElement : VisualElement
    14.     {
    15.         static readonly Vertex[] _vertices = new Vertex[4];
    16.         static readonly ushort[] _indices = { 0, 1, 2, 2, 3, 0 };
    17.         static readonly CustomStyleProperty<Color> _gradientFromProperty = new("--gradient-from");
    18.         static readonly CustomStyleProperty<Color> _gradientToProperty = new("--gradient-to");
    19.         static readonly CustomStyleProperty<string> _gradientDirectionProperty = new("--gradient-direction");
    20.         GradientDirection _gradientDirection;
    21.  
    22.         Color _gradientFrom;
    23.         Color _gradientTo;
    24.  
    25.         public GradientElement()
    26.         {
    27.             generateVisualContent += GenerateVisualContent;
    28.             RegisterCallback<CustomStyleResolvedEvent>(OnStylesResolved);
    29.         }
    30.  
    31.         void OnStylesResolved(CustomStyleResolvedEvent @event)
    32.         {
    33.             @event.customStyle.TryGetValue(_gradientFromProperty, out _gradientFrom);
    34.             @event.customStyle.TryGetValue(_gradientToProperty, out _gradientTo);
    35.             @event.customStyle.TryGetValue(_gradientDirectionProperty, out var gradientDirectionAsString);
    36.             if (Enum.TryParse(typeof(GradientDirection), gradientDirectionAsString, true, out var gradientDirection))
    37.                 _gradientDirection = (GradientDirection)gradientDirection;
    38.             else
    39.                 _gradientDirection = GradientDirection.Horizontal;
    40.         }
    41.  
    42.         void GenerateVisualContent(MeshGenerationContext meshGenerationContext)
    43.         {
    44.             var rect = contentRect;
    45.             if (rect.width < 0.1f || rect.height < 0.1f)
    46.                 return;
    47.  
    48.             UpdateVerticesTint();
    49.             UpdateVerticesPosition(rect);
    50.  
    51.             var meshWriteData = meshGenerationContext.Allocate(_vertices.Length, _indices.Length);
    52.             meshWriteData.SetAllVertices(_vertices);
    53.             meshWriteData.SetAllIndices(_indices);
    54.         }
    55.  
    56.         static void UpdateVerticesPosition(Rect rect)
    57.         {
    58.             const float left = 0f;
    59.             var right = rect.width;
    60.             const float top = 0f;
    61.             var bottom = rect.height;
    62.  
    63.             _vertices[0].position = new Vector3(left, bottom, Vertex.nearZ);
    64.             _vertices[1].position = new Vector3(left, top, Vertex.nearZ);
    65.             _vertices[2].position = new Vector3(right, top, Vertex.nearZ);
    66.             _vertices[3].position = new Vector3(right, bottom, Vertex.nearZ);
    67.         }
    68.  
    69.         void UpdateVerticesTint()
    70.         {
    71.             if (_gradientDirection is GradientDirection.Horizontal)
    72.             {
    73.                 _vertices[0].tint = _gradientFrom;
    74.                 _vertices[1].tint = _gradientFrom;
    75.                 _vertices[2].tint = _gradientTo;
    76.                 _vertices[3].tint = _gradientTo;
    77.             }
    78.             else
    79.             {
    80.                 _vertices[0].tint = _gradientTo;
    81.                 _vertices[1].tint = _gradientFrom;
    82.                 _vertices[2].tint = _gradientFrom;
    83.                 _vertices[3].tint = _gradientTo;
    84.             }
    85.         }
    86.  
    87.         public new class UxmlFactory : UxmlFactory<GradientElement, GradientElementUxmlTraits>
    88.         {
    89.         }
    90.  
    91.         public class GradientElementUxmlTraits : UxmlTraits
    92.         {
    93.         }
    94.     }
    95. }
    Enjoy
     
  23. RunninglVlan

    RunninglVlan

    Joined:
    Nov 6, 2018
    Posts:
    182
    I see that Unity finally supports attributes on custom UITK controls in latest Alpha! That's great!
     
  24. patrickjarnfelt

    patrickjarnfelt

    Joined:
    Jun 24, 2013
    Posts:
    28
    I also think gradient backgrounds would be a nice feature to have natively.
    Alternatively I agree that the SVG vector background on a Visual Elements (VE) is a good alternative solution.

    I noticed that the border radius is ignored on the VE that has the background SVG, but if I add it to a container and give that the border radius with overflow:none; then it manages to make rounded corners and hide the background on the child. Is this the right way to do it?

    Screenshot 2023-08-18 at 11.15.03.png
    I added a white outline to the VEs above to show where the child should've masked the corner and where the parent does manage to mask the corners.


    a simple svg with gradient:

    Code (JavaScript):
    1. <svg height="100" width="100">
    2.   <defs>
    3.     <linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
    4.       <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
    5.       <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
    6.     </linearGradient>
    7.   </defs>
    8.   <rect width="100" height="100" fill="url(#grad1)" />
    9. </svg>
     
  25. StPaulStylee

    StPaulStylee

    Joined:
    Apr 10, 2019
    Posts:
    1
    This functionality needs to be here. Or can we get an update when it might be available? As a front-end dev I want UI Toolkit for Runtime to be my default UI solution... But when it doesn't have basic functionality like this I just get discouraged.
     
  26. pzoghbi

    pzoghbi

    Joined:
    Oct 8, 2019
    Posts:
    24
    *On road map since 2021.*
    Better pay per install.