Touch to Zoom

Touch to Zoom
Photo by Nick Night / Unsplash

Working on a Linux t-shirts e-commerce, I added a simple "hover-to-zoom" feature on the main image displayed in each product's page (this one, for example): it is possible to just hover the mouse cursor to obtain a larger image, which moves accordingly to the mouse movements. Nothing new, here: it is a quite common behavior in many stores.

The problem arises on mobile, where no hover event is triggered by any mouse cursor. But after some experiment, I ended up to implement mostly the same effect using the pressed finger as a cursor: while you touch the image, a larger version is displayed (as when hovered); when you move the finger (keeping it touched), the alternative larger image moves accordingly.

Probably this is not the best solution possible, as it is barely discoverable by the user, but I like it and want to share here the proper code to reproduce.

In HTML:

<figure class="zoom" style="background-image: url(/images/larger.png)">
    <img class="shirt-cover" src="/images/normal.png">
</figure>

In SCSS:

figure.zoom {
    position: relative;
    overflow: hidden;
    cursor: zoom-in;

    img {
        transition: opacity 0.5s;
        display: block;
        width: 100%;

        &:hover {
            opacity: 0;
        }
    }
}

In JS:

/***********************************************************************
    Desktop
*/

$('.zoom').on('mousemove', (e) => {
    e.preventDefault();
    let zoomer = e.currentTarget;
    let offsetX = e.offsetX ? e.offsetX : e.touches[0].pageX;
    let offsetY = e.offsetY ? e.offsetY : e.touches[0].pageX;
    let x = offsetX / zoomer.offsetWidth * 100;
    let y = offsetY / zoomer.offsetHeight * 100;
    zoomer.style.backgroundPosition = x + '% ' + y + '%';
});

/***********************************************************************
    Mobile
*/

$('.zoom').on('touchmove', (e) => {
    e.preventDefault();
    let zoomer = e.currentTarget;
    let offsetX = e.offsetX ? e.offsetX : e.touches[0].pageX;
    let offsetY = e.offsetY ? e.offsetY : e.touches[0].pageY;
    let x = offsetX / zoomer.offsetWidth * 100;
    let y = offsetY / zoomer.offsetHeight * 100;
    zoomer.style.backgroundPosition = x + '% ' + y + '%';
});

$('.zoom').on('touchstart', (e) => {
    $(e.currentTarget).find('img').css('opacity', 0);
});

$('.zoom').on('touchend', (e) => {
    $(e.currentTarget).find('img').css('opacity', 1);
});

When on desktop, the "normal" image is properly hidden by the native :hover modifier in CSS and the mousemove JS event provides the figure background movements. When on mobile, the "normal" image is hidden or displayed due JS events of touch start and end and the rest of interaction happens on touchmove events.