Search code examples
javascripthtmljquerycsscss-transforms

Panzoom to element on click


I want the map of Disneyland to pan/zoom to the yellow square when you click on it. I'm new to web dev so any pointers on how to implement this with CSS/JavaScript/jQuery would be hugely appreciated.

Edit: I replaced my example with something closer to the actual app to avoid confusion.

Codepen: https://codepen.io/dan-freeman/pen/mdVWYox

HTML

<img src="https://i.redd.it/64xwakxpbae21.jpg">
<div class="pin">Click me!</div>-

CSS

img {
    width: 800px;
    height: auto;
}

.pin {
    position: absolute;
    height: 75px;
    width: 75px;
    top: 300px; /*Solution should use these coordinates*/
    left: 550px;
    background-color: yellow;
}

Before Click enter image description here After Click enter image description here


Solution

  • I created an example for your case. This literally solves your problem. You can give the scale size what you want in .zoom class. I gave it 1.5.

    const pins = document.querySelectorAll(".pin");
    const figure = document.querySelector(".figure");
    const mapImage = document.querySelector("#mapImage");
    
    pins.forEach(p => {
      p.addEventListener("click", e => {
    
        figure.style.transformOrigin = `${e.target.getBoundingClientRect().left - e.target.getBoundingClientRect().width / 2}px ${e.target.getBoundingClientRect().top - e.target.getBoundingClientRect().height / 2}px`;
    
    
        if (figure.classList.contains("zoom")) {
          figure.style.transition = "transform-origin 0.3s, transform 0.3s";
        } else {
          figure.style.transition = "transform 0.3s";
        }
    
        figure.classList.add("zoom");
      });
    });
    
    document.addEventListener("click", (e) => {
      pins.forEach((p) => {
        let isClickInside = p.contains(event.target);
        if (!isClickInside &&
          !event.target.classList.contains("pin")
        ) {
          figure.style.transition = "transform-origin 0.3s, transform 0.3s";
          figure.style.transformOrigin = '50% 50%';
          figure.classList.remove("zoom");
        }
      });
    });
    .container {
      width: 800px;
      overflow: hidden;
    }
    
    figure {
      width: 100%;
      position: relative;
      transform-origin: 50% 50%;
    }
    
    figure img {
      width: 100%;
    }
    
    .pin {
      position: absolute;
      height: 75px;
      width: 75px;
      background-color: yellow;
      transition: 0.3s;
      transform-origin: 50% 50%;
    }
    
    .pin1 {
      top: 300px;
      left: 550px;
    }
    
    .pin2 {
      top: 100px;
      left: 250px;
    }
    
    .zoom {
      transform: scale(1.5);
    }
    <div class="container">
      <figure class="figure">
        <img id="mapImage" src="https://i.redd.it/64xwakxpbae21.jpg">
    
        <div class="pin pin1">Click me!</div>
        <div class="pin pin2">Another Pin</div>
      </figure>
    </div>

    Another one:

    const pins = document.querySelectorAll(".pin");
    const mapImage = document.querySelector("#mapImage");
    
    pins.forEach(p => {
      p.addEventListener("click", e => {
    
        pins.forEach(pin => {
          pin.classList.remove("pinZoom");
        });
    
        mapImage.style.transformOrigin = `${e.target.getBoundingClientRect().left - e.target.getBoundingClientRect().width / 2}px ${e.target.getBoundingClientRect().top - e.target.getBoundingClientRect().height / 2}px`;
    
    
        if (mapImage.classList.contains("zoom")) {
          mapImage.style.transition = "transform-origin 0.3s, transform 0.3s";
        } else {
          mapImage.style.transition = "transform 0.3s";
        }
    
    
        mapImage.classList.add("zoom");
        p.classList.add("pinZoom");
    
      });
    });
    
    document.addEventListener("click", (e) => {
      pins.forEach((p) => {
        let isClickInside = p.contains(event.target);
        if (!isClickInside &&
          !event.target.classList.contains("pin")
        ) {
          mapImage.style.transformOrigin = '50% 50%';
          mapImage.classList.remove("zoom");
          p.classList.remove("pinZoom");
        }
      });
    });
    figure{
      position: relative;
      width: 800px;
      height: auto;
      overflow: hidden;
    }
    
    figure img {
      width: 100%;
      transform-origin: 50% 50%;
    }
    
    .pin {
      position: absolute;
      height: 75px;
      width: 75px;
      background-color: yellow;
      transition: 0.3s;
      transform-origin: 50% 50%;
    }
    
    .pin1 {
      top: 300px;
      left: 550px;
    }
    
    .pin2 {
      top: 100px;
      left: 250px;
    }
    
    .zoom {
      transform: scale(1.5);
    }
    
    .pinZoom {
      transform: scale(1.3);
    }
    <figure>
      <img id="mapImage" src="https://i.redd.it/64xwakxpbae21.jpg">
    </figure>
    
    <div class="pin pin1">Click me!</div>
    <div class="pin pin2">Another Pin</div>

    I created another example also with js-image-zoom library and no click needed. You can set scale, zoom position, offset, zoom width and zoom style. See documentation.

    var options = {
      width: 800,
      zoomWidth: 800,
      /*
      offset: {
        vertical: 0,
        horizontal: 10
      },
      scale: 2.0,
      */
      zoomPosition: 'original', /* right, top, bottom, left */
    };
    
    new ImageZoom(document.getElementById("img-container"), options);
    .pin {
      position: absolute;
      height: 75px;
      width: 75px;
      top: 300px;
      left: 550px;
      background-color: yellow;
    }
    
    #img-container {
      position: relative;
    }
    <div id="img-container" style="width: 400px">
      <img src="https://i.redd.it/64xwakxpbae21.jpg" />
    <div>
    
    <script src="https://unpkg.com/js-image-zoom@0.7.0/js-image-zoom.js"></script>