Svg.js: .get() function and picking colors in Gradient

Created on 29 Nov 2016  路  10Comments  路  Source: svgdotjs/svg.js

The .get() function doesn't seem to be working. It would also be nice to be able to grab the color from a specific position of a gradient.

All 10 comments

Code? Error messages? Anything?

@medeor Can you explain what you're trying to do in more detail or provide some code that illustrate your issue?

If not, then I'm gonna close this in a few days.

First I created a gradient as a legend for my scatter plot using this code.

 var gradient = legend.gradient('linear', function(stop) {
  stop.at(0, '#799abc')
  stop.at(0.5, '#ccc')
  stop.at(1, '#e88081')
});

And then I wanted to paint the bubbles in the scatter plot normalizing their values and comparing them to the gradient scale. So for instance, if the value is 0 then the bubble should be '#799abc'. I thought the .get() function would be appropriate for this but then I found out that it's not used for such practical purposes. Then I found a workaround for that.

function GradientReader(colorStops) {

        var canvas = document.createElement('canvas'),     // create canvas element
            ctx = canvas.getContext('2d'),                 // get context
            gr = ctx.createLinearGradient(0, 0, 101, 0),   // create a gradient
            i = 0, cs;

        canvas.width = 101;                                // 101 pixels incl.
        canvas.height = 1;                                 // as the gradient

        for(; cs = colorStops[i++];)                       // add color stops
            gr.addColorStop(cs.stop, cs.color);

        ctx.fillStyle = gr;                                // set as fill style
        ctx.fillRect(0, 0, 101, 1);                        // draw a single line

        // method to get color of gradient at % position [0, 100]
        this.getColor = function(pst) {
            return ctx.getImageData(pst|0, 0, 1, 1).data;
        };

    }

 var gr = new GradientReader([{stop: 0.0, color: '#799abc'},
                             {stop: 0.5, color: '#ccc'},
                             {stop: 1.0, color: '#e88081'}]);

                    var col = gr.getColor(100/(colorData[colorData.length-1]/allData[j][dataForCircleColor]));

The workaround worked but I still think it would be awesome to be able to pick a specific color from a gradient giving a value from 0 to 1.

You can achieve this by using the SVG.Color class:

var blend = new SVG.Color('#799abc').morph('#ccc')
blend.at(0.5)

http://svgjs.com/classes/color/#at

You'll have to find the two stops in a gradient at both sides of a given offset. Something like:

var first, offset = 0.4

gradient.each(function() {
  if (this.attr('offset') < offset)
    first = this
})

Then you can get the colors and make the blend:

var blend = new SVG.Color(first.attr('stop-color')).morph(first.next().attr('stop-color'))

Finally, you'll have to calculate the value for blend.at() in relation tot the total offset of the gradient (0 to 1).

And to make things really convenient, you can make a small plugin by extending SVG.Gradient:

SVG.extend(SVG.Gradient, {
  // Get color at given offset
  colorAt: function(offset) {
    var first, last, blend

    // find stops
    this.each(function() {
      if (this.attr('offset') < offset)
        first = this
    })

    last = first.next()

    // create morph
    blend = new SVG.Color(first.attr('stop-color'))
    blend.morph(last.attr('stop-color'))

    // .. calculate relative offset

    return blend.at( /* calculated offset */ )
  }

})

So you can do:

var color = gradient.colorAt(0.4)

Actually that makes an awesome feature or at least a nice plugin :)

That's what I was looking for and and glad to know that it's supported. Thanks!

Great. Please consider creating a plugin or at least posting the final code so other people can enjoy this goodness :)

I guess the formular would be

relativeOffset = (offset - first.attr('offset')) / (second.attr('offset') - first.attr('offset'))

Thanks @Fuzzyma. So that would result in:

SVG.extend(SVG.Gradient, {
  // Get color at given offset
  colorAt: function(offset) {
    var first, last

    // find stops
    this.each(function() {
      if (this.attr('offset') <= offset) first = this
    })

    last = first.next()

    // if first is the last stop, return color
    if ( ! last ) return first.attr('stop-color')

    var blend, relative, fo, lo

    // create morph
    blend = new SVG.Color(first.attr('stop-color'))
    blend.morph(last.attr('stop-color'))

    // calculate relative offset
    fo = first.attr('offset')
    lo = last.attr('offset')
    relative = (offset - fo) / (lo - fo)

    return blend.at( relative )
  }

})

https://jsfiddle.net/wout/sn7um5fr/

The plugin is now available at bower and npm.
Just run bower/npm install svg.colorat.js

Was this page helpful?
0 / 5 - 0 ratings