skip to Main Content

Why my handleMouseMove function is not working properly? I am trying to add zoom on hovering of image. It seems calculation is wrong, I need help how it is working, how to write it correctly. Trying to get the same effect as in amazon when you hover product , it show you little bit closer product. In my case it doesnt works. Please help, thank you

import React, { useState } from 'react';

const Index = () => {
  const [hero, setHero] = useState('/img.jpg');

  const handleImageClick = (src) => {
    setHero(src);
    const magnifier = document.getElementById('magnifier');
    magnifier.style.backgroundImage = `url(${src})`;
  };

  const handleMouseMove = (e) => {
    const magnifier = document.getElementById('magnifier');
    const detailImage = document.getElementById('detailImage');
    const rect = detailImage.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    const offsetX = magnifier.offsetWidth / 2;
    const offsetY = magnifier.offsetHeight / 2;

    magnifier.style.backgroundPosition = `-${(x * 2) - offsetX}px -${(y * 2) - offsetY}px`;
    magnifier.style.left = `${x - offsetX}px`;
    magnifier.style.top = `${y - offsetY}px`;
  };

  return (
    <>
      
          <div className="col-11 col-md-4 bg-light border p-3 rounded-3">
            <div className="zoom-container">
              <div
                id="detailImage"
                className="detail-image"
                onMouseMove={handleMouseMove}

                onMouseLeave={() => {
                  const magnifier = document.getElementById('magnifier');
                  magnifier.style.display = 'none';
                }}

                onMouseEnter={() => {
                  const magnifier = document.getElementById('magnifier');
                  magnifier.style.display = 'block';
                }}
              >
                <img
                  id="mainImage"
                  src={hero}
                  className="border zoom-image"
                  alt="card"
                />
                <div id="magnifier" className="magnifier"></div>
              </div>
            </div>
            <div className="mt-3 d-flex justify-content-between">
              <img
                src="/img.jpg"
                className="border"
                style={{ position: 'relative', width: '31%' }}
                alt="card1"
                onClick={() => handleImageClick('/img.jpg')}
              />
              <img
                src="/img.jpg"
                className="border"
                style={{ position: 'relative', width: '31%' }}
                alt="card2"
                onClick={() => handleImageClick('/img1.jpg')}
              />
              <img
                src="/img.jpg"
                className="border"
                style={{ position: 'relative', width: '31%' }}
                alt="card3"
                onClick={() => handleImageClick('/img2.jpg')}
              />
            </div>
          </div>
          
      </div>

      <style>{`
        .zoom-container {
          position: relative;
          width: 100%;
        }

        .detail-image {
          position: relative;
        }

        .zoom-image {
          width: 100%;
        }

        .magnifier {
          position: absolute;
          border: 3px solid #000;
          border-radius: 50%;
          cursor: none;
          width: 100px;
          height: 100px;
          display: none;
          background-repeat: no-repeat;
          background-size: 200%;
        }
      `}</style>
    </>
  );
};

export default Index;

2

Answers


  1. Change backgroundPosition calculation to:

    magnifier.style.backgroundPosition = `-${((x / detailImage.offsetWidth) * 100)}% -${((y / detailImage.offsetHeight) * 100)}%`;
    
    Login or Signup to reply.
  2. First, I multiply the actual image width by a scaler to get the zoomed image width. Then, its position must be scaler times negative of actual coordinates plus half size of the magnifier circle.

    I also check a few conditions to overcome a kind of overflow problem. With this block, the magnifying circle will always be inside the container unless it is very large.

    const handleMouseMove = (e) => {
        const magnifier = document.getElementById('magnifier');
        const detailImage = document.getElementById('detailImage');
        const rect = detailImage.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        const offsetX = magnifier.offsetWidth / 2;
        const offsetY = magnifier.offsetHeight / 2;
    
        magnifier.style.left = `${x - offsetX}px`;
        magnifier.style.top = `${y - offsetY}px`;
    
        const scaler = 3;
    
        magnifier.style.backgroundSize = `${rect.width * scaler}px`;
        magnifier.style.backgroundPositionX = `${-1 * scaler * x + offsetX}px`;
        magnifier.style.backgroundPositionY = `${-1 * scaler * y + offsetY}px`;
    
        const positionX = x < offsetX*2 || rect.width - x >= offsetX*2 ? 1 : -1;
        const positionY = y < offsetY*2 || rect.height - y >= offsetY*2 ? 1 : -1;
        magnifier.style.translate = `${50 * positionX}% ${50 * positionY}%`;
        
        magnifier.style.backgroundColor = 'red';
    };
    
    

    Finally, I recommend you not to hide cursor but show it like this.

    .zoom-container {
        ...
        cursor: crosshair;
    }
    .magnifier {
        ...
        /*cursor: none;*/
    }
    

    Demo

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search