kopia lustrzana https://github.com/Aonrud/ila-microblog.pub
feat: add Panzoom to js image viewer
rodzic
bdd0dfe1c7
commit
260d36cb03
|
@ -26,11 +26,8 @@ function applyConfig(defaults, conf) {
|
|||
let c = {};
|
||||
|
||||
for (const prop in defaults) {
|
||||
if (conf[prop] && typeof conf[prop] !== typeof defaults[prop]) {
|
||||
console.warn(`Config option ${prop} has the wrong type of value. Skipping`);
|
||||
continue;
|
||||
}
|
||||
if (typeof defaults[prop] === "object" && !(defaults[prop] instanceof Array) && conf[prop]) {
|
||||
|
||||
if (typeof defaults[prop] === "object" && !(defaults[prop] instanceof Array) && conf[prop] && typeof conf[prop] !== "function") {
|
||||
c[prop] = applyConfig(defaults[prop], conf[prop]);
|
||||
} else {
|
||||
c[prop] = conf[prop] ?? defaults[prop];
|
||||
|
@ -276,7 +273,7 @@ class Scroller {
|
|||
const swipe = new Swipe(this._wrapper);
|
||||
swipe.attach();
|
||||
this._wrapper.addEventListener('swiped-right', () => this.left() );
|
||||
this._wrapper.addEventListener('swiped-left', e => this.right() );
|
||||
this._wrapper.addEventListener('swiped-left', () => this.right() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -440,8 +437,8 @@ class Scroller {
|
|||
* The configuration object for the ImageViewer.
|
||||
* @typedef {object} imageViewerConfig
|
||||
* @property {string} [targetClass = viewer]
|
||||
* @property {boolean} [panzoom = false]
|
||||
* Activate the zoom button, which toggles the image's full size and allows panning around.
|
||||
* @property {function|null} [panzoom = null]
|
||||
* If the Panzoom function is passed, activate the zoom button, which toggles the image's full size and allows panning around.
|
||||
* Requires @panzoom/panzoom module to be available.
|
||||
* @property {boolean} [showDownload = false] Show a button to download the image
|
||||
* @property {boolean} [showLink = true] Show a link button for any images with a link associated
|
||||
|
@ -495,7 +492,7 @@ class Scroller {
|
|||
*/
|
||||
const defaultImageViewerConfig = {
|
||||
targetClass: "viewer",
|
||||
panzoom: false,
|
||||
panzoom: null,
|
||||
showDownload: false,
|
||||
showLink: true,
|
||||
captions: [ "& + figcaption", "& + .caption" ],
|
||||
|
@ -565,6 +562,7 @@ class ImageViewer {
|
|||
* @public
|
||||
*/
|
||||
next() {
|
||||
if (this._images.length < 2) return;
|
||||
const n = this._activeIndex === this._images.length - 1 ? 0 : this._activeIndex + 1;
|
||||
this.show(n);
|
||||
}
|
||||
|
@ -574,6 +572,7 @@ class ImageViewer {
|
|||
* @public
|
||||
*/
|
||||
prev() {
|
||||
if (this._images.length < 2) return;
|
||||
const n = this._activeIndex === 0 ? this._images.length - 1 : this._activeIndex - 1;
|
||||
this.show(n);
|
||||
}
|
||||
|
@ -639,14 +638,13 @@ class ImageViewer {
|
|||
|
||||
//Prevent jumping back if display has moved on to another image before this one is loaded.
|
||||
if(el.dataset.loading == url) {
|
||||
console.log(url);
|
||||
//Put the loaded image into the visible <img> element.
|
||||
//The promise is awaited again to prevent the controls update checking the width before the image is in position.
|
||||
await this._loadImage(url, this._imgDisplay);
|
||||
el.alt = img.getAttribute("alt");
|
||||
this._loader.style.visibility = "hidden";
|
||||
this._updateCaption(n);
|
||||
this.revealToggle(!img.dataset.hasOwnProperty("reveal") || !img.dataset.reveal == 'true');
|
||||
this.revealToggle(!Object.hasOwn(img.dataset, "reveal") || !img.dataset.reveal == 'true');
|
||||
this._updateControls();
|
||||
}
|
||||
}
|
||||
|
@ -659,7 +657,6 @@ class ImageViewer {
|
|||
* @return Promise
|
||||
*/
|
||||
_loadImage(url, i = new Image()) {
|
||||
console.log(`Loading ${url}`);
|
||||
return new Promise((resolve) => {
|
||||
i.addEventListener('load', () => { resolve(url); });
|
||||
i.src = url;
|
||||
|
@ -752,7 +749,7 @@ class ImageViewer {
|
|||
*/
|
||||
btnToggle(btn, switchOn = true) {
|
||||
const btnName = btn.id.replace("btn-", "");
|
||||
const btnTextNode = [...btn.childNodes].filter( n => n.nodeType === Node.TEXT_NODE)[0];
|
||||
let btnTextNode = [...btn.childNodes].filter( n => n.nodeType === Node.TEXT_NODE)[0];
|
||||
const txt = switchOn ? this._config.texts[`${btnName}Active`] : this._config.texts[btnName];
|
||||
const icon = switchOn ? this._config.icons[`${btnName}Active`] : this._config.icons[btnName];
|
||||
const title = switchOn ? this._config.titles[`${btnName}Active`] : this._config.titles[btnName];
|
||||
|
@ -853,8 +850,8 @@ class ImageViewer {
|
|||
document.body.append(this._overlay);
|
||||
|
||||
//Instantiate panzoom if needed
|
||||
if (this._config.panzoom) {
|
||||
this._pzInstance = this._pzInstance ?? Panzoom(this._imgDisplay, { disableZoom: true, noBind: true });
|
||||
if (typeof this._config.panzoom === "function") {
|
||||
this._pzInstance = this._pzInstance ?? this._config.panzoom(this._imgDisplay, { disableZoom: true, noBind: true });
|
||||
//Even though only instantiated, the classes are set so we do a full switch off for the initial state
|
||||
this.zoomToggle(false);
|
||||
}
|
||||
|
@ -917,7 +914,12 @@ class ImageViewer {
|
|||
controls.append(makeButton(b, "", this._config.texts[b], this._config.titles[b], this._config.icons[b], this));
|
||||
}
|
||||
|
||||
if (this._config.panzoom) controls.append(makeButton("zoom", "", this._config.texts.zoom, this._config.titles.zoom, this._config.icons.zoom, this));
|
||||
if (typeof this._config.panzoom === "function") {
|
||||
controls.append(makeButton("zoom", "", this._config.texts.zoom, this._config.titles.zoom, this._config.icons.zoom, this));
|
||||
} else {
|
||||
console.log(`No Panzoom: ${typeof this._config.panzoom}`);
|
||||
console.log(this._config.panzoom);
|
||||
}
|
||||
|
||||
for (const a of anchors) {
|
||||
if (this._config[`show${a.charAt(0).toUpperCase() + a.slice(1)}`]) {
|
||||
|
@ -961,8 +963,7 @@ class ImageViewer {
|
|||
}
|
||||
|
||||
|
||||
if (this._config.panzoom) {
|
||||
console.log(`Shown: ${img.width}; Actual: ${img.naturalWidth}`);
|
||||
if (typeof this._config.panzoom === "function") {
|
||||
const btnZoom = document.getElementById("btn-zoom");
|
||||
if(img.width < img.naturalWidth) {
|
||||
btnZoom.disabled = false;
|
||||
|
@ -985,7 +986,7 @@ class ImageViewer {
|
|||
"ArrowLeft": "prev",
|
||||
"ArrowRight": "next"
|
||||
};
|
||||
if (keys.hasOwnProperty(e.key) && typeof this[keys[e.key]] === "function") {
|
||||
if (Object.hasOwn(keys, e.key) && typeof this[keys[e.key]] === "function") {
|
||||
e.preventDefault();
|
||||
this[keys[e.key]]();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { ImageViewer } from './ila-ui.esm.js';
|
||||
import Panzoom from './panzoom.es.js';
|
||||
|
||||
const iv = new ImageViewer({
|
||||
showDownload: true,
|
||||
showLink: false,
|
||||
panzoom: Panzoom,
|
||||
texts: {
|
||||
cue: "",
|
||||
hide: "",
|
||||
|
|
|
@ -0,0 +1,784 @@
|
|||
/**
|
||||
* Panzoom for panning and zooming elements using CSS transforms
|
||||
* Copyright Timmy Willison and other contributors
|
||||
* https://github.com/timmywil/panzoom/blob/main/MIT-License.txt
|
||||
*/
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
var __assign = function() {
|
||||
__assign = Object.assign || function __assign(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
|
||||
/* eslint-disable no-var */
|
||||
if (typeof window !== 'undefined') {
|
||||
// Support: IE11 only
|
||||
if (window.NodeList && !NodeList.prototype.forEach) {
|
||||
NodeList.prototype.forEach = Array.prototype.forEach;
|
||||
}
|
||||
// Support: IE11 only
|
||||
// CustomEvent is an object instead of a constructor
|
||||
if (typeof window.CustomEvent !== 'function') {
|
||||
window.CustomEvent = function CustomEvent(event, params) {
|
||||
params = params || { bubbles: false, cancelable: false, detail: null };
|
||||
var evt = document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
||||
return evt
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilites for working with multiple pointer events
|
||||
*/
|
||||
function findEventIndex(pointers, event) {
|
||||
var i = pointers.length;
|
||||
while (i--) {
|
||||
if (pointers[i].pointerId === event.pointerId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function addPointer(pointers, event) {
|
||||
var i;
|
||||
// Add touches if applicable
|
||||
if (event.touches) {
|
||||
i = 0;
|
||||
for (var _i = 0, _a = event.touches; _i < _a.length; _i++) {
|
||||
var touch = _a[_i];
|
||||
touch.pointerId = i++;
|
||||
addPointer(pointers, touch);
|
||||
}
|
||||
return;
|
||||
}
|
||||
i = findEventIndex(pointers, event);
|
||||
// Update if already present
|
||||
if (i > -1) {
|
||||
pointers.splice(i, 1);
|
||||
}
|
||||
pointers.push(event);
|
||||
}
|
||||
function removePointer(pointers, event) {
|
||||
// Add touches if applicable
|
||||
if (event.touches) {
|
||||
// Remove all touches
|
||||
while (pointers.length) {
|
||||
pointers.pop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
var i = findEventIndex(pointers, event);
|
||||
if (i > -1) {
|
||||
pointers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Calculates a center point between
|
||||
* the given pointer events, for panning
|
||||
* with multiple pointers.
|
||||
*/
|
||||
function getMiddle(pointers) {
|
||||
// Copy to avoid changing by reference
|
||||
pointers = pointers.slice(0);
|
||||
var event1 = pointers.pop();
|
||||
var event2;
|
||||
while ((event2 = pointers.pop())) {
|
||||
event1 = {
|
||||
clientX: (event2.clientX - event1.clientX) / 2 + event1.clientX,
|
||||
clientY: (event2.clientY - event1.clientY) / 2 + event1.clientY
|
||||
};
|
||||
}
|
||||
return event1;
|
||||
}
|
||||
/**
|
||||
* Calculates the distance between two points
|
||||
* for pinch zooming.
|
||||
* Limits to the first 2
|
||||
*/
|
||||
function getDistance(pointers) {
|
||||
if (pointers.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
var event1 = pointers[0];
|
||||
var event2 = pointers[1];
|
||||
return Math.sqrt(Math.pow(Math.abs(event2.clientX - event1.clientX), 2) +
|
||||
Math.pow(Math.abs(event2.clientY - event1.clientY), 2));
|
||||
}
|
||||
|
||||
var events = {
|
||||
down: 'mousedown',
|
||||
move: 'mousemove',
|
||||
up: 'mouseup mouseleave'
|
||||
};
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window.PointerEvent === 'function') {
|
||||
events = {
|
||||
down: 'pointerdown',
|
||||
move: 'pointermove',
|
||||
up: 'pointerup pointerleave pointercancel'
|
||||
};
|
||||
}
|
||||
else if (typeof window.TouchEvent === 'function') {
|
||||
events = {
|
||||
down: 'touchstart',
|
||||
move: 'touchmove',
|
||||
up: 'touchend touchcancel'
|
||||
};
|
||||
}
|
||||
}
|
||||
function onPointer(event, elem, handler, eventOpts) {
|
||||
events[event].split(' ').forEach(function (name) {
|
||||
elem.addEventListener(name, handler, eventOpts);
|
||||
});
|
||||
}
|
||||
function destroyPointer(event, elem, handler) {
|
||||
events[event].split(' ').forEach(function (name) {
|
||||
elem.removeEventListener(name, handler);
|
||||
});
|
||||
}
|
||||
|
||||
var isIE = typeof document !== 'undefined' && !!document.documentMode;
|
||||
/**
|
||||
* Lazy creation of a CSS style declaration
|
||||
*/
|
||||
var divStyle;
|
||||
function createStyle() {
|
||||
if (divStyle) {
|
||||
return divStyle;
|
||||
}
|
||||
return (divStyle = document.createElement('div').style);
|
||||
}
|
||||
/**
|
||||
* Proper prefixing for cross-browser compatibility
|
||||
*/
|
||||
var prefixes = ['webkit', 'moz', 'ms'];
|
||||
var prefixCache = {};
|
||||
function getPrefixedName(name) {
|
||||
if (prefixCache[name]) {
|
||||
return prefixCache[name];
|
||||
}
|
||||
var divStyle = createStyle();
|
||||
if (name in divStyle) {
|
||||
return (prefixCache[name] = name);
|
||||
}
|
||||
var capName = name[0].toUpperCase() + name.slice(1);
|
||||
var i = prefixes.length;
|
||||
while (i--) {
|
||||
var prefixedName = "".concat(prefixes[i]).concat(capName);
|
||||
if (prefixedName in divStyle) {
|
||||
return (prefixCache[name] = prefixedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets a style value expected to be a number
|
||||
*/
|
||||
function getCSSNum(name, style) {
|
||||
return parseFloat(style[getPrefixedName(name)]) || 0;
|
||||
}
|
||||
function getBoxStyle(elem, name, style) {
|
||||
if (style === void 0) { style = window.getComputedStyle(elem); }
|
||||
// Support: FF 68+
|
||||
// Firefox requires specificity for border
|
||||
var suffix = name === 'border' ? 'Width' : '';
|
||||
return {
|
||||
left: getCSSNum("".concat(name, "Left").concat(suffix), style),
|
||||
right: getCSSNum("".concat(name, "Right").concat(suffix), style),
|
||||
top: getCSSNum("".concat(name, "Top").concat(suffix), style),
|
||||
bottom: getCSSNum("".concat(name, "Bottom").concat(suffix), style)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Set a style using the properly prefixed name
|
||||
*/
|
||||
function setStyle(elem, name, value) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
elem.style[getPrefixedName(name)] = value;
|
||||
}
|
||||
/**
|
||||
* Constructs the transition from panzoom options
|
||||
* and takes care of prefixing the transition and transform
|
||||
*/
|
||||
function setTransition(elem, options) {
|
||||
var transform = getPrefixedName('transform');
|
||||
setStyle(elem, 'transition', "".concat(transform, " ").concat(options.duration, "ms ").concat(options.easing));
|
||||
}
|
||||
/**
|
||||
* Set the transform using the proper prefix
|
||||
*
|
||||
* Override the transform setter.
|
||||
* This is exposed mostly so the user could
|
||||
* set other parts of a transform
|
||||
* aside from scale and translate.
|
||||
* Default is defined in src/css.ts.
|
||||
*
|
||||
* ```js
|
||||
* // This example always sets a rotation
|
||||
* // when setting the scale and translation
|
||||
* const panzoom = Panzoom(elem, {
|
||||
* setTransform: (elem, { scale, x, y }) => {
|
||||
* panzoom.setStyle('transform', `rotate(0.5turn) scale(${scale}) translate(${x}px, ${y}px)`)
|
||||
* }
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
function setTransform(elem, _a, _options) {
|
||||
var x = _a.x, y = _a.y, scale = _a.scale, isSVG = _a.isSVG;
|
||||
setStyle(elem, 'transform', "scale(".concat(scale, ") translate(").concat(x, "px, ").concat(y, "px)"));
|
||||
if (isSVG && isIE) {
|
||||
var matrixValue = window.getComputedStyle(elem).getPropertyValue('transform');
|
||||
elem.setAttribute('transform', matrixValue);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Dimensions used in containment and focal point zooming
|
||||
*/
|
||||
function getDimensions(elem) {
|
||||
var parent = elem.parentNode;
|
||||
var style = window.getComputedStyle(elem);
|
||||
var parentStyle = window.getComputedStyle(parent);
|
||||
var rectElem = elem.getBoundingClientRect();
|
||||
var rectParent = parent.getBoundingClientRect();
|
||||
return {
|
||||
elem: {
|
||||
style: style,
|
||||
width: rectElem.width,
|
||||
height: rectElem.height,
|
||||
top: rectElem.top,
|
||||
bottom: rectElem.bottom,
|
||||
left: rectElem.left,
|
||||
right: rectElem.right,
|
||||
margin: getBoxStyle(elem, 'margin', style),
|
||||
border: getBoxStyle(elem, 'border', style)
|
||||
},
|
||||
parent: {
|
||||
style: parentStyle,
|
||||
width: rectParent.width,
|
||||
height: rectParent.height,
|
||||
top: rectParent.top,
|
||||
bottom: rectParent.bottom,
|
||||
left: rectParent.left,
|
||||
right: rectParent.right,
|
||||
padding: getBoxStyle(parent, 'padding', parentStyle),
|
||||
border: getBoxStyle(parent, 'border', parentStyle)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an element is attached to the DOM
|
||||
* Panzoom requires this so events work properly
|
||||
*/
|
||||
function isAttached(elem) {
|
||||
var doc = elem.ownerDocument;
|
||||
var parent = elem.parentNode;
|
||||
return (doc &&
|
||||
parent &&
|
||||
doc.nodeType === 9 &&
|
||||
parent.nodeType === 1 &&
|
||||
doc.documentElement.contains(parent));
|
||||
}
|
||||
|
||||
function getClass(elem) {
|
||||
return (elem.getAttribute('class') || '').trim();
|
||||
}
|
||||
function hasClass(elem, className) {
|
||||
return elem.nodeType === 1 && " ".concat(getClass(elem), " ").indexOf(" ".concat(className, " ")) > -1;
|
||||
}
|
||||
function isExcluded(elem, options) {
|
||||
for (var cur = elem; cur != null; cur = cur.parentNode) {
|
||||
if (hasClass(cur, options.excludeClass) || options.exclude.indexOf(cur) > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an element is SVG by checking the namespace
|
||||
* Exception: the <svg> element itself should be treated like HTML
|
||||
*/
|
||||
var rsvg = /^http:[\w\.\/]+svg$/;
|
||||
function isSVGElement(elem) {
|
||||
return rsvg.test(elem.namespaceURI) && elem.nodeName.toLowerCase() !== 'svg';
|
||||
}
|
||||
|
||||
function shallowClone(obj) {
|
||||
var clone = {};
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
clone[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
var defaultOptions = {
|
||||
animate: false,
|
||||
canvas: false,
|
||||
cursor: 'move',
|
||||
disablePan: false,
|
||||
disableZoom: false,
|
||||
disableXAxis: false,
|
||||
disableYAxis: false,
|
||||
duration: 200,
|
||||
easing: 'ease-in-out',
|
||||
exclude: [],
|
||||
excludeClass: 'panzoom-exclude',
|
||||
handleStartEvent: function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
maxScale: 4,
|
||||
minScale: 0.125,
|
||||
overflow: 'hidden',
|
||||
panOnlyWhenZoomed: false,
|
||||
pinchAndPan: false,
|
||||
relative: false,
|
||||
setTransform: setTransform,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
startScale: 1,
|
||||
step: 0.3,
|
||||
touchAction: 'none'
|
||||
};
|
||||
function Panzoom(elem, options) {
|
||||
if (!elem) {
|
||||
throw new Error('Panzoom requires an element as an argument');
|
||||
}
|
||||
if (elem.nodeType !== 1) {
|
||||
throw new Error('Panzoom requires an element with a nodeType of 1');
|
||||
}
|
||||
if (!isAttached(elem)) {
|
||||
throw new Error('Panzoom should be called on elements that have been attached to the DOM');
|
||||
}
|
||||
options = __assign(__assign({}, defaultOptions), options);
|
||||
var isSVG = isSVGElement(elem);
|
||||
var parent = elem.parentNode;
|
||||
// Set parent styles
|
||||
parent.style.overflow = options.overflow;
|
||||
parent.style.userSelect = 'none';
|
||||
// This is important for mobile to
|
||||
// prevent scrolling while panning
|
||||
parent.style.touchAction = options.touchAction;
|
||||
(options.canvas ? parent : elem).style.cursor = options.cursor;
|
||||
// Set element styles
|
||||
elem.style.userSelect = 'none';
|
||||
elem.style.touchAction = options.touchAction;
|
||||
// The default for HTML is '50% 50%'
|
||||
// The default for SVG is '0 0'
|
||||
// SVG can't be changed in IE
|
||||
setStyle(elem, 'transformOrigin', typeof options.origin === 'string' ? options.origin : isSVG ? '0 0' : '50% 50%');
|
||||
function resetStyle() {
|
||||
parent.style.overflow = '';
|
||||
parent.style.userSelect = '';
|
||||
parent.style.touchAction = '';
|
||||
parent.style.cursor = '';
|
||||
elem.style.cursor = '';
|
||||
elem.style.userSelect = '';
|
||||
elem.style.touchAction = '';
|
||||
setStyle(elem, 'transformOrigin', '');
|
||||
}
|
||||
function setOptions(opts) {
|
||||
if (opts === void 0) { opts = {}; }
|
||||
for (var key in opts) {
|
||||
if (opts.hasOwnProperty(key)) {
|
||||
options[key] = opts[key];
|
||||
}
|
||||
}
|
||||
// Handle option side-effects
|
||||
if (opts.hasOwnProperty('cursor') || opts.hasOwnProperty('canvas')) {
|
||||
parent.style.cursor = elem.style.cursor = '';
|
||||
(options.canvas ? parent : elem).style.cursor = options.cursor;
|
||||
}
|
||||
if (opts.hasOwnProperty('overflow')) {
|
||||
parent.style.overflow = opts.overflow;
|
||||
}
|
||||
if (opts.hasOwnProperty('touchAction')) {
|
||||
parent.style.touchAction = opts.touchAction;
|
||||
elem.style.touchAction = opts.touchAction;
|
||||
}
|
||||
}
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var scale = 1;
|
||||
var isPanning = false;
|
||||
zoom(options.startScale, { animate: false, force: true });
|
||||
// Wait for scale to update
|
||||
// for accurate dimensions
|
||||
// to constrain initial values
|
||||
setTimeout(function () {
|
||||
pan(options.startX, options.startY, { animate: false, force: true });
|
||||
});
|
||||
function trigger(eventName, detail, opts) {
|
||||
if (opts.silent) {
|
||||
return;
|
||||
}
|
||||
var event = new CustomEvent(eventName, { detail: detail });
|
||||
elem.dispatchEvent(event);
|
||||
}
|
||||
function setTransformWithEvent(eventName, opts, originalEvent) {
|
||||
var value = { x: x, y: y, scale: scale, isSVG: isSVG, originalEvent: originalEvent };
|
||||
requestAnimationFrame(function () {
|
||||
if (typeof opts.animate === 'boolean') {
|
||||
if (opts.animate) {
|
||||
setTransition(elem, opts);
|
||||
}
|
||||
else {
|
||||
setStyle(elem, 'transition', 'none');
|
||||
}
|
||||
}
|
||||
opts.setTransform(elem, value, opts);
|
||||
trigger(eventName, value, opts);
|
||||
trigger('panzoomchange', value, opts);
|
||||
});
|
||||
return value;
|
||||
}
|
||||
function constrainXY(toX, toY, toScale, panOptions) {
|
||||
var opts = __assign(__assign({}, options), panOptions);
|
||||
var result = { x: x, y: y, opts: opts };
|
||||
if (!opts.force && (opts.disablePan || (opts.panOnlyWhenZoomed && scale === opts.startScale))) {
|
||||
return result;
|
||||
}
|
||||
toX = parseFloat(toX);
|
||||
toY = parseFloat(toY);
|
||||
if (!opts.disableXAxis) {
|
||||
result.x = (opts.relative ? x : 0) + toX;
|
||||
}
|
||||
if (!opts.disableYAxis) {
|
||||
result.y = (opts.relative ? y : 0) + toY;
|
||||
}
|
||||
if (opts.contain) {
|
||||
var dims = getDimensions(elem);
|
||||
var realWidth = dims.elem.width / scale;
|
||||
var realHeight = dims.elem.height / scale;
|
||||
var scaledWidth = realWidth * toScale;
|
||||
var scaledHeight = realHeight * toScale;
|
||||
var diffHorizontal = (scaledWidth - realWidth) / 2;
|
||||
var diffVertical = (scaledHeight - realHeight) / 2;
|
||||
if (opts.contain === 'inside') {
|
||||
var minX = (-dims.elem.margin.left - dims.parent.padding.left + diffHorizontal) / toScale;
|
||||
var maxX = (dims.parent.width -
|
||||
scaledWidth -
|
||||
dims.parent.padding.left -
|
||||
dims.elem.margin.left -
|
||||
dims.parent.border.left -
|
||||
dims.parent.border.right +
|
||||
diffHorizontal) /
|
||||
toScale;
|
||||
result.x = Math.max(Math.min(result.x, maxX), minX);
|
||||
var minY = (-dims.elem.margin.top - dims.parent.padding.top + diffVertical) / toScale;
|
||||
var maxY = (dims.parent.height -
|
||||
scaledHeight -
|
||||
dims.parent.padding.top -
|
||||
dims.elem.margin.top -
|
||||
dims.parent.border.top -
|
||||
dims.parent.border.bottom +
|
||||
diffVertical) /
|
||||
toScale;
|
||||
result.y = Math.max(Math.min(result.y, maxY), minY);
|
||||
}
|
||||
else if (opts.contain === 'outside') {
|
||||
var minX = (-(scaledWidth - dims.parent.width) -
|
||||
dims.parent.padding.left -
|
||||
dims.parent.border.left -
|
||||
dims.parent.border.right +
|
||||
diffHorizontal) /
|
||||
toScale;
|
||||
var maxX = (diffHorizontal - dims.parent.padding.left) / toScale;
|
||||
result.x = Math.max(Math.min(result.x, maxX), minX);
|
||||
var minY = (-(scaledHeight - dims.parent.height) -
|
||||
dims.parent.padding.top -
|
||||
dims.parent.border.top -
|
||||
dims.parent.border.bottom +
|
||||
diffVertical) /
|
||||
toScale;
|
||||
var maxY = (diffVertical - dims.parent.padding.top) / toScale;
|
||||
result.y = Math.max(Math.min(result.y, maxY), minY);
|
||||
}
|
||||
}
|
||||
if (opts.roundPixels) {
|
||||
result.x = Math.round(result.x);
|
||||
result.y = Math.round(result.y);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function constrainScale(toScale, zoomOptions) {
|
||||
var opts = __assign(__assign({}, options), zoomOptions);
|
||||
var result = { scale: scale, opts: opts };
|
||||
if (!opts.force && opts.disableZoom) {
|
||||
return result;
|
||||
}
|
||||
var minScale = options.minScale;
|
||||
var maxScale = options.maxScale;
|
||||
if (opts.contain) {
|
||||
var dims = getDimensions(elem);
|
||||
var elemWidth = dims.elem.width / scale;
|
||||
var elemHeight = dims.elem.height / scale;
|
||||
if (elemWidth > 1 && elemHeight > 1) {
|
||||
var parentWidth = dims.parent.width - dims.parent.border.left - dims.parent.border.right;
|
||||
var parentHeight = dims.parent.height - dims.parent.border.top - dims.parent.border.bottom;
|
||||
var elemScaledWidth = parentWidth / elemWidth;
|
||||
var elemScaledHeight = parentHeight / elemHeight;
|
||||
if (options.contain === 'inside') {
|
||||
maxScale = Math.min(maxScale, elemScaledWidth, elemScaledHeight);
|
||||
}
|
||||
else if (options.contain === 'outside') {
|
||||
minScale = Math.max(minScale, elemScaledWidth, elemScaledHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.scale = Math.min(Math.max(toScale, minScale), maxScale);
|
||||
return result;
|
||||
}
|
||||
function pan(toX, toY, panOptions, originalEvent) {
|
||||
var result = constrainXY(toX, toY, scale, panOptions);
|
||||
// Only try to set if the result is somehow different
|
||||
if (x !== result.x || y !== result.y) {
|
||||
x = result.x;
|
||||
y = result.y;
|
||||
return setTransformWithEvent('panzoompan', result.opts, originalEvent);
|
||||
}
|
||||
return { x: x, y: y, scale: scale, isSVG: isSVG, originalEvent: originalEvent };
|
||||
}
|
||||
function zoom(toScale, zoomOptions, originalEvent) {
|
||||
var result = constrainScale(toScale, zoomOptions);
|
||||
var opts = result.opts;
|
||||
if (!opts.force && opts.disableZoom) {
|
||||
return;
|
||||
}
|
||||
toScale = result.scale;
|
||||
var toX = x;
|
||||
var toY = y;
|
||||
if (opts.focal) {
|
||||
// The difference between the point after the scale and the point before the scale
|
||||
// plus the current translation after the scale
|
||||
// neutralized to no scale (as the transform scale will apply to the translation)
|
||||
var focal = opts.focal;
|
||||
toX = (focal.x / toScale - focal.x / scale + x * toScale) / toScale;
|
||||
toY = (focal.y / toScale - focal.y / scale + y * toScale) / toScale;
|
||||
}
|
||||
var panResult = constrainXY(toX, toY, toScale, { relative: false, force: true });
|
||||
x = panResult.x;
|
||||
y = panResult.y;
|
||||
scale = toScale;
|
||||
return setTransformWithEvent('panzoomzoom', opts, originalEvent);
|
||||
}
|
||||
function zoomInOut(isIn, zoomOptions) {
|
||||
var opts = __assign(__assign(__assign({}, options), { animate: true }), zoomOptions);
|
||||
return zoom(scale * Math.exp((isIn ? 1 : -1) * opts.step), opts);
|
||||
}
|
||||
function zoomIn(zoomOptions) {
|
||||
return zoomInOut(true, zoomOptions);
|
||||
}
|
||||
function zoomOut(zoomOptions) {
|
||||
return zoomInOut(false, zoomOptions);
|
||||
}
|
||||
function zoomToPoint(toScale, point, zoomOptions, originalEvent) {
|
||||
var dims = getDimensions(elem);
|
||||
// Instead of thinking of operating on the panzoom element,
|
||||
// think of operating on the area inside the panzoom
|
||||
// element's parent
|
||||
// Subtract padding and border
|
||||
var effectiveArea = {
|
||||
width: dims.parent.width -
|
||||
dims.parent.padding.left -
|
||||
dims.parent.padding.right -
|
||||
dims.parent.border.left -
|
||||
dims.parent.border.right,
|
||||
height: dims.parent.height -
|
||||
dims.parent.padding.top -
|
||||
dims.parent.padding.bottom -
|
||||
dims.parent.border.top -
|
||||
dims.parent.border.bottom
|
||||
};
|
||||
// Adjust the clientX/clientY to ignore the area
|
||||
// outside the effective area
|
||||
var clientX = point.clientX -
|
||||
dims.parent.left -
|
||||
dims.parent.padding.left -
|
||||
dims.parent.border.left -
|
||||
dims.elem.margin.left;
|
||||
var clientY = point.clientY -
|
||||
dims.parent.top -
|
||||
dims.parent.padding.top -
|
||||
dims.parent.border.top -
|
||||
dims.elem.margin.top;
|
||||
// Adjust the clientX/clientY for HTML elements,
|
||||
// because they have a transform-origin of 50% 50%
|
||||
if (!isSVG) {
|
||||
clientX -= dims.elem.width / scale / 2;
|
||||
clientY -= dims.elem.height / scale / 2;
|
||||
}
|
||||
// Convert the mouse point from it's position over the
|
||||
// effective area before the scale to the position
|
||||
// over the effective area after the scale.
|
||||
var focal = {
|
||||
x: (clientX / effectiveArea.width) * (effectiveArea.width * toScale),
|
||||
y: (clientY / effectiveArea.height) * (effectiveArea.height * toScale)
|
||||
};
|
||||
return zoom(toScale, __assign(__assign({}, zoomOptions), { animate: false, focal: focal }), originalEvent);
|
||||
}
|
||||
function zoomWithWheel(event, zoomOptions) {
|
||||
// Need to prevent the default here
|
||||
// or it conflicts with regular page scroll
|
||||
event.preventDefault();
|
||||
var opts = __assign(__assign(__assign({}, options), zoomOptions), { animate: false });
|
||||
// Normalize to deltaX in case shift modifier is used on Mac
|
||||
var delta = event.deltaY === 0 && event.deltaX ? event.deltaX : event.deltaY;
|
||||
var wheel = delta < 0 ? 1 : -1;
|
||||
var toScale = constrainScale(scale * Math.exp((wheel * opts.step) / 3), opts).scale;
|
||||
return zoomToPoint(toScale, event, opts, event);
|
||||
}
|
||||
function reset(resetOptions) {
|
||||
var opts = __assign(__assign(__assign({}, options), { animate: true, force: true }), resetOptions);
|
||||
scale = constrainScale(opts.startScale, opts).scale;
|
||||
var panResult = constrainXY(opts.startX, opts.startY, scale, opts);
|
||||
x = panResult.x;
|
||||
y = panResult.y;
|
||||
return setTransformWithEvent('panzoomreset', opts);
|
||||
}
|
||||
var origX;
|
||||
var origY;
|
||||
var startClientX;
|
||||
var startClientY;
|
||||
var startScale;
|
||||
var startDistance;
|
||||
var pointers = [];
|
||||
function handleDown(event) {
|
||||
// Don't handle this event if the target is excluded
|
||||
if (isExcluded(event.target, options)) {
|
||||
return;
|
||||
}
|
||||
addPointer(pointers, event);
|
||||
isPanning = true;
|
||||
options.handleStartEvent(event);
|
||||
origX = x;
|
||||
origY = y;
|
||||
trigger('panzoomstart', { x: x, y: y, scale: scale, isSVG: isSVG, originalEvent: event }, options);
|
||||
// This works whether there are multiple
|
||||
// pointers or not
|
||||
var point = getMiddle(pointers);
|
||||
startClientX = point.clientX;
|
||||
startClientY = point.clientY;
|
||||
startScale = scale;
|
||||
startDistance = getDistance(pointers);
|
||||
}
|
||||
function handleMove(event) {
|
||||
if (!isPanning ||
|
||||
origX === undefined ||
|
||||
origY === undefined ||
|
||||
startClientX === undefined ||
|
||||
startClientY === undefined) {
|
||||
return;
|
||||
}
|
||||
addPointer(pointers, event);
|
||||
var current = getMiddle(pointers);
|
||||
var hasMultiple = pointers.length > 1;
|
||||
var toScale = scale;
|
||||
if (hasMultiple) {
|
||||
// A startDistance of 0 means
|
||||
// that there weren't 2 pointers
|
||||
// handled on start
|
||||
if (startDistance === 0) {
|
||||
startDistance = getDistance(pointers);
|
||||
}
|
||||
// Use the distance between the first 2 pointers
|
||||
// to determine the current scale
|
||||
var diff = getDistance(pointers) - startDistance;
|
||||
toScale = constrainScale((diff * options.step) / 80 + startScale).scale;
|
||||
zoomToPoint(toScale, current, { animate: false }, event);
|
||||
}
|
||||
// Pan during pinch if pinchAndPan is true.
|
||||
// Note: some calculations may be off because the zoom
|
||||
// above has not yet rendered. However, the behavior
|
||||
// was removed before the new scale was used in the following
|
||||
// pan calculation.
|
||||
// See https://github.com/timmywil/panzoom/issues/512
|
||||
// and https://github.com/timmywil/panzoom/issues/606
|
||||
if (!hasMultiple || options.pinchAndPan) {
|
||||
pan(origX + (current.clientX - startClientX) / toScale, origY + (current.clientY - startClientY) / toScale, {
|
||||
animate: false
|
||||
}, event);
|
||||
}
|
||||
}
|
||||
function handleUp(event) {
|
||||
// Don't call panzoomend when panning with 2 touches
|
||||
// until both touches end
|
||||
if (pointers.length === 1) {
|
||||
trigger('panzoomend', { x: x, y: y, scale: scale, isSVG: isSVG, originalEvent: event }, options);
|
||||
}
|
||||
// Note: don't remove all pointers
|
||||
// Can restart without having to reinitiate all of them
|
||||
// Remove the pointer regardless of the isPanning state
|
||||
removePointer(pointers, event);
|
||||
if (!isPanning) {
|
||||
return;
|
||||
}
|
||||
isPanning = false;
|
||||
origX = origY = startClientX = startClientY = undefined;
|
||||
}
|
||||
var bound = false;
|
||||
function bind() {
|
||||
if (bound) {
|
||||
return;
|
||||
}
|
||||
bound = true;
|
||||
onPointer('down', options.canvas ? parent : elem, handleDown);
|
||||
onPointer('move', document, handleMove, { passive: true });
|
||||
onPointer('up', document, handleUp, { passive: true });
|
||||
}
|
||||
function destroy() {
|
||||
bound = false;
|
||||
destroyPointer('down', options.canvas ? parent : elem, handleDown);
|
||||
destroyPointer('move', document, handleMove);
|
||||
destroyPointer('up', document, handleUp);
|
||||
}
|
||||
if (!options.noBind) {
|
||||
bind();
|
||||
}
|
||||
return {
|
||||
bind: bind,
|
||||
destroy: destroy,
|
||||
eventNames: events,
|
||||
getPan: function () { return ({ x: x, y: y }); },
|
||||
getScale: function () { return scale; },
|
||||
getOptions: function () { return shallowClone(options); },
|
||||
handleDown: handleDown,
|
||||
handleMove: handleMove,
|
||||
handleUp: handleUp,
|
||||
pan: pan,
|
||||
reset: reset,
|
||||
resetStyle: resetStyle,
|
||||
setOptions: setOptions,
|
||||
setStyle: function (name, value) { return setStyle(elem, name, value); },
|
||||
zoom: zoom,
|
||||
zoomIn: zoomIn,
|
||||
zoomOut: zoomOut,
|
||||
zoomToPoint: zoomToPoint,
|
||||
zoomWithWheel: zoomWithWheel
|
||||
};
|
||||
}
|
||||
Panzoom.defaultOptions = defaultOptions;
|
||||
|
||||
export { Panzoom as default };
|
10
package.json
10
package.json
|
@ -2,11 +2,11 @@
|
|||
"name": "ILA Microblog.pub assets",
|
||||
"description": "ILA Microblog.pub assets",
|
||||
"scripts": {
|
||||
"js": "cp node_modules/ila-ui-elements/dist/ila-ui.esm.js app/static/"
|
||||
"js": "cp node_modules/ila-ui-elements/dist/ila-ui.esm.js app/static/ && cp node_modules/@panzoom/panzoom/dist/panzoom.es.js app/static"
|
||||
},
|
||||
"author": "Aonrud",
|
||||
"license": "",
|
||||
"dependencies": {
|
||||
"ila-ui-elements": "Aonrud/ila-ui-elements#semver:0.8.2"
|
||||
"devDependencies": {
|
||||
"@panzoom/panzoom": "^4.5.1",
|
||||
"ila-ui-elements": "git+https://git.aonghus.org/Irish-Left-Archive/ila-ui-elements.git#semver:0.9.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
lockfileVersion: 5.4
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
specifiers:
|
||||
ila-ui-elements: Aonrud/ila-ui-elements#semver:0.8.2
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
ila-ui-elements: github.com/Aonrud/ila-ui-elements/66bd1b0d5d7ff2b6e064e78f7db4c7522df0cd15
|
||||
devDependencies:
|
||||
'@panzoom/panzoom':
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1
|
||||
ila-ui-elements:
|
||||
specifier: git+https://git.aonghus.org/Irish-Left-Archive/ila-ui-elements.git#semver:0.9.0
|
||||
version: git.aonghus.org/Irish-Left-Archive/ila-ui-elements/3409cd600d42972200d5bc6f5e4bbf970e1ca0d6
|
||||
|
||||
packages:
|
||||
|
||||
github.com/Aonrud/ila-ui-elements/66bd1b0d5d7ff2b6e064e78f7db4c7522df0cd15:
|
||||
resolution: {tarball: https://codeload.github.com/Aonrud/ila-ui-elements/tar.gz/66bd1b0d5d7ff2b6e064e78f7db4c7522df0cd15}
|
||||
/@panzoom/panzoom@4.5.1:
|
||||
resolution: {integrity: sha512-QOr/t7314XTwgAUDazR+RDcTAWSbkpjDnZJddd9f56jSUA8ptUsyDblAb+sp/O5O1o5Fiu9KpWxVHKuhGUgp5w==}
|
||||
dev: true
|
||||
|
||||
git.aonghus.org/Irish-Left-Archive/ila-ui-elements/3409cd600d42972200d5bc6f5e4bbf970e1ca0d6:
|
||||
resolution: {commit: 3409cd600d42972200d5bc6f5e4bbf970e1ca0d6, repo: https://git.aonghus.org/Irish-Left-Archive/ila-ui-elements.git, type: git}
|
||||
name: ila_ui_elements
|
||||
version: 0.8.2
|
||||
dev: false
|
||||
version: 0.9.0
|
||||
dev: true
|
||||
|
|
Ładowanie…
Reference in New Issue