During my internship, I was tasked with creating a tooltip on hover for the company's website, displaying the alt-text of the image. To make this tooltip unique, a designer proposed the idea of dynamically changing the tooltip's background color based on the pixel color where the mouse hovers. This seemed like a nice challenge, so I started exploring options.
The website was written in the JavaScript-framework React.js combined with Next.js. The image source comes from the headless CMS Sanity.
The first challenge was to change the background color on hover position of the mouse over the image. This challenge can be broken down into two challenges:
However, both these challenges were easily solved as the examples found online were easy to understand and seemed to do the trick.
One of the hurdles I encountered fairly quickly was that when sizing a canvas with CSS, the visual size of the canvas changed, but the actual canvas didn't. This caused issues with the hover color extraction, resulting in the extraction of the wrong color. To fix this, I had to devise some funky formulas to resize the canvas, ensuring it scaled correctly with CSS sizing.
The function takes 4 parameters.
function setCanvasSize(
box: HTMLDivElement,
img: HTMLImageElement,
canvas: HTMLCanvasElement,
aspectRatio: number
) {
//Rest of function
}
1st step: retrieve the container dimensions.
const boxRect = box.getBoundingClientRect();
const boxHeight = boxRect.height;
const boxWidth = boxRect.width;
2nd step: I determine the aspect ratios.
const highestBoxValue = Math.max(boxHeight, boxWidth);
const lowestBoxValue = Math.min(boxHeight, boxWidth);
const boxAspectRatio = highestBoxValue / lowestBoxValue;
3rd Step: calculate the new canvas dimensions and assign them to the variables canvasWidth and canvasHeight.
// 3. Calculate Canvas Dimensions
let canvasWidth;
let canvasHeight;
// Conditions based on image dimensions and aspect ratio
if (img.width === img.height) {
// Equal width and height
canvasWidth = boxWidth >= boxHeight ? boxWidth : boxWidth * boxAspectRatio;
canvasHeight = boxHeight >= boxWidth ? boxHeight : boxHeight * boxAspectRatio;
} else if (img.width > img.height) {
// Landscape orientation
canvasWidth = boxWidth >= boxHeight ? boxWidth * aspectRatio : boxHeight * aspectRatio;
canvasHeight = boxWidth >= boxHeight ? boxWidth : boxHeight;
} else if (img.width < img.height) {
// Portrait orientation
canvasWidth = boxWidth >= boxHeight ? boxWidth : boxHeight;
canvasHeight = boxWidth >= boxHeight ? boxWidth * aspectRatio : boxHeight * aspectRatio;
}
4th step: Store the canvasWidth and canvasHeight followed by px in separate strings.
const pixelWidth = `${canvasWidth}px`;
const pixelHeight = `${canvasHeight}px`;
5th step: apply the canvas style dimensions.
canvas.style.width = pixelWidth;
canvas.style.height = pixelHeight;
And below the full setCanvasSize() function in TypeScript.
function setCanvasSize(
box: HTMLDivElement,
img: HTMLImageElement,
canvas: HTMLCanvasElement,
aspectRatio: number
) {
// 1. Retrieve Container Dimensions
const boxRect = box.getBoundingClientRect();
const boxHeight = boxRect.height;
const boxWidth = boxRect.width;
// 2. Determine Aspect Ratios
const highestBoxValue = Math.max(boxHeight, boxWidth);
const lowestBoxValue = Math.min(boxHeight, boxWidth);
const boxAspectRatio = highestBoxValue / lowestBoxValue;
// 3. Calculate Canvas Dimensions
let canvasWidth;
let canvasHeight;
// Conditions based on image dimensions and aspect ratio
if (img.width === img.height) {
// Equal width and height
canvasWidth = boxWidth >= boxHeight ? boxWidth : boxWidth * boxAspectRatio;
canvasHeight = boxHeight >= boxWidth ? boxHeight : boxHeight * boxAspectRatio;
} else if (img.width > img.height) {
// Landscape orientation
canvasWidth = boxWidth >= boxHeight ? boxWidth * aspectRatio : boxHeight * aspectRatio;
canvasHeight = boxWidth >= boxHeight ? boxWidth : boxHeight;
} else if (img.width < img.height) {
// Portrait orientation
canvasWidth = boxWidth >= boxHeight ? boxWidth : boxHeight;
canvasHeight = boxWidth >= boxHeight ? boxWidth * aspectRatio : boxHeight * aspectRatio;
}
// 4. Set Pixel Width and Height
const pixelWidth = `${canvasWidth}px`;
const pixelHeight = `${canvasHeight}px`;
// 5. Apply Styles to Canvas
canvas.style.width = pixelWidth;
canvas.style.height = pixelHeight;
// 6. Return Dimensions
return { canvasWidth, canvasHeight };
}