1

Trying to follow the examples on this StackOverflow post but having trouble getting it to work with my code - hoping someone can show me what I am doing wrong....

Simply put, I just want a color code I can use to set a translucent DIV to a color that matches the image (in application this is random), then to set the font color to something that's easily readable against the color code, but that's a fight for another day...

MyWall = 'https://images.unsplash.com/photo-1537017469405-7faf1912af7c?ixid=MnwzMDUwMHwwfDF8cmFuZG9tfHx8fHx8fHx8MTY1Mzk5ODA1OQ&ixlib=rb-1.2.1'

function get_average_rgb(img) {
    var context = document.createElement('canvas').getContext('2d');
    if (typeof img == 'string') {
        var src = img;
        img = new Image;
        img.setAttribute('crossOrigin', ''); 
        img.src = src;
    }
    context.imageSmoothingEnabled = true;
    context.drawImage(img, 0, 0, 1, 1);
    return context.getImageData(1, 1, 1, 1).data.slice(0,3);
}


foo = get_average_rgb(MyWall)
Setting the BODY's background to the average color for an image URL provided:
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Chris Pare
  • 21
  • 5
  • That's getting the top-left pixel's value. Have you tried to get the average of more than one pixel? – Lee Taylor Jun 01 '22 at 00:23
  • 1
    (on/offtopic) should be `getImageData(0, 0, 1, 1)` [Read more: getImageData](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData) – Roko C. Buljan Jun 01 '22 at 06:55
  • @LeeTaylor - I guess I am not sure how to sample a sample of pixels, I would need a height/width to accomplish this - correct? – Chris Pare Jun 01 '22 at 20:21
  • I tried using the sample on the original post and was able to get my URL to convert to data, but when feeding it into the getAverageRGB it's giving me an error Uncaught (in promise) TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'. Updated Fiddle - https://jsfiddle.net/cpare/ejw8fy9n/51/ – Chris Pare Jun 01 '22 at 21:18
  • @ChrisPare See my answer. Let me know if you need further explanation. – Lee Taylor Jun 01 '22 at 21:53

1 Answers1

-1

OK, as the error pointed out, you needed to provide an img for the drawImage function.

So, in order to do that I've created a dynamic image, created an onload handler and then passed the loaded img to your routine. I don't know if the result is "correct", but the code now works.

function getAverageRGB(imgEl) {
  var blockSize = 5, // only visit every 5 pixels
    defaultRGB = {
      r: 0,
      g: 0,
      b: 0
    }, // for non-supporting envs
    canvas = document.createElement('canvas'),
    context = canvas.getContext && canvas.getContext('2d'),
    data, width, height,
    i = -4,
    length,
    rgb = {
      r: 0,
      g: 0,
      b: 0
    },
    count = 0;

  if (!context) {
    return defaultRGB;
  }
  height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
  width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;
  context.drawImage(imgEl, 0, 0);

  try {
    data = context.getImageData(0, 0, width, height);
  } catch (e) {
    /* security error, img on diff domain */
    alert('x');
    return defaultRGB;
  }
  length = data.data.length;
  while ((i += blockSize * 4) < length) {
    ++count;
    rgb.r += data.data[i];
    rgb.g += data.data[i + 1];
    rgb.b += data.data[i + 2];
  }
  // ~~ used to floor values
  rgb.r = ~~(rgb.r / count);
  rgb.g = ~~(rgb.g / count);
  rgb.b = ~~(rgb.b / count);

  return rgb;
}

(async function() {
  let blob = await fetch("https://images.unsplash.com/photo-1537017469405-7faf1912af7c?ixid=MnwzMDUwMHwwfDF8cmFuZG9tfHx8fHx8fHx8MTY1Mzk5ODA1OQ&ixlib=rb-1.2.1").then(r => r.blob());
  let dataUrl = await new Promise(resolve => 
  {
    let reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.readAsDataURL(blob);
  });

  // In order to use the data url, we need to create an img and wait for it to load
  let img = document.createElement('img');
  document.body.appendChild(img);
  img.onload = function(e) {
    var rgb = getAverageRGB(img);
    
    let rgbText = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
    document.body.style.backgroundColor = rgbText;
    document.querySelector("#rgb").innerText = rgbText;
  }
  img.src = dataUrl;

})();
#text, #rgb {
  color: white;
}

img {
  width: 600px;
}
<div id="text">Setting the BODY's background to the average color in the following image:</div>
<div id="rgb"></div>
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49