Creating an image on hover color-picker

Prototype

Image of trees.

Assignment context

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.

Technologies

The website was written in the JavaScript-framework React.js combined with Next.js. The image source comes from the headless CMS Sanity.

Challenges

Challenge 1: Dynamic changing of background color on hover

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:

  1. Extract the color of the pixel the mouse hovers over.
  2. Use this color to change the background color of the tooltip.

However, both these challenges were easily solved as the examples found online were easy to understand and seemed to do the trick.

Challenge 2: Dynamic sizing

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.

How the canvas scaling function works:

The function takes 4 parameters.

  1. box: The container of the canvas element.
  2. img: The image element to be rendered in the canvas.
  3. canvas: The canvas element.
  4. aspectRatio: the aspect ratio of the canvas.
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 };
}