Skip to content

Instantly share code, notes, and snippets.

@swarad07
Created February 5, 2019 11:25
Show Gist options
  • Save swarad07/ed739465361d952df5cbe6e9f27d2a96 to your computer and use it in GitHub Desktop.
Save swarad07/ed739465361d952df5cbe6e9f27d2a96 to your computer and use it in GitHub Desktop.
Directive.js from demo page https://www.cssscript.com/demo/touch-enabled-image-zoom-plugin-javascript/ the github repo points to a new version which doesnt work in Chrome mobile on some devices.
// @codingStandardsIgnoreStart
"use strict";
zoom();
function zoom(classNames, settings) {
/**
* IE 11 Compatiblity fix.
*
* IE 11 doesnt support ES6 standard so the function defination line,
* function zoom(classNames = {}, settings = {}) {
* needs to be changed to,
* function zoom(classNames, settings) {
*
* And we need to explictly check for empty argument and assign a default
* value below
*
* Patch starts.
*/
var classNames = classNames || {};
var settings = settings || {};
/**
* Patch ends.
*/
var C_scaleDefault = settings["scaleDefault"] || 2;
var C_scaleDifference = settings["scaleDifference"] || 0.5;
var C_scaleMax = settings["scaleMax"] || 10;
var C_scaleMin = settings["scaleMin"] || 1;
var _active = classNames["active"] || "active";
var _dataScale = "data-scale";
var _dataTranslateX = "data-translate-x";
var _dataTranslateY = "data-translate-y";
var _visible = classNames["visible"] || "visible";
var $container;
var $element;
var $zoom = document.getElementsByClassName(classNames["zoom"] || "zoom");
var capture = false;
var doubleClickMonitor = [null];
var containerHeight;
var containerWidth;
var containerOffsetX;
var containerOffsetY;
var initialScale;
var elementHeight;
var elementWidth;
var heightDifference;
var initialOffsetX;
var initialOffsetY;
var initialPinchDistance;
var initialPointerOffsetX;
var initialPointerOffsetX2;
var initialPointerOffsetY;
var initialPointerOffsetY2;
var limitOffsetX;
var limitOffsetY;
var mousemoveCount = 0;
var offset;
var pinchOffsetX;
var pinchOffsetY;
var pointerOffsetX;
var pointerOffsetX2;
var pointerOffsetY;
var pointerOffsetY2;
var scaleDirection;
var scaleDifference;
var targetOffsetX;
var targetOffsetY;
var targetPinchDistance;
var targetScale;
var touchable = false;
var touchCount;
var touchmoveCount = 0;
var doubleTapMonitor = [null];
var widthDifference;
for (var i = 0; i < $zoom.length; i++) {
$container = $zoom[i];
$element = $container.children[0];
$element.setAttribute(_dataScale, 1);
$element.setAttribute(_dataTranslateX, 0);
$element.setAttribute(_dataTranslateY, 0);
}
window.addEventListener("load", function() {
for (var i = 0; i < $zoom.length; i++) {
$container = $zoom[i];
$element = $container.children[0];
addClass($element, _visible);
}
window.addEventListener("resize", function() {
for (var i = 0; i < $zoom.length; i++) {
$container = $zoom[i];
$element = $container.children[0];
if (hasClass($container, _active) === false) {
continue;
}
containerHeight = $container.clientHeight;
containerWidth = $container.clientWidth;
elementHeight = $element.clientHeight;
elementWidth = $element.clientWidth;
initialOffsetX = parseFloat($element.getAttribute(_dataTranslateX));
initialOffsetY = parseFloat($element.getAttribute(_dataTranslateY));
targetScale = C_scaleDefault;
limitOffsetX = ((elementWidth * targetScale) - containerWidth) / 2;
limitOffsetY = ((elementHeight * targetScale) - containerHeight) / 2;
targetOffsetX = (elementWidth * targetScale) > containerWidth ? minMax(initialOffsetX, limitOffsetX * (-1), limitOffsetX) : 0;
targetOffsetY = (elementHeight * targetScale) > containerHeight ? minMax(initialOffsetY, limitOffsetY * (-1), limitOffsetY) : 0;
if (targetScale === 1) {
removeClass($container, _active);
}
$element.setAttribute(_dataScale, targetScale);
$element.setAttribute(_dataTranslateX, targetOffsetX);
$element.setAttribute(_dataTranslateY, targetOffsetY);
moveScaleElement($element, targetOffsetX + "px", targetOffsetY + "px", targetScale);
}
});
});
massAddEventListener($zoom, "mousedown", mouseDown);
massAddEventListener($zoom, "mouseenter", mouseEnter);
massAddEventListener($zoom, "mouseleave", mouseLeave);
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);
document.addEventListener("touchstart", function() {
touchable = true;
});
massAddEventListener($zoom, "touchstart", touchStart);
document.addEventListener("touchmove", touchMove);
document.addEventListener("touchend", touchEnd);
massAddEventListener($zoom, "wheel", wheel);
function mouseEnter() {
disableScroll();
}
function mouseLeave() {
enableScroll();
}
function mouseDown(e) {
e.preventDefault();
if (touchable === true || e.which !== 1) {
return false;
}
$container = this;
$element = this.children[0];
initialPointerOffsetX = e.clientX;
initialPointerOffsetY = e.clientY;
if (doubleClickMonitor[0] === null) {
doubleClickMonitor[0] = e.target;
doubleClickMonitor[1] = initialPointerOffsetX;
doubleClickMonitor[2] = initialPointerOffsetY;
setTimeout(function() {
doubleClickMonitor = [null];
}, 400);
} else if (doubleClickMonitor[0] === e.target && mousemoveCount <= 5 && isWithinRange(initialPointerOffsetX, doubleClickMonitor[1] - 10, doubleClickMonitor[1] + 10) === true && isWithinRange(initialPointerOffsetY, doubleClickMonitor[2] - 10, doubleClickMonitor[2] + 10) === true) {
if (hasClass($container, _active) === true) {
$element.setAttribute(_dataScale, 1);
$element.setAttribute(_dataTranslateX, 0);
$element.setAttribute(_dataTranslateY, 0);
removeClass($container, _active);
moveScaleElement($element, 0, 0, 1);
} else {
$element.setAttribute(_dataScale, C_scaleDefault);
$element.setAttribute(_dataTranslateX, 0);
$element.setAttribute(_dataTranslateY, 0);
addClass($container, _active);
moveScaleElement($element, 0, 0, C_scaleDefault);
}
doubleClickMonitor = [null];
return false;
}
containerOffsetX = $container.offsetLeft;
containerOffsetY = $container.offsetTop;
containerHeight = $container.clientHeight;
containerWidth = $container.clientWidth
elementHeight = $element.clientHeight;
elementWidth = $element.clientWidth;
initialOffsetX = parseFloat($element.getAttribute(_dataTranslateX));
initialOffsetY = parseFloat($element.getAttribute(_dataTranslateY));
initialScale = minMax(parseFloat($element.getAttribute(_dataScale)), C_scaleMin, C_scaleMax);
mousemoveCount = 0;
capture = true;
}
function mouseMove(e) {
if (touchable === true || capture === false) {
return false;
}
pointerOffsetX = e.clientX;
pointerOffsetY = e.clientY;
targetScale = initialScale;
limitOffsetX = ((elementWidth * targetScale) - containerWidth) / 2;
limitOffsetY = ((elementHeight * targetScale) - containerHeight) / 2;
targetOffsetX = (elementWidth * targetScale) <= containerWidth ? 0 : minMax(pointerOffsetX - (initialPointerOffsetX - initialOffsetX), limitOffsetX * (-1), limitOffsetX);
targetOffsetY = (elementHeight * targetScale) <= containerHeight ? 0 : minMax(pointerOffsetY - (initialPointerOffsetY - initialOffsetY), limitOffsetY * (-1), limitOffsetY);
mousemoveCount++;
if (Math.abs(targetOffsetX) === Math.abs(limitOffsetX)) {
initialOffsetX = targetOffsetX;
initialPointerOffsetX = pointerOffsetX;
}
if (Math.abs(targetOffsetY) === Math.abs(limitOffsetY)) {
initialOffsetY = targetOffsetY;
initialPointerOffsetY = pointerOffsetY;
}
$element.setAttribute(_dataScale, targetScale);
$element.setAttribute(_dataTranslateX, targetOffsetX);
$element.setAttribute(_dataTranslateY, targetOffsetY);
moveScaleElement($element, targetOffsetX + "px", targetOffsetY + "px", targetScale);
}
function mouseUp() {
if (touchable === true || capture === false) {
return false;
}
capture = false;
}
function touchStart(e) {
e.preventDefault();
if (e.touches.length > 2) {
return false;
}
$container = this;
$element = this.children[0];
containerOffsetX = $container.offsetLeft;
containerOffsetY = $container.offsetTop;
containerHeight = $container.clientHeight;
containerWidth = $container.clientWidth;
elementHeight = $element.clientHeight;
elementWidth = $element.clientWidth;
initialPointerOffsetX = e.touches[0].clientX;
initialPointerOffsetY = e.touches[0].clientY;
initialScale = minMax(parseFloat($element.getAttribute(_dataScale)), C_scaleMin, C_scaleMax);
touchCount = e.touches.length;
if (touchCount === 1) {
if (doubleTapMonitor[0] === null) {
doubleTapMonitor[0] = e.target;
doubleTapMonitor[1] = initialPointerOffsetX;
doubleTapMonitor[2] = initialPointerOffsetY;
setTimeout(function() {
doubleTapMonitor = [null];
}, 400);
} else if (doubleTapMonitor[0] === e.target && touchmoveCount <= 5 && isWithinRange(initialPointerOffsetX, doubleTapMonitor[1] - 30, doubleTapMonitor[1] + 30) === true && isWithinRange(initialPointerOffsetY, doubleTapMonitor[2] - 30, doubleTapMonitor[2] + 25) === true) {
if (hasClass($container, _active) === true) {
$element.setAttribute(_dataScale, 1);
$element.setAttribute(_dataTranslateX, 0);
$element.setAttribute(_dataTranslateY, 0);
removeClass($container, _active);
moveScaleElement($element, 0, 0, 1);
} else {
$element.setAttribute(_dataScale, C_scaleDefault);
$element.setAttribute(_dataTranslateX, 0);
$element.setAttribute(_dataTranslateY, 0);
addClass($container, _active);
moveScaleElement($element, 0, 0, C_scaleDefault);
}
doubleTapMonitor = [null];
return false;
}
initialOffsetX = parseFloat($element.getAttribute(_dataTranslateX));
initialOffsetY = parseFloat($element.getAttribute(_dataTranslateY));
} else if (touchCount === 2) {
initialOffsetX = parseFloat($element.getAttribute(_dataTranslateX));
initialOffsetY = parseFloat($element.getAttribute(_dataTranslateY));
initialPointerOffsetX2 = e.touches[1].clientX;
initialPointerOffsetY2 = e.touches[1].clientY;
pinchOffsetX = (initialPointerOffsetX + initialPointerOffsetX2) / 2;
pinchOffsetY = (initialPointerOffsetY + initialPointerOffsetY2) / 2;
initialPinchDistance = Math.sqrt(((initialPointerOffsetX - initialPointerOffsetX2) * (initialPointerOffsetX - initialPointerOffsetX2)) + ((initialPointerOffsetY - initialPointerOffsetY2) * (initialPointerOffsetY - initialPointerOffsetY2)));
}
touchmoveCount = 0;
capture = true;
}
function touchMove(e) {
e.preventDefault();
if (capture === false) {
return false;
}
pointerOffsetX = e.touches[0].clientX;
pointerOffsetY = e.touches[0].clientY;
touchCount = e.touches.length;
touchmoveCount++;
if (touchCount > 1) {
pointerOffsetX2 = e.touches[1].clientX;
pointerOffsetY2 = e.touches[1].clientY;
targetPinchDistance = Math.sqrt(((pointerOffsetX - pointerOffsetX2) * (pointerOffsetX - pointerOffsetX2)) + ((pointerOffsetY - pointerOffsetY2) * (pointerOffsetY - pointerOffsetY2)));
if (initialPinchDistance === null) {
initialPinchDistance = targetPinchDistance;
}
if (Math.abs(initialPinchDistance - targetPinchDistance) >= 1) {
targetScale = minMax(targetPinchDistance / initialPinchDistance * initialScale, C_scaleMin, C_scaleMax);
limitOffsetX = ((elementWidth * targetScale) - containerWidth) / 2;
limitOffsetY = ((elementHeight * targetScale) - containerHeight) / 2;
scaleDifference = targetScale - initialScale;
targetOffsetX = (elementWidth * targetScale) <= containerWidth ? 0 : minMax(initialOffsetX - ((((((pinchOffsetX - containerOffsetX) - (containerWidth / 2)) - initialOffsetX) / (targetScale - scaleDifference))) * scaleDifference), limitOffsetX * (-1), limitOffsetX);
targetOffsetY = (elementHeight * targetScale) <= containerHeight ? 0 : minMax(initialOffsetY - ((((((pinchOffsetY - containerOffsetY) - (containerHeight / 2)) - initialOffsetY) / (targetScale - scaleDifference))) * scaleDifference), limitOffsetY * (-1), limitOffsetY);
if (targetScale > 1) {
addClass($container, _active);
} else {
removeClass($container, _active);
}
moveScaleElement($element, targetOffsetX + "px", targetOffsetY + "px", targetScale);
initialPinchDistance = targetPinchDistance;
initialScale = targetScale;
initialOffsetX = targetOffsetX;
initialOffsetY = targetOffsetY;
}
} else {
targetScale = initialScale;
limitOffsetX = ((elementWidth * targetScale) - containerWidth) / 2;
limitOffsetY = ((elementHeight * targetScale) - containerHeight) / 2;
targetOffsetX = (elementWidth * targetScale) <= containerWidth ? 0 : minMax(pointerOffsetX - (initialPointerOffsetX - initialOffsetX), limitOffsetX * (-1), limitOffsetX);
targetOffsetY = (elementHeight * targetScale) <= containerHeight ? 0 : minMax(pointerOffsetY - (initialPointerOffsetY - initialOffsetY), limitOffsetY * (-1), limitOffsetY);
if (Math.abs(targetOffsetX) === Math.abs(limitOffsetX)) {
initialOffsetX = targetOffsetX;
initialPointerOffsetX = pointerOffsetX;
}
if (Math.abs(targetOffsetY) === Math.abs(limitOffsetY)) {
initialOffsetY = targetOffsetY;
initialPointerOffsetY = pointerOffsetY;
}
$element.setAttribute(_dataScale, initialScale);
$element.setAttribute(_dataTranslateX, targetOffsetX);
$element.setAttribute(_dataTranslateY, targetOffsetY);
moveScaleElement($element, targetOffsetX + "px", targetOffsetY + "px", targetScale);
}
}
function touchEnd(e) {
touchCount = e.touches.length;
if (capture === false) {
return false;
}
if (touchCount === 0) {
$element.setAttribute(_dataScale, initialScale);
$element.setAttribute(_dataTranslateX, targetOffsetX);
$element.setAttribute(_dataTranslateY, targetOffsetY);
initialPinchDistance = null;
capture = false;
} else if (touchCount === 1) {
initialPointerOffsetX = e.touches[0].clientX;
initialPointerOffsetY = e.touches[0].clientY;
} else if (touchCount > 1) {
initialPinchDistance = null;
}
}
function wheel(e) {
$container = this;
$element = this.children[0];
offset = $container.getBoundingClientRect();
containerHeight = $container.clientHeight;
containerWidth = $container.clientWidth;
elementHeight = $element.clientHeight;
elementWidth = $element.clientWidth;
containerOffsetX = offset.left;
containerOffsetY = offset.top;
initialScale = minMax(parseFloat($element.getAttribute(_dataScale), C_scaleMin, C_scaleMax));
initialOffsetX = parseFloat($element.getAttribute(_dataTranslateX));
initialOffsetY = parseFloat($element.getAttribute(_dataTranslateY));
pointerOffsetX = e.clientX;
pointerOffsetY = e.clientY;
scaleDirection = e.deltaY < 0 ? 1 : -1;
scaleDifference = C_scaleDifference * scaleDirection;
targetScale = initialScale + scaleDifference;
if (targetScale < C_scaleMin || targetScale > C_scaleMax) {
return false;
}
limitOffsetX = ((elementWidth * targetScale) - containerWidth) / 2;
limitOffsetY = ((elementHeight * targetScale) - containerHeight) / 2;
if (targetScale <= 1) {
targetOffsetX = 0;
targetOffsetY = 0;
} else {
targetOffsetX = (elementWidth * targetScale) <= containerWidth ? 0 : minMax(initialOffsetX - ((((((pointerOffsetX - containerOffsetX) - (containerWidth / 2)) - initialOffsetX) / (targetScale - scaleDifference))) * scaleDifference), limitOffsetX * (-1), limitOffsetX);
targetOffsetY = (elementHeight * targetScale) <= containerHeight ? 0 : minMax(initialOffsetY - ((((((pointerOffsetY - containerOffsetY) - (containerHeight / 2)) - initialOffsetY) / (targetScale - scaleDifference))) * scaleDifference), limitOffsetY * (-1), limitOffsetY);
}
if (targetScale > 1) {
addClass($container, _active);
} else {
removeClass($container, _active);
}
$element.setAttribute(_dataScale, targetScale);
$element.setAttribute(_dataTranslateX, targetOffsetX);
$element.setAttribute(_dataTranslateY, targetOffsetY);
moveScaleElement($element, targetOffsetX + "px", targetOffsetY + "px", targetScale);
}
}
function addClass($element, targetClass) {
if (hasClass($element, targetClass) === false) {
$element.className += " " + targetClass;
}
}
function disableScroll() {
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', preventDefault, false);
}
window.onwheel = preventDefault;
window.onmousewheel = document.onmousewheel = preventDefault;
window.ontouchmove = preventDefault;
document.onkeydown = preventDefaultForScrollKeys;
}
function enableScroll() {
if (window.removeEventListener) {
window.removeEventListener('DOMMouseScroll', preventDefault, false);
}
window.onmousewheel = document.onmousewheel = null;
window.onwheel = null;
window.ontouchmove = null;
document.onkeydown = null;
}
function isWithinRange(value, min, max) {
if (value >= min && value <= max) {
return true;
} else {
return false;
}
}
function hasClass($element, targetClass) {
var rgx = new RegExp("(?:^|\\s)" + targetClass + "(?!\\S)","g");
if ($element.className.match(rgx)) {
return true;
} else {
return false;
}
}
function massAddEventListener($elements, event, customFunction, useCapture) {
var useCapture = useCapture || false;
for (var i = 0; i < $elements.length; i++) {
$elements[i].addEventListener(event, customFunction, useCapture);
}
}
function minMax(value, min, max) {
if (value < min) {
value = min;
} else if (value > max) {
value = max;
}
return value;
}
function moveScaleElement($element, targetOffsetX, targetOffsetY, targetScale) {
$element.style.cssText = "-moz-transform : translate(" + targetOffsetX + ", " + targetOffsetY + ") scale(" + targetScale + "); -ms-transform : translate(" + targetOffsetX + ", " + targetOffsetY + ") scale(" + targetScale + "); -o-transform : translate(" + targetOffsetX + ", " + targetOffsetY + ") scale(" + targetScale + "); -webkit-transform : translate(" + targetOffsetX + ", " + targetOffsetY + ") scale(" + targetScale + "); transform : translate3d(" + targetOffsetX + ", " + targetOffsetY + ", 0) scale3d(" + targetScale + ", " + targetScale + ", 1);";
}
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
}
e.returnValue = false;
}
function preventDefaultForScrollKeys(e) {
var keys = {
37: 1,
38: 1,
39: 1,
40: 1
};
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
function removeClass($element, targetClass) {
var rgx = new RegExp("(?:^|\\s)" + targetClass + "(?!\\S)","g");
$element.className = $element.className.replace(rgx, "");
}
// @codingStandardsIgnoreEnd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment