Piddien
Piddien

Reputation: 1446

Custom gradient in javascript for canvas have some bugs

For a project I am attempting to make my own linear gradient code. And I have managed to come quite far, however, it is not perfect. Some places the gradient is not looking as it should.

See the following code where the colorStops are the stops in the gradient. It is supposed to show a gradient in between red, yellow and green as in a traffic light.

gradientSlider = {
      colorStops: ["#b30000", "#ffff1a", "#00e600"],
      minValue: 0,
      maxValue: 100,
      init: function(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext("2d");
        this.width = canvas.width;
        this.height = canvas.height;
        this.colors = this.calculateHexColorForStep();
        this.draw();
      },
      draw: function() {
        pixelWidth = this.width / this.maxValue;
        for (i = 0; i < this.maxValue; i++) {
          this.ctx.beginPath();
          this.ctx.rect(i * pixelWidth, 0, pixelWidth, this.height);
          this.ctx.fillStyle = this.colors[i];
          this.ctx.fill();
        }
      },
      calculateHexColorForStep: function() {
        result = [];
        stepsPerGradient = this.maxValue / (this.colorStops.length - 1);

        for (i = 0; i < this.colorStops.length - 1; i++) {
          percentIncrease = 100 / stepsPerGradient / 100;

          firstColor = this.colorStops[i];
          targetColor = this.colorStops[i + 1];

          firstColorDecArray = this.tools.parseColor(firstColor);
          targetColorDecArray = this.tools.parseColor(targetColor);

          for (j = 0; j <= stepsPerGradient; j++) {
            if (j == 0) {
              result.push(firstColor);
            } else if (j == stepsPerGradient) {
              result.push(targetColor);
            } else {
              stepColorDecArray = [firstColorDecArray[0] + (percentIncrease * j) * (targetColorDecArray[0] - firstColorDecArray[0]),
                firstColorDecArray[1] + (percentIncrease * j) * (targetColorDecArray[1] - firstColorDecArray[1]),
                firstColorDecArray[2] + (percentIncrease * j) * (targetColorDecArray[2] - firstColorDecArray[2])
              ];
              result.push(this.tools.decimalToHex(stepColorDecArray));
            }
          }
        }

        return result;
      },
      tools: {
        parseColor: function(hexColorString) {
          var m;
          m = hexColorString.match(/^#([0-9a-f]{6})$/i)[1];
          if (m) {
            return [parseInt(m.substring(0, 2), 16), parseInt(m.substring(2, 4), 16), parseInt(m.substring(4, 6), 16)];
          }
        },
        decimalToHex: function(decimalNumberArray) {
          //TODO fikse tall under ti - alltid to plasser
          var results = [];

          results[1] = Math.round(decimalNumberArray[0]).toString(16);
          results[2] = Math.round(decimalNumberArray[1]).toString(16);
          results[3] = Math.round(decimalNumberArray[2]).toString(16);

          for (var i = 1; i <= results.length; i++) {
            if (!(isNaN(results[i]))) {
              if (results[i] < 10) {
                results[i] = "0" + results[i];
              }
            }
          }

          return "#" + results[1] + results[2] + results[3];
        }
      }
    }

		//get the canvas element
		var canvasElm = document.querySelector("canvas");
		//initialize the slider
		gradientSlider.init(canvasElm);
<canvas id="gradient-slider" width="600" height="200" class="gradientslider"></canvas>

Upvotes: 0

Views: 116

Answers (1)

eoma
eoma

Reputation: 36

My solution is a fixed version of the function tools.decimalToHex. The error was that you treat the results array as an array of numbers, instead of an array of strings.

    decimalToHex : function(decimalNumberArray){
        var results = [];

        // Maybe check if number is in range 0 - 255, before converting to string?
        results[0] = Math.round(decimalNumberArray[0]).toString(16);
        results[1] = Math.round(decimalNumberArray[1]).toString(16);
        results[2] = Math.round(decimalNumberArray[2]).toString(16);

        for (var i = 0; i<results.length; i++) {
                if(results[i].length < 2) {
                    results[i] = "0" + results[i];
                }
        }

        return "#" + results[0] + results[1] + results[2];
    }

Corresponding jsFiddle: https://jsfiddle.net/8xr4zujj/4/

Upvotes: 2

Related Questions