UNPKG

drift-zoom

Version:

Easily add "zoom on hover" functionality to your site's images. Lightweight, no-dependency JavaScript.

208 lines (173 loc) 5.91 kB
import throwIfMissing from "./util/throwIfMissing"; import BoundingBox from "./BoundingBox"; export default class Trigger { constructor(options = {}) { this._show = this._show.bind(this); this._hide = this._hide.bind(this); this._handleEntry = this._handleEntry.bind(this); this._handleMovement = this._handleMovement.bind(this); const { el = throwIfMissing(), zoomPane = throwIfMissing(), sourceAttribute = throwIfMissing(), handleTouch = throwIfMissing(), onShow = null, onHide = null, hoverDelay = 0, touchDelay = 0, hoverBoundingBox = throwIfMissing(), touchBoundingBox = throwIfMissing(), namespace = null, zoomFactor = throwIfMissing(), boundingBoxContainer = throwIfMissing(), passive = false, } = options; this.settings = { el, zoomPane, sourceAttribute, handleTouch, onShow, onHide, hoverDelay, touchDelay, hoverBoundingBox, touchBoundingBox, namespace, zoomFactor, boundingBoxContainer, passive, }; if (this.settings.hoverBoundingBox || this.settings.touchBoundingBox) { this.boundingBox = new BoundingBox({ namespace: this.settings.namespace, zoomFactor: this.settings.zoomFactor, containerEl: this.settings.boundingBoxContainer, }); } this.enabled = true; this._bindEvents(); } get isShowing() { return this.settings.zoomPane.isShowing; } _preventDefault(event) { event.preventDefault(); } _preventDefaultAllowTouchScroll(event) { if (!this.settings.touchDelay || !this._isTouchEvent(event) || this.isShowing) { event.preventDefault(); } } _isTouchEvent(event) { return !!event.touches; } _bindEvents() { this.settings.el.addEventListener("mouseenter", this._handleEntry); this.settings.el.addEventListener("mouseleave", this._hide); this.settings.el.addEventListener("mousemove", this._handleMovement); const isPassive = { passive: this.settings.passive }; if (this.settings.handleTouch) { this.settings.el.addEventListener("touchstart", this._handleEntry, isPassive); this.settings.el.addEventListener("touchend", this._hide); this.settings.el.addEventListener("touchmove", this._handleMovement, isPassive); } else { this.settings.el.addEventListener("touchstart", this._preventDefault, isPassive); this.settings.el.addEventListener("touchend", this._preventDefault); this.settings.el.addEventListener("touchmove", this._preventDefault, isPassive); } } _unbindEvents() { this.settings.el.removeEventListener("mouseenter", this._handleEntry); this.settings.el.removeEventListener("mouseleave", this._hide); this.settings.el.removeEventListener("mousemove", this._handleMovement); if (this.settings.handleTouch) { this.settings.el.removeEventListener("touchstart", this._handleEntry); this.settings.el.removeEventListener("touchend", this._hide); this.settings.el.removeEventListener("touchmove", this._handleMovement); } else { this.settings.el.removeEventListener("touchstart", this._preventDefault); this.settings.el.removeEventListener("touchend", this._preventDefault); this.settings.el.removeEventListener("touchmove", this._preventDefault); } } _handleEntry(e) { this._preventDefaultAllowTouchScroll(e); this._lastMovement = e; if (e.type == "mouseenter" && this.settings.hoverDelay) { this.entryTimeout = setTimeout(this._show, this.settings.hoverDelay); } else if (this.settings.touchDelay) { this.entryTimeout = setTimeout(this._show, this.settings.touchDelay); } else { this._show(); } } _show() { if (!this.enabled) { return; } const onShow = this.settings.onShow; if (onShow && typeof onShow === "function") { onShow(); } this.settings.zoomPane.show( this.settings.el.getAttribute(this.settings.sourceAttribute), this.settings.el.clientWidth, this.settings.el.clientHeight ); if (this._lastMovement) { const touchActivated = this._lastMovement.touches; if ((touchActivated && this.settings.touchBoundingBox) || (!touchActivated && this.settings.hoverBoundingBox)) { this.boundingBox.show(this.settings.zoomPane.el.clientWidth, this.settings.zoomPane.el.clientHeight); } } this._handleMovement(); } _hide(e) { if (e) { this._preventDefaultAllowTouchScroll(e); } this._lastMovement = null; if (this.entryTimeout) { clearTimeout(this.entryTimeout); } if (this.boundingBox) { this.boundingBox.hide(); } const onHide = this.settings.onHide; if (onHide && typeof onHide === "function") { onHide(); } this.settings.zoomPane.hide(); } _handleMovement(e) { if (e) { this._preventDefaultAllowTouchScroll(e); this._lastMovement = e; } else if (this._lastMovement) { e = this._lastMovement; } else { return; } let movementX; let movementY; if (e.touches) { const firstTouch = e.touches[0]; movementX = firstTouch.clientX; movementY = firstTouch.clientY; } else { movementX = e.clientX; movementY = e.clientY; } const el = this.settings.el; const rect = el.getBoundingClientRect(); const offsetX = movementX - rect.left; const offsetY = movementY - rect.top; const percentageOffsetX = offsetX / this.settings.el.clientWidth; const percentageOffsetY = offsetY / this.settings.el.clientHeight; if (this.boundingBox) { this.boundingBox.setPosition(percentageOffsetX, percentageOffsetY, rect); } this.settings.zoomPane.setPosition(percentageOffsetX, percentageOffsetY, rect); } }