Ben Blank
Ben Blank

Reputation: 56572

Can a two-dimensional gradient be created (or faked) in SVG?

I'm trying to create a color-picker which must dynamically generate its images and figured it would be a whole lot easier to generate SVG than a raster image. Unfortunately, I can't figure out how to represent the big, two-dimensional gradient which will form the centerpiece of the picker.

For example, if the currently selected axes are blue and green, I need to paint a square which has the lower-left corner black, the upper-left blue, the lower-right green, and the upper-right cyan.

If there's a way to accomplish this by overlaying two linearGradient-filled squares and playing with their opacity, I wasn't able to work it out. I also tried creating a gradient whose start end end colors were other gradients (hoping I was being clever), but all that got me was a "big black nothing". Google searches have thus far gotten me nowhere.

I'd hate to resort to a stack of 256 1-pixel high gradients, both because of the increase in size and complexity and because I suspect it wouldn't resize well. Perhaps someone with a bit more working knowledge of SVG can suggest something

Upvotes: 5

Views: 1066

Answers (2)

Florian P
Florian P

Reputation: 51

An alternative approach to Paul Wheelers "mix-blend-mode" style, would be to use the stop-opacity attribute. The typical chrome color picker for example, can be recreated in by adding a horizontal gradient in the background going from white to your selected color and a vertical gradient holding the color black but increasing the opacity to 1 starting from 0.

In your case you could rotate the gradients and offset it to achieve a the desired result.

Example for the standard picker via svg (white to green):

<svg height="120" width="230">
    <defs>
        <linearGradient id="white-to-color" x1="0%" y1="0%" x2="100%" y2="0%">
            <stop offset="0" stop-color="#ffffff" stop-opacity="1" />
            <stop offset="100%" stop-color="#00ff00" stop-opacity="1" />
        </linearGradient>

        <linearGradient id="black" x1="0%" y1="0%" x2="0" y2="100%">
            <stop offset="0" stop-color="#000000" stop-opacity="0" />
            <stop offset="100%" stop-color="#000000" stop-opacity="1" />
        </linearGradient>
    </defs>

    <rect x="0" y="0" width="230" height="120" fill="url('#white-to-color')" />
    <rect x="0" y="0" width="230" height="120" fill="url('#black')" />
</svg>

Upvotes: 2

Paul Wheeler
Paul Wheeler

Reputation: 20160

This is possible using the mix-blend-mode style attribute:

<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256">
  <defs>
    <linearGradient id="black-to-green" gradientTransform="rotate(0)">
      <stop offset="0"  stop-color="#000000" />
      <stop offset="100%" stop-color="#00ff00" />
    </linearGradient>
    <linearGradient id="black-to-blue" gradientTransform="rotate(90)">
      <stop offset="0"  stop-color="#0000ff" />
      <stop offset="100%" stop-color="#000000" />
    </linearGradient>
  </defs>
  <rect x="0" y="0" width="256" height="256" fill="url('#black-to-green')" />
  <rect x="0" y="0" width="256" height="256" fill="url('#black-to-blue')" style="mix-blend-mode: screen" />
</svg>

Documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode

Upvotes: 5

Related Questions