Using the Color Thief javascript effect, I have written some code that grabs an images dominant color and adjusts the entire theme's color scheme based on the image.
This all works well on a single product page, where only one image is being used. On my catalog page, there are multiple images that I need to grab the dominant color from. One image for each product. I need to display each separate color along with the product.
You can see the colored border
in each product's panel (it's a brown/orange color).
My very slimmed-down code I'm using is below:
jQuery( document ).ready( function( $ ) {
var image = new Image;
var bg;
$('.post-image-hidden-container').each(function() {
bg = $(this).text();
image.onload = function() {
var colorThief = new ColorThief();
var dominantColor = colorThief.getColor(image);
var colorPalette = colorThief.getPalette(image, 7);
var backgroundColor = 'rgb('+ dominantColor +')';
/* Calculate the Lightest Color in the Palette */
var lightestColor = colorPalette.reduce(function(previousValue, currentValue) {
var currLightNess = (0.2126*currentValue[0] + 0.7152*currentValue[1] + 0.0722*currentValue[2]);
var prevLightNess = (0.2126*previousValue[0] + 0.7152*previousValue[1] + 0.0722*previousValue[2]);
return (prevLightNess < currLightNess) ? currentValue : previousValue;
});
/* Calculate the Darkest Color in the Palette */
var darkestColor = colorPalette.reduce(function(previousValue, currentValue) {
var currLightNess = (0.2126*currentValue[0] + 0.7152*currentValue[1] + 0.0722*currentValue[2]);
var prevLightNess = (0.2126*previousValue[0] + 0.7152*previousValue[1] + 0.0722*previousValue[2]);
return (prevLightNess > currLightNess) ? currentValue : previousValue;
});
/* Create Shades and Tints of Lightest Color */
...
/* Shades (darker) */
...
/* Tints (lighter) */
...
/* Handle all CSS based on returned colors */
$('.product-bottom-info-container').each(function() {
$(this).css({
borderTop: '3px solid rgb('+ lightestColor +')'
});
});
}
image.src = bg;
});
});
Right after declaring the variable bg
I wrapped the entire thing in an each()
loop. Down towards the bottom, .product-bottom-info-container
is the element where the colored border appears. I cannot seem to get each product's border to be it's own color. It keeps giving every border the LAST color in the loop.
Some notes:
.post-image-hidden-container
is a hidden div above each product that contains the product's image url..product-bottom-info-container
is the container at the bottom of each product with the product title and the colored border.Am I using the each()
function properly? What am I doing wrong?
Thanks
UPDATE I was able to grab all of the RGB values for each image and put them into an array:
var thisColor;
var theseColors = [];
$('.shop-page-item-thumb').each(function() {
$(this).find('img').each(function() {
thisColor = colorThief.getColor(this);
theseColors.push(thisColor);
});
});
Now that I have all RGB's available, is there a way to simply loop through this array and assign each value to its respective .product-bottom-info-container element?
theseColors[0]
is the first RGB, theseColors[1]
is the second, etc. all the way up to theseColors[11]
.
If I run console.log(thisColor)
inside of the loop, I get these results:
[29, 28, 22]
[217, 195, 189]
[14, 14, 8]
[233, 232, 206]
[31, 31, 31]
[82, 97, 111]
[60, 68, 84]
[34, 29, 30]
[17, 30, 37]
[12, 11, 12]
[56, 43, 26]
[209, 150, 108]
The 12 RGB values that I need. So we're headed in the right direction.
UPDATE for Mottie
Here is the HTML structure for one of the products. .shop-page-item-thumb
is the container that holds the thumbnail, but .shop-page-item-article
is the parent (besides the actual li
list item).
FINAL UPDATE (Thanks Mottie!) This is the piece of code that finally worked:
$('.shop-page-item-thumb').each(function() {
var thumb = $(this);
thumb.find('img').each(function() {
thisColor = colorThief.getColor(this);
thumb.parent().find('.product-bottom-info-container').css({
borderTop: '3px solid rgb('+ thisColor +')'
})
});
});
Much love, Stack Overflow! <3
It looks like this code is looping through each container after each image load...
/* Handle all CSS based on returned colors */
$('.product-bottom-info-container').each(function() {
$(this).css({
borderTop: '3px solid rgb('+ lightestColor +')'
});
});
try this instead:
$(this).closest('.product-bottom-info-container').css({
borderTop: '3px solid rgb('+ lightestColor +')'
});
Update: Oh sorry, I didn't look at the code that closely... another problem is the image
definition. There is only one, not one for each container... Instead of defining it outside the loop, find it inside of the .each
loop and then attach an onload...
$(this).find('img')[0].onload = function() {
It shouldn't change the above code that adds the border color since this
would refer to the image inside of the onload
function.
It would have been easier to troubleshoot the problem if you would have provided a ready-made demo to use.
Update2: Instead of pushing the colors to an array, apply them directly to the border; I have no idea about the thumb images in relation to the containers, so we're just going to assume that the thumbs are in the same order as the containers. Some HTML would have helped. Updated for the given HTML...
$('.shop-page-item-thumb').each(function() {
var thumb = $(this);
thumb.find('img').each(function() {
thisColor = colorThief.getColor(this);
// prev() targets the previous element (it should be
// the 'product-bottom-info-container'; or use
// thumb.parent().find('.product-bottom-info-container')
thumb.prev().css({
borderTop: '3px solid rgb('+ thisColor +')'
});
});
});