fat / zoom.js

Medium's Image Zoom for jQuery
https://fat.github.io/zoom.js
MIT License
4.04k stars 331 forks source link

Zoomed image location impacted by page flow #16

Closed davidklebanoff closed 9 years ago

davidklebanoff commented 9 years ago

The placement of the zoomed-in version of the image is being impacted by other elements in the page flow. If parent elements have transforms (e.g. transform: translate3d(0, 0, 0) ) applied to them, the z-index and location of the zoomed image can be thrown off completely. This can result in the zoomed image being off-center, off-screen, clipped, or under other elements (such as the zoom overlay).

I came across this issue while attempting to integrate Zoom with the Slick Carousel , however this issue could occur in many different scenarios.

Any thoughts on solutions? A few initial ideas:

  1. Don't do that!
  2. Make use of the .zoom-overlay-open selector to temporarily subdue any offending parent elements by removing breaking css (e.g. transform: none, overflow: visible, etc.). Although, I've been playing with this for a little bit, and have yet to achieve a successful result. The z-indexing issues are fixable, but the image is still off-center/off-screen. Perhaps the damage is already done by offset calculations being performed before the offending selectors have been removed.
  3. Add a new element next to the zoom-overlay to contain the zoomed-in image... but what about the zoom-out effect that starts at the location of the image?
  4. ???
davidklebanoff commented 9 years ago

Ok, so I played around with the 2nd solution a little more. Adding the .zoom-overlay-open class before the offsets are calculated fixed the positioning issues, although you will see the elements change position as the backdrop overlay fades in (preventable if you eliminate the transition on the backdrop).

Note: Only works after I added some sketchy css to remove offending transforms and overflow. Meh.

/* Ewwwww */
.zoom-overlay-open div:not(.zoom-img-wrap) {
  -webkit-transform: none !important;
  -moz-transform: none !important;
  -ms-transform: none !important;
  -o-transform: none !important;
  transform: none !important;
  overflow: visible !important;
}
    Zoom.prototype._triggerAnimation = function () {

        this._$body.addClass('zoom-overlay-open')

        this._targetImage.offsetWidth // repaint before animating

        var imageOffset = $(this._targetImage).offset()
        var scrollTop = window.scrollY

        var viewportY = scrollTop + (window.innerHeight / 2)
        var viewportX = (window.innerWidth / 2)

        var imageCenterY = imageOffset.top + (this._targetImage.height / 2)
        var imageCenterX = imageOffset.left + (this._targetImage.width / 2)

        this._translateY = viewportY - imageCenterY
        this._translateX = viewportX - imageCenterX

        $(this._targetImage).css('transform', 'scale(' + this._imgScaleFactor + ')')
        $(this._targetImageWrap).css('transform', 'translate(' + this._translateX + 'px, ' + this._translateY + 'px) translateZ(0)')

    }
fat commented 9 years ago

Ah yeah don't put it inside something with a transform

Transforms are going to create new stacking contexts and throw off lots of calculations :+1:

We did that same sketchy css thing at medium if it makes you feel better - to null the transforms

davidklebanoff commented 9 years ago

Do we want to at least move the addition of the zoom-overlay-open class to the top of the _triggerAnimation method, as mentioned above, so the calculations are performed after the transforms are tweaked... otherwise the "sketchy css" has no effect.

this._$body.addClass('zoom-overlay-open')