Button to toggle Zoom (Pinch-zoom in/out)

In what follows is an attempt to have a button that toggles zoom in and out at the back of my card. At the back field of my card is a scroll captured image that mimics the view of a pdf in addition to some text.

I had hoped that when the button is clicked on, the middle of my current screen (which has the said image) will be zoomed in and out on. Instead the zoom happens on at a different part of the image. Also, when the zoom/scale(2) happens, I can scroll to all parts of the right side of the image but not the left.

The most important thing is that the zoom has to happen in the middle of the screen pretty much like you would a normal android pinch-to-zoom on a pdf. Any assistance is welcomed. Thanks already.

Back Template:

<button class="setzoom" onclick="toggleZoom();" id="myBtn3b"></button>
<button class="spoiler" id="Back" style="display:; padding:0; font-size: 16px; text-align: left;" onclick="{document.getElementById('Back') .style.display='none'}"><div id="contentField">{{Back_m}}</div></button>
<script>
// Get the button
let zoomedIn = false;

function toggleZoom() {
    const firstImage = document.querySelector("#contentField img:first-of-type");
    const button = document.getElementById("myBtn3b");

    if (firstImage) {
        if (zoomedIn) {
            firstImage.style.transform = "scale(1)";
        } else {
            firstImage.style.transform = "scale(2)";
        }

        zoomedIn = !zoomedIn;
    } else {
      document.getElementById("myBtn3b").innerHTML="!Img";
    }
}
</script>

CSS

img {
  display: block;
  margin-left: auto;
  margin-right: auto;
  max-width: auto;
  width:auto;
  max-height:100%;
}

#myBtn3b {
  position: fixed;
  bottom: 175px;
  right: 33px;
  z-index: 99;
  border: none;
  outline: none;
  background-color: rgba(133, 133, 133,0.5);
  padding: 26px;
  padding-top: 24px;
  padding-bottom: px;
  border-radius: 5px;
}

#contentField {
    position: relative;
    overflow: auto;
    max-width: 100%; 
}

#contentField img:first-of-type {
    max-width: 100%;
    transition: transform 0.3s;
    transform-origin: center;
}

I don’t think I got what you wanted from your description but how about this. Instead of scaling the image and showing scrollbars, allow the image to overflow so it fills your screen when zoomed. This feels better to me compared to having the image constrained within the viewport. Currently this will push all other elements around as it resizes though.

Template

<button class="setzoom" onclick="toggleZoom();" id="myBtn3b"></button>
<button
  class="spoiler"
  id="Back"
  style="display: ; padding: 0; font-size: 16px; text-align: left"
  onclick="{document.getElementById('Back') .style.display='none'}"
>
  <div id="contentField">{{Back}}</div>
</button>
<script>
  var zoomedIn = false;

  function toggleZoom() {
    const firstImage = document.querySelector(
      "#contentField img:first-of-type"
    );
    const zoomButton = document.getElementById("myBtn3b");

    if (firstImage) {
      // The image is allowed to overflow its container so we can control its "zoom"
      // by changing the width of the image, it will then increase in size and overflow
      // the container and scrolling around the image will be possible
      if (zoomedIn) {
        firstImage.style.width = "100%";
      } else {
        firstImage.style.width = "200%";
      }

      zoomedIn = !zoomedIn;
    } else {
      zoomButton.innerHTML = "!Img";
    }
  }
</script>

Styling

img {
  display: block;
  margin-left: auto;
  margin-right: auto;
  /* override the max-width: 100% and max-height: 95vh set in the default reviewer.css */
  max-width: none;
  max-height: none;
  /* allow the image to overflow the viewport */
  overflow: visible;
  /* however initially we want to the image to fit the viewport
     zooming in and out will be controlled by changing the width */
  width: 100%;
}

#myBtn3b {
  position: fixed;
  bottom: 175px;
  right: 33px;
  z-index: 9999;
  border: none;
  outline: none;
  background-color: rgba(133, 133, 133,0.5);
  padding: 26px;
  padding-top: 24px;
  padding-bottom: px;
  border-radius: 5px;
}

#contentField {
    position: relative;
    max-width: 100%; 
}

#contentField img:first-of-type {
    transition: width 0.3s;
    transform-origin: center;
}

Scaling doesn’t happen at all.

Unless I change firstImage.style.width="200%"; to first Image.style.transform="scale(2)"; and in the css, transition: width 0.3s; to transition: transform 0.3s;

This is what GPT is also saying (tried but isn’t working):

The issue seems to arise from the transform-origin not being set to the center of the visible area when the zoom happens, and the overflow styling not allowing proper scrolling to the left. Here’s how you can fix it:

Key Fixes:

  1. Ensure Transform Origin is Correctly Set: Modify the transform-origin dynamically to focus on the center of the visible area when zooming.
  2. Fix Scroll Overflow: Ensure the container allows full scrolling in all directions when zoomed in.
  3. Enhance Responsiveness: Add dynamic centering logic so the zoom focuses on the middle of the visible screen rather than the default transform origin.

Here’s your updated code:

Back Template

<button class="setzoom" onclick="toggleZoom();" id="myBtn3b"></button>
<button class="spoiler" id="Back" style="display:; padding:0; font-size: 16px; text-align: left;" onclick="{document.getElementById('Back').style.display='none'}">
  <div id="contentField">{{Back_m}}</div>
</button>
<script>
let zoomedIn = false;

function toggleZoom() {
    const firstImage = document.querySelector("#contentField img:first-of-type");
    const contentField = document.getElementById("contentField");

    if (firstImage) {
        if (zoomedIn) {
            // Zoom out
            firstImage.style.transform = "scale(1)";
            firstImage.style.transformOrigin = "center center";
            contentField.scrollTo(contentField.scrollWidth / 2, contentField.scrollHeight / 2);
        } else {
            // Calculate the center of the visible area
            const rect = contentField.getBoundingClientRect();
            const centerX = rect.width / 2 + contentField.scrollLeft;
            const centerY = rect.height / 2 + contentField.scrollTop;

            // Set transform-origin dynamically to the center of the visible area
            firstImage.style.transformOrigin = `${centerX}px ${centerY}px`;
            firstImage.style.transform = "scale(2)";
        }

        zoomedIn = !zoomedIn;
    } else {
        document.getElementById("myBtn3b").innerHTML = "!Img";
    }
}
</script>

CSS

img {
  display: block;
  margin-left: auto;
  margin-right: auto;
  max-width: auto;
  width: auto;
  max-height: 100%;
}

#myBtn3b {
  position: fixed;
  bottom: 175px;
  right: 33px;
  z-index: 99;
  border: none;
  outline: none;
  background-color: rgba(133, 133, 133, 0.5);
  padding: 26px;
  padding-top: 24px;
  border-radius: 5px;
}

#contentField {
  position: relative;
  overflow: auto;
  max-width: 100%;
  height: 500px; /* Adjust based on your card's layout */
}

#contentField img:first-of-type {
  max-width: 100%;
  transition: transform 0.3s ease-in-out;
}