Search code examples
javascriptcsscanvashtml5-canvas

Mapping canvas LinearGradient to an image


function show(d) {

   function  normalize(val, max, min) { return (val - min) / (max - min); } 

   var canvas = document.createElement("canvas")
   var ctx = canvas.getContext('2d')

   let width = canvas.width = 400;
   let height = canvas.height = 400;
   let arr = d //<-- 1D array of noise data, type: float

   let gradient = ctx.createLinearGradient(25, 25, width  - 50, 25);
   gradient.addColorStop(0,   "black");
   gradient.addColorStop(0.5, "red");
   gradient.addColorStop(1,   "lime");

   ctx.fillStyle = gradient;
   ctx.fillRect(25, 25, width  - 50, 25);

     function generate(end=false) {
       let w = 50, h = 50;
       let startx = (width/2) - (w/2);
       let starty = 85;
       ctx.clearRect(startx, starty, w, h);
       ctx.strokeRect(startx, starty, w, h);
       if (end) return;
       for (let i = 0; i < w; i++)
         for (let j = 0; j < h; j++) {
           let x = i + startx;
           let y = j + starty;
           let index = i + j * w;
           let val = arr[index]
           let color = colorAt(val).join(", ")
           ctx.fillStyle = `rgba(${color})`;
           ctx.fillRect(x, y, 1, 1);
         }
     }

     generate()

     function colorAt(t) {
       t = 25 + (350 * t);
       let pixel = ctx.getImageData(t, 35, 1, 1).data;
       return [...pixel];
     }

     canvas.id     = "CursorLayer";
     canvas.style.width = '600px'
     canvas.style.height = '600px'
     canvas.style.zIndex   = 8;
     canvas.style.position = "absolute";
     canvas.style.border   = "1px solid";
     document.body.appendChild(canvas);
 }

/* create noise */
let array = []; // <-- this array is allways going to have a length of 2500
function populate(w, h) {
  for (let i = 0; i < w * h; i++)
    array[i] = Math.random();
}
 populate(50, 50) 
 show(array)

I have mapped my data to CanvasRenderingContext2D.createLinearGradient using this code above. arr is the data and it's a 1D array of float values of length 2500. The length is static I don't want to add more data to it

I'm currently able to output this image: gradientImage

How do I get rid of the gradient bar and make my desired image larger? without adding more data to the array


Solution

  • A simplistic way of getting rid of the gradient bar across the top once it is no longer needed is to overwrite it with white when you have finished using it.

    The size of the final image is set with w and h variables and this snippet increases them from 50 to 300 so the image is still within the canvas.

    Here's your code as a runnable snippet. As I do not know how you set up arr I have removed use of that in comments and just used a color.

    function show(d) {
    
      function normalize(val, max, min) {
        return (val - min) / (max - min);
      }
    
      var canvas = document.createElement("canvas")
      var ctx = canvas.getContext('2d')
    
      let width = canvas.width = 400;
      let height = canvas.height = 400;
      //let arr = d.attributes.position //<-- 1D array of noise data, type: float
    
      let gradient = ctx.createLinearGradient(25, 25, width - 50, 25);
      gradient.addColorStop(0, "black");
      gradient.addColorStop(0.5, "red");
      gradient.addColorStop(.8, "lime");
    
      ctx.fillStyle = gradient;
      ctx.fillRect(25, 25, width - 50, 25);
    
      function generate(end = false) {
        //w and h made bigger
        let w = 300,
          h = 300;
        let startx = (width / 2) - (w / 2);
        let starty = 85;
        ctx.clearRect(startx, starty, w, h);
        ctx.strokeRect(startx, starty, w, h);
        if (end) return;
        for (let i = 0; i < w; i++)
          for (let j = 0; j < h; j++) {
            let x = i + startx;
            let y = j + starty;
            let index = i + j * w;
            //let val =  normalize(arr[index],255,0)
            //let color = colorAt(val).join(", ")
            ctx.fillStyle = `rgba(255,0,,0,1)`;
            ctx.fillRect(x, y, 1, 1);
          }
      }
    
      generate()
    
      function colorAt(t) {
        t = 25 + (350 * t);
        let pixel = ctx.getImageData(t, 35, 1, 1).data;
        return [...pixel];
      }
    
      canvas.id = "CursorLayer";
      canvas.style.width = '600px'
      canvas.style.height = '600px'
      canvas.style.zIndex = 8;
      canvas.style.position = "absolute";
      canvas.style.border = "1px solid";
    
      //ADDED   
      ctx.fillStyle = 'white';
      ctx.fillRect(25, 25, width - 50, 25);
    
      //END OF ADDED
      document.body.appendChild(canvas);
    }
    show();

    If this were mine I would probably want two canvases. One just temporary which will hold the gradient so you can look up the color at a particular position and the other where the final image will be painted and I'd make it fill the whole of that canvas.