16

I'm not sure if this is possible or not, but can I use CSS/Jquery techniques to create a gradient mesh? Something similar to this enter image description here

I'd like to randomly generate this mesh and then possibly animate it, so I'm trying to avoid using images. I'm not sure if something like this would even be possible.

I was thinking maybe creating several layers of individual gradients and then layering them all together into a fixed position and changing their opacity settings?

Qix - MONICA WAS MISTREATED
  • 14,451
  • 16
  • 82
  • 145
android_student
  • 1,246
  • 1
  • 13
  • 32

4 Answers4

8

At present

I experimented with something along these lines a few years ago, using SVG, the HTML5 canvas tag, and more recently CSS3 gradients. I don't believe there's a natural way to go beyond simple linear or radial gradients at present.

The question is if a mesh gradient can be simulated using only simple linear and radial gradients (which are the only tools available).

Unfortunately, when you combine multiple gradients using opacity or rgba, the colors of the different gradients tend to combine in a way that's not useful, leading to washed-out colors. Avoiding this would require being able to render it in the browser as a single complex gradient.

There are also significant limitations to the shapes the gradients can have -- linear gradients at an arbitrary angle, and elliptical gradients with radial symmetry. Neither allows for free-form, irregular shapes. The 2D transformations that can be applied to the resulting image are fairly regular in nature as well (scaling, skewing, etc).

In the future

The most promising option I'm aware of for the near future is the possible support for mesh gradients in SVG 2.0 (and perhaps diffusion curves as well). If this does happen and it's eventually supported by browsers, that should start to greatly expand the options. And the HTML5 canvas tag and CSS3 may follow soon afterwards.

And as @vals pointed out in the comment below, WebGL should offer some very promising options (for HTML5 canvas tags using a 3D context).

Related links

Matt Coughlin
  • 18,666
  • 3
  • 46
  • 59
  • 1
    You are right that in overlaying multiple gradients you have the problem of lack of control over the averaging/composing function. It's also true that given a large enough set of gradients, you could aproximate whith enough accuracy (in the limit, 1 gradient per pixel ...) but this is obviously not practical. Beyond the posibilities that you say, there is also WebGL. You have a real interpolation between vertexs, and total freedom fior the shape. – vals Feb 19 '13 at 17:12
  • @vals: Thanks for the background info about WHY gradients don't overlay well; I had observed this but couldn't explain it. I've experimented with breaking a gradient down into small squares with a solid bg color for each, but it renders extremely slowly and/or looks blocky. WebGL sounds like a good option in the long run! – Matt Coughlin Feb 19 '13 at 17:29
  • Re-reading the post, one little detail that I miss the first time I read it. You talk about combining he gradients via opacity. This has the drawback that the opacity is a single value for all the overlay. Specifying the gradient as rgba gives you a better (but still limited) result, as you can change alpha at will. – vals Feb 21 '13 at 17:19
  • @vals: Agreed. Thanks for pointing that out. I updated the answer to mention rgba along with opacity. Haven't tested rgba yet, but I'm assuming that for an individual pixel, it makes no difference whether 0.5 opacity or 0.5 rgba transparency was used (for that one pixel). – Matt Coughlin Feb 21 '13 at 18:04
7

I have done a simple layout to demostrate this.

First, I will put 4 divs, the first to show the partial results, and the last to see the final result. The markup is just:

<div class="box mesh1"></div>
<div class="box mesh2"></div>
<div class="box mesh3"></div> 
<div class="box mesh"></div> 

here box is just for dimensions, mesh1 to 3 hold the partial results, in mesh we put it all together.

The CSS is:

.box {
    width: 400px;
    height: 150px;
    position: relative;
    display: inline-block;
}

.mesh1, .mesh {
background:
    -webkit-linear-gradient(5deg,  rgba(0, 250, 0, 0.5), rgba(0, 100, 0, 0.5))
}

.mesh:after, .mesh:before {
    content: "";
    position: absolute;
    left: 0px;
    bottom: 0px;
    top: 0px;
    right: 0px;
}
.mesh2, .mesh:after {
background:   -webkit-radial-gradient(center center, circle cover, rgba(250, 0, 0, 0.6) 0%, rgba(120, 0, 10, 0.5) 100%);}

.mesh3, .mesh:before {
background: -webkit-radial-gradient(10% 10%, ellipse cover, rgba(0, 0, 250, 0.6) 0%, white 100%);}

I am giving to the mesh1 class a background linear, inclinated 5 degrees, and specifying colors in rgba format to allow for transparency.

Then, to be able to overlay more gradients, I specify to pseudo elements as before and after, sharing the same layout properties.

To the after element I give a circular gradient, shared with the mesh2 To the before element I give an elliptical gradient, off center. All of them can be rgba.

At the end, you see in the mesh div the result of overlapping everything

(I have used everywhere the webkit notation to make it short)

I wouldn't say it is much artistic, but I leave this part to you :-)

fiddle here

vals
  • 61,425
  • 11
  • 89
  • 138
3

In my first answer, I interpreted this going more in the "artistic" way than in the "mathematical" way. The answer from Matt Coughlin make me think about a more mathematic answer, in wich we would make the "mesh" part of the requirement more rigorous.

That is, we have a matrix of colors, and we want to show this in a grid. If the grid has a spacing of say 100px, then the color [x][y] of the matrix would be given to the pixel in 100x and 100y. The pixels in between would be aproximated in a bilinear way.

For such an approach, the css would be:

.mesh { overflow: hidden; position: absolute;   width: 300px;   height: 300px;    }

.tile {    width: 200px;    height: 200px;    position: absolute;    display: block;   }

.tile11, .tile21, .tile31 {
left: -50px;
}
.tile12, .tile22, .tile32 {
left: 50px;
}
.tile13, .tile23, .tile33 {
left: 150px;
}
.tile11, .tile12, .tile13 {
top: -50px;
}
.tile21, .tile22, .tile23 {
    top: 50px;
}
.tile31, .tile32, .tile33 {
top: 150px;
}

.tile11 {
background: -webkit-radial-gradient(center center, 100px 100px, 
      rgba(255, 0, 0, 1) 0%, 
      rgba(255, 0, 0, 0.5) 50%,
      rgba(255, 0, 0, 0) 100%);}

.tile12 {
background: -webkit-radial-gradient(center center, 100px 100px, 
      rgba(255, 0, 0, 1) 0%, 
      rgba(255, 0, 0, 0.5) 50%,
      rgba(255, 0, 0, 0) 100%);}

I am using every div as a local contributor of the mesh, getting only to "touch" the inmediate neighbour, and going to a full transparency beyond that point.

The result is this :

fiddle

The 2 first colors are both red as a test. In a perfect system, the line connecting the 2 points should be perfect red all the time.

It's true that it's not a perfect result, but the "muddying" or "washyness" of the result is more or less avoided

vals
  • 61,425
  • 11
  • 89
  • 138
2

I wrote about a solution for creating lightweight, scalable mesh gradients using raster images: https://peterhrynkow.com/performance/2019/01/13/blowing-up-images-to-make-them-small.html

It doesn’t solve your problem of wanting to generate them randomly but at least gives you the small file size and resolution-independent scaling that you’d expect from a CSS or SVG solution.

peterhry
  • 1,120
  • 12
  • 16