Search code examples
javascriptcolorslegendlinear-gradientsdiscrete

Convert continuous colors(legend) to color as stepwise constant (discrete colors) in linear gradient


I am creating a continuous legend (have created using linear gradient). Now I want to convert same legend to look like discrete legend (have constant stepwise values). I have shared the images like how it looks now and how I want it to be looked and also the code snippet along with fiddle link

const legendColor = [{
    offset: 0.0,
    color: "#ff0000"
  },
  {
    offset: 0.2,
    color: "#ffff00"
  },
  {
    offset: 0.4,
    color: "#00ff00"
  },
  {
    offset: 0.6,
    color: "#00ffff"
  },
  {
    offset: 0.8,
    color: "#0000ff"
  },
  {
    offset: 1.0,
    color: "#ff00ff"
  }
];

const svg = d3.select("svg");
const colorScale2 = d3.scaleLinear().domain([0, 1]).range([0, 400]);
const id = "linear-gradient-0";
const linearGradient2 = svg.append("defs")
  .append("linearGradient")
  .attr("id", "linear-gradient-1")
  .attr("x1", "100%")
  .attr("x2", "0%")
  .attr("y1", "0%")
  .attr("y2", "0%");

// append the color
linearGradient2
  .selectAll("stop")
  .data(legendColor)
  .enter()
  .append("stop")
  .attr("offset", function(data) {
    return colorScale2(data.offset) / 4 + "%";
    //return data.offset + "%";
  })
  .attr("stop-color", function(data) {
    return data.color;
  });

// draw the rectangle and fill with gradient
svg.append("rect")
  .attr("x", 10)
  .attr("y", 88)
  .attr("width", 400)
  .attr("height", 20)
  .style("fill", "url(#linear-gradient-1)");

Fiddle link : https://jsfiddle.net/p8mukjz9/2/

How I want it to be looked :

enter image description here


Solution

  • It's possible to change only the legendColor array the way that each color will appear twice. Each repeated color item should have an offset that is "close enough" to the next color like in example below.

    You can play with this array and make the colors in any order (so red will be displayed first from the left).

    const legendColor = [{
        offset: 0.0,
        color: "#ff0000"
    },
    {
        offset: 0.18,
        color: "#ff0000"
    },
    {
        offset: 0.18,
        color: "#ffff00"
    },
    {
        offset: 0.34,
        color: "#ffff00"
    },
    {
        offset: 0.34,
        color: "#00ff00"
    },
    {
        offset: 0.5,
        color: "#00ff00"
    },
    {
        offset: 0.5,
        color: "#00ffff"
    },
    {
        offset: 0.66,
        color: "#00ffff"
    },
    {
        offset: 0.66,
        color: "#0000ff"
    },
    {
        offset: 0.83,
        color: "#0000ff"
    },
    {
        offset: 0.83,
        color: "#ff00ff"
    },
    {
        offset: 1.0,
        color: "#ff00ff"
    }
    ];
    
    const svg = d3.select("svg");
    const colorScale2 = d3.scaleLinear().domain([0, 1]).range([0, 400]);
    const id = "linear-gradient-0";
    const linearGradient2 = svg.append("defs")
      .append("linearGradient")
      .attr("id", "linear-gradient-1")
      .attr("x1", "100%")
      .attr("x2", "0%")
      .attr("y1", "0%")
      .attr("y2", "0%");
    
    // append the color
    linearGradient2
      .selectAll("stop")
      .data(legendColor)
      .enter()
      .append("stop")
      .attr("offset", function(data) {
        return colorScale2(data.offset) / 4 + "%";
        //return data.offset + "%";
      })
      .attr("stop-color", function(data) {
        return data.color;
      });
    
    // draw the rectangle and fill with gradient
    svg.append("rect")
      .attr("x", 10)
      .attr("y", 88)
      .attr("width", 400)
      .attr("height", 20)
      .style("fill", "url(#linear-gradient-1)");
    
    // create tick
    svg.append("g").attr("transform", "translate(10,115)").call(d3.axisBottom(colorScale2));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.4.0/d3.min.js"></script>
    <svg width="500"></svg>