Compare commits
10 commits
68abb6a2b8
...
4741a2f932
Author | SHA1 | Date | |
---|---|---|---|
FloatingGhost | 4741a2f932 | ||
FloatingGhost | dc8dfa6a91 | ||
ff8e6f380e | |||
b5d2e9fb41 | |||
fbb4d71b1c | |||
de150c0105 | |||
a94fb753a1 | |||
390b0e0278 | |||
9f6bd1d6b0 | |||
f5866785e1 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
*.css.d.ts
|
||||
.rpt2_cache
|
||||
node_modules
|
||||
|
|
2
dist/pinch-zoom-min.js
vendored
2
dist/pinch-zoom-min.js
vendored
File diff suppressed because one or more lines are too long
147
dist/pinch-zoom.cjs.js
vendored
147
dist/pinch-zoom.cjs.js
vendored
|
@ -35,6 +35,11 @@ var css = "pinch-zoom {\n display: block;\n overflow: hidden;\n touch-action:
|
|||
styleInject(css);
|
||||
|
||||
const minScaleAttr = 'min-scale';
|
||||
const allowPanMinScaleAttr = 'allow-pan-min-scale';
|
||||
const resetToMinScaleLimitAttr = 'reset-to-min-scale-limit';
|
||||
const reachMinScaleStrategyAttr = 'reach-min-scale-strategy';
|
||||
const stopPropagateHandledAttr = 'stop-propagate-handled';
|
||||
const reachMinScaleStrategyDefault = 'none';
|
||||
function getDistance(a, b) {
|
||||
if (!b)
|
||||
return 0;
|
||||
|
@ -69,6 +74,12 @@ function createPoint() {
|
|||
return getSVG().createSVGPoint();
|
||||
}
|
||||
const MIN_SCALE = 0.01;
|
||||
const ALLOW_PAN_MIN_SCALE = -1;
|
||||
const RESET_TO_MIN_SCALE_LIMIT = -1;
|
||||
const BUTTON_LEFT = 0;
|
||||
const roundedCmp = (a, b) => {
|
||||
return Math.round(a * 100) - Math.round(b * 100);
|
||||
};
|
||||
class PinchZoom extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -85,14 +96,26 @@ class PinchZoom extends HTMLElement {
|
|||
// We only want to track 2 pointers at most
|
||||
if (pointerTracker.currentPointers.length === 2 || !this._positioningEl)
|
||||
return false;
|
||||
const isPan = pointerTracker.currentPointers.length + 1 === 1;
|
||||
const handled = !(isPan && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
if (!isPan) { // only cancel if something was propagated
|
||||
this._maybeEmitCancel([pointer, ...pointerTracker.currentPointers]);
|
||||
}
|
||||
}
|
||||
event.preventDefault();
|
||||
return true;
|
||||
},
|
||||
move: (previousPointers) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
||||
move: (previousPointers, _, event) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers, event);
|
||||
},
|
||||
end: (pointer, event) => {
|
||||
this._onPointerEnd(pointer, pointerTracker.currentPointers, event);
|
||||
},
|
||||
});
|
||||
this.addEventListener('wheel', event => this._onWheel(event));
|
||||
this.addEventListener('click', event => this._onClick(event, pointerTracker));
|
||||
}
|
||||
static get observedAttributes() { return [minScaleAttr]; }
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
|
@ -114,6 +137,49 @@ class PinchZoom extends HTMLElement {
|
|||
set minScale(value) {
|
||||
this.setAttribute(minScaleAttr, String(value));
|
||||
}
|
||||
get reachMinScaleStrategy() {
|
||||
const attrValue = this.getAttribute(reachMinScaleStrategyAttr);
|
||||
const v = attrValue;
|
||||
return v || reachMinScaleStrategyDefault;
|
||||
}
|
||||
set reachMinScaleStrategy(value) {
|
||||
this.setAttribute(reachMinScaleStrategyAttr, value);
|
||||
}
|
||||
get allowPanMinScale() {
|
||||
const attrValue = this.getAttribute(allowPanMinScaleAttr);
|
||||
if (!attrValue)
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(ALLOW_PAN_MIN_SCALE, value);
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
}
|
||||
set allowPanMinScale(value) {
|
||||
this.setAttribute(allowPanMinScaleAttr, String(value));
|
||||
}
|
||||
get resetToMinScaleLimit() {
|
||||
const attrValue = this.getAttribute(resetToMinScaleLimitAttr);
|
||||
if (!attrValue)
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(RESET_TO_MIN_SCALE_LIMIT, value);
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
}
|
||||
set resetToMinScaleLimit(value) {
|
||||
this.setAttribute(resetToMinScaleLimitAttr, String(value));
|
||||
}
|
||||
get stopPropagateHandled() {
|
||||
return this.hasAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
set stopPropagateHandled(value) {
|
||||
if (value) {
|
||||
this.setAttribute(stopPropagateHandledAttr, '');
|
||||
}
|
||||
else {
|
||||
this.removeAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this._stageElChange();
|
||||
}
|
||||
|
@ -215,8 +281,9 @@ class PinchZoom extends HTMLElement {
|
|||
*/
|
||||
_updateTransform(scale, x, y, allowChangeEvent) {
|
||||
// Avoid scaling to zero
|
||||
if (scale < this.minScale)
|
||||
return;
|
||||
if (scale < this.minScale) {
|
||||
scale = this.minScale;
|
||||
}
|
||||
// Return if there's no change
|
||||
if (scale === this.scale &&
|
||||
x === this.x &&
|
||||
|
@ -264,16 +331,24 @@ class PinchZoom extends HTMLElement {
|
|||
// ctrlKey is true when pinch-zooming on a trackpad.
|
||||
const divisor = ctrlKey ? 100 : 300;
|
||||
const scaleDiff = 1 - deltaY / divisor;
|
||||
const isZoomOut = scaleDiff < 1;
|
||||
this._applyChange({
|
||||
scaleDiff,
|
||||
originX: event.clientX - currentRect.left,
|
||||
originY: event.clientY - currentRect.top,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
if (isZoomOut) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
}
|
||||
_onPointerMove(previousPointers, currentPointers) {
|
||||
_onPointerMove(previousPointers, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const isPan = previousPointers.length < 2;
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
// Combine next points with previous points
|
||||
const currentRect = this._positioningEl.getBoundingClientRect();
|
||||
// For calculating panning movement
|
||||
|
@ -292,6 +367,34 @@ class PinchZoom extends HTMLElement {
|
|||
panY: newMidpoint.clientY - prevMidpoint.clientY,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_maybeResetScale() {
|
||||
if (roundedCmp(this.scale, this.resetToMinScaleLimit) <= 0) {
|
||||
this._resetToMinScale();
|
||||
}
|
||||
}
|
||||
_onPointerEnd(pointer, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const totalPointers = 1 + currentPointers.length;
|
||||
const isPinch = totalPointers >= 2;
|
||||
const isPan = totalPointers == 1;
|
||||
if (isPinch) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_resetToMinScale() {
|
||||
if (this.reachMinScaleStrategy === 'reset') {
|
||||
this.setTransform({ scale: this.minScale, x: 0, y: 0 });
|
||||
}
|
||||
else {
|
||||
this.setTransform({ scale: this.minScale });
|
||||
}
|
||||
}
|
||||
/** Transform the view & fire a change event */
|
||||
_applyChange(opts = {}) {
|
||||
|
@ -315,6 +418,40 @@ class PinchZoom extends HTMLElement {
|
|||
y: matrix.f,
|
||||
});
|
||||
}
|
||||
_maybeStopPropagate(event) {
|
||||
if (this.stopPropagateHandled) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
_allowPan() {
|
||||
return (this.allowPanMinScale > 0
|
||||
&& roundedCmp(this.scale, this.allowPanMinScale) > 0);
|
||||
}
|
||||
_maybeEmitCancel(pointers) {
|
||||
const makeCancelEvent = (pointer) => (new PointerEvent('pointercancel', {
|
||||
pointerId: pointer.id,
|
||||
clientX: pointer.clientX,
|
||||
clientY: pointer.clientY,
|
||||
}));
|
||||
if (this.stopPropagateHandled) {
|
||||
pointers.forEach(p => {
|
||||
if (this.parentElement && typeof this.parentElement.dispatchEvent === 'function') {
|
||||
this.parentElement.dispatchEvent(makeCancelEvent(p));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_onClick(event, pointerTracker) {
|
||||
// We never handle non-left-clicks
|
||||
if (event.button !== BUTTON_LEFT) {
|
||||
return;
|
||||
}
|
||||
const wasPanning = pointerTracker.currentPointers.length === 0;
|
||||
const handled = !(wasPanning && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('pinch-zoom', PinchZoom);
|
||||
|
|
14
dist/pinch-zoom.css
vendored
Normal file
14
dist/pinch-zoom.css
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
pinch-zoom {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
touch-action: none;
|
||||
--scale: 1;
|
||||
--x: 0;
|
||||
--y: 0;
|
||||
}
|
||||
|
||||
pinch-zoom > * {
|
||||
transform: translate(var(--x), var(--y)) scale(var(--scale));
|
||||
transform-origin: 0 0;
|
||||
will-change: transform;
|
||||
}
|
12
dist/pinch-zoom.d.ts
vendored
12
dist/pinch-zoom.d.ts
vendored
|
@ -11,6 +11,7 @@ interface SetTransformOpts extends ChangeOptions {
|
|||
y?: number;
|
||||
}
|
||||
declare type ScaleRelativeToValues = 'container' | 'content';
|
||||
declare type ReachMinScaleStrategy = 'reset' | 'none';
|
||||
export interface ScaleToOpts extends ChangeOptions {
|
||||
/** Transform origin. Can be a number, or string percent, eg "50%" */
|
||||
originX?: number | string;
|
||||
|
@ -26,6 +27,10 @@ export default class PinchZoom extends HTMLElement {
|
|||
constructor();
|
||||
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
||||
minScale: number;
|
||||
reachMinScaleStrategy: ReachMinScaleStrategy;
|
||||
allowPanMinScale: number;
|
||||
resetToMinScaleLimit: number;
|
||||
stopPropagateHandled: boolean;
|
||||
connectedCallback(): void;
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
|
@ -51,7 +56,14 @@ export default class PinchZoom extends HTMLElement {
|
|||
private _stageElChange;
|
||||
private _onWheel;
|
||||
private _onPointerMove;
|
||||
private _maybeResetScale;
|
||||
private _onPointerEnd;
|
||||
private _resetToMinScale;
|
||||
/** Transform the view & fire a change event */
|
||||
private _applyChange;
|
||||
private _maybeStopPropagate;
|
||||
private _allowPan;
|
||||
private _maybeEmitCancel;
|
||||
private _onClick;
|
||||
}
|
||||
export {};
|
||||
|
|
147
dist/pinch-zoom.es.js
vendored
147
dist/pinch-zoom.es.js
vendored
|
@ -31,6 +31,11 @@ var css = "pinch-zoom {\n display: block;\n overflow: hidden;\n touch-action:
|
|||
styleInject(css);
|
||||
|
||||
const minScaleAttr = 'min-scale';
|
||||
const allowPanMinScaleAttr = 'allow-pan-min-scale';
|
||||
const resetToMinScaleLimitAttr = 'reset-to-min-scale-limit';
|
||||
const reachMinScaleStrategyAttr = 'reach-min-scale-strategy';
|
||||
const stopPropagateHandledAttr = 'stop-propagate-handled';
|
||||
const reachMinScaleStrategyDefault = 'none';
|
||||
function getDistance(a, b) {
|
||||
if (!b)
|
||||
return 0;
|
||||
|
@ -65,6 +70,12 @@ function createPoint() {
|
|||
return getSVG().createSVGPoint();
|
||||
}
|
||||
const MIN_SCALE = 0.01;
|
||||
const ALLOW_PAN_MIN_SCALE = -1;
|
||||
const RESET_TO_MIN_SCALE_LIMIT = -1;
|
||||
const BUTTON_LEFT = 0;
|
||||
const roundedCmp = (a, b) => {
|
||||
return Math.round(a * 100) - Math.round(b * 100);
|
||||
};
|
||||
class PinchZoom extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -81,14 +92,26 @@ class PinchZoom extends HTMLElement {
|
|||
// We only want to track 2 pointers at most
|
||||
if (pointerTracker.currentPointers.length === 2 || !this._positioningEl)
|
||||
return false;
|
||||
const isPan = pointerTracker.currentPointers.length + 1 === 1;
|
||||
const handled = !(isPan && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
if (!isPan) { // only cancel if something was propagated
|
||||
this._maybeEmitCancel([pointer, ...pointerTracker.currentPointers]);
|
||||
}
|
||||
}
|
||||
event.preventDefault();
|
||||
return true;
|
||||
},
|
||||
move: (previousPointers) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
||||
move: (previousPointers, _, event) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers, event);
|
||||
},
|
||||
end: (pointer, event) => {
|
||||
this._onPointerEnd(pointer, pointerTracker.currentPointers, event);
|
||||
},
|
||||
});
|
||||
this.addEventListener('wheel', event => this._onWheel(event));
|
||||
this.addEventListener('click', event => this._onClick(event, pointerTracker));
|
||||
}
|
||||
static get observedAttributes() { return [minScaleAttr]; }
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
|
@ -110,6 +133,49 @@ class PinchZoom extends HTMLElement {
|
|||
set minScale(value) {
|
||||
this.setAttribute(minScaleAttr, String(value));
|
||||
}
|
||||
get reachMinScaleStrategy() {
|
||||
const attrValue = this.getAttribute(reachMinScaleStrategyAttr);
|
||||
const v = attrValue;
|
||||
return v || reachMinScaleStrategyDefault;
|
||||
}
|
||||
set reachMinScaleStrategy(value) {
|
||||
this.setAttribute(reachMinScaleStrategyAttr, value);
|
||||
}
|
||||
get allowPanMinScale() {
|
||||
const attrValue = this.getAttribute(allowPanMinScaleAttr);
|
||||
if (!attrValue)
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(ALLOW_PAN_MIN_SCALE, value);
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
}
|
||||
set allowPanMinScale(value) {
|
||||
this.setAttribute(allowPanMinScaleAttr, String(value));
|
||||
}
|
||||
get resetToMinScaleLimit() {
|
||||
const attrValue = this.getAttribute(resetToMinScaleLimitAttr);
|
||||
if (!attrValue)
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(RESET_TO_MIN_SCALE_LIMIT, value);
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
}
|
||||
set resetToMinScaleLimit(value) {
|
||||
this.setAttribute(resetToMinScaleLimitAttr, String(value));
|
||||
}
|
||||
get stopPropagateHandled() {
|
||||
return this.hasAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
set stopPropagateHandled(value) {
|
||||
if (value) {
|
||||
this.setAttribute(stopPropagateHandledAttr, '');
|
||||
}
|
||||
else {
|
||||
this.removeAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this._stageElChange();
|
||||
}
|
||||
|
@ -211,8 +277,9 @@ class PinchZoom extends HTMLElement {
|
|||
*/
|
||||
_updateTransform(scale, x, y, allowChangeEvent) {
|
||||
// Avoid scaling to zero
|
||||
if (scale < this.minScale)
|
||||
return;
|
||||
if (scale < this.minScale) {
|
||||
scale = this.minScale;
|
||||
}
|
||||
// Return if there's no change
|
||||
if (scale === this.scale &&
|
||||
x === this.x &&
|
||||
|
@ -260,16 +327,24 @@ class PinchZoom extends HTMLElement {
|
|||
// ctrlKey is true when pinch-zooming on a trackpad.
|
||||
const divisor = ctrlKey ? 100 : 300;
|
||||
const scaleDiff = 1 - deltaY / divisor;
|
||||
const isZoomOut = scaleDiff < 1;
|
||||
this._applyChange({
|
||||
scaleDiff,
|
||||
originX: event.clientX - currentRect.left,
|
||||
originY: event.clientY - currentRect.top,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
if (isZoomOut) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
}
|
||||
_onPointerMove(previousPointers, currentPointers) {
|
||||
_onPointerMove(previousPointers, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const isPan = previousPointers.length < 2;
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
// Combine next points with previous points
|
||||
const currentRect = this._positioningEl.getBoundingClientRect();
|
||||
// For calculating panning movement
|
||||
|
@ -288,6 +363,34 @@ class PinchZoom extends HTMLElement {
|
|||
panY: newMidpoint.clientY - prevMidpoint.clientY,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_maybeResetScale() {
|
||||
if (roundedCmp(this.scale, this.resetToMinScaleLimit) <= 0) {
|
||||
this._resetToMinScale();
|
||||
}
|
||||
}
|
||||
_onPointerEnd(pointer, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const totalPointers = 1 + currentPointers.length;
|
||||
const isPinch = totalPointers >= 2;
|
||||
const isPan = totalPointers == 1;
|
||||
if (isPinch) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_resetToMinScale() {
|
||||
if (this.reachMinScaleStrategy === 'reset') {
|
||||
this.setTransform({ scale: this.minScale, x: 0, y: 0 });
|
||||
}
|
||||
else {
|
||||
this.setTransform({ scale: this.minScale });
|
||||
}
|
||||
}
|
||||
/** Transform the view & fire a change event */
|
||||
_applyChange(opts = {}) {
|
||||
|
@ -311,6 +414,40 @@ class PinchZoom extends HTMLElement {
|
|||
y: matrix.f,
|
||||
});
|
||||
}
|
||||
_maybeStopPropagate(event) {
|
||||
if (this.stopPropagateHandled) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
_allowPan() {
|
||||
return (this.allowPanMinScale > 0
|
||||
&& roundedCmp(this.scale, this.allowPanMinScale) > 0);
|
||||
}
|
||||
_maybeEmitCancel(pointers) {
|
||||
const makeCancelEvent = (pointer) => (new PointerEvent('pointercancel', {
|
||||
pointerId: pointer.id,
|
||||
clientX: pointer.clientX,
|
||||
clientY: pointer.clientY,
|
||||
}));
|
||||
if (this.stopPropagateHandled) {
|
||||
pointers.forEach(p => {
|
||||
if (this.parentElement && typeof this.parentElement.dispatchEvent === 'function') {
|
||||
this.parentElement.dispatchEvent(makeCancelEvent(p));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_onClick(event, pointerTracker) {
|
||||
// We never handle non-left-clicks
|
||||
if (event.button !== BUTTON_LEFT) {
|
||||
return;
|
||||
}
|
||||
const wasPanning = pointerTracker.currentPointers.length === 0;
|
||||
const handled = !(wasPanning && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('pinch-zoom', PinchZoom);
|
||||
|
|
177
dist/pinch-zoom.js
vendored
177
dist/pinch-zoom.js
vendored
|
@ -185,37 +185,12 @@ var PinchZoom = (function () {
|
|||
}
|
||||
}
|
||||
|
||||
function styleInject(css, ref) {
|
||||
if ( ref === void 0 ) ref = {};
|
||||
var insertAt = ref.insertAt;
|
||||
|
||||
if (!css || typeof document === 'undefined') { return; }
|
||||
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
var style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
|
||||
if (insertAt === 'top') {
|
||||
if (head.firstChild) {
|
||||
head.insertBefore(style, head.firstChild);
|
||||
} else {
|
||||
head.appendChild(style);
|
||||
}
|
||||
} else {
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(css));
|
||||
}
|
||||
}
|
||||
|
||||
var css = "pinch-zoom {\n display: block;\n overflow: hidden;\n touch-action: none;\n --scale: 1;\n --x: 0;\n --y: 0;\n}\n\npinch-zoom > * {\n transform: translate(var(--x), var(--y)) scale(var(--scale));\n transform-origin: 0 0;\n will-change: transform;\n}\n";
|
||||
styleInject(css);
|
||||
|
||||
const minScaleAttr = 'min-scale';
|
||||
const allowPanMinScaleAttr = 'allow-pan-min-scale';
|
||||
const resetToMinScaleLimitAttr = 'reset-to-min-scale-limit';
|
||||
const reachMinScaleStrategyAttr = 'reach-min-scale-strategy';
|
||||
const stopPropagateHandledAttr = 'stop-propagate-handled';
|
||||
const reachMinScaleStrategyDefault = 'none';
|
||||
function getDistance(a, b) {
|
||||
if (!b)
|
||||
return 0;
|
||||
|
@ -250,6 +225,12 @@ var PinchZoom = (function () {
|
|||
return getSVG().createSVGPoint();
|
||||
}
|
||||
const MIN_SCALE = 0.01;
|
||||
const ALLOW_PAN_MIN_SCALE = -1;
|
||||
const RESET_TO_MIN_SCALE_LIMIT = -1;
|
||||
const BUTTON_LEFT = 0;
|
||||
const roundedCmp = (a, b) => {
|
||||
return Math.round(a * 100) - Math.round(b * 100);
|
||||
};
|
||||
class PinchZoom extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -266,14 +247,26 @@ var PinchZoom = (function () {
|
|||
// We only want to track 2 pointers at most
|
||||
if (pointerTracker.currentPointers.length === 2 || !this._positioningEl)
|
||||
return false;
|
||||
const isPan = pointerTracker.currentPointers.length + 1 === 1;
|
||||
const handled = !(isPan && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
if (!isPan) { // only cancel if something was propagated
|
||||
this._maybeEmitCancel([pointer, ...pointerTracker.currentPointers]);
|
||||
}
|
||||
}
|
||||
event.preventDefault();
|
||||
return true;
|
||||
},
|
||||
move: (previousPointers) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
||||
move: (previousPointers, _, event) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers, event);
|
||||
},
|
||||
end: (pointer, event) => {
|
||||
this._onPointerEnd(pointer, pointerTracker.currentPointers, event);
|
||||
},
|
||||
});
|
||||
this.addEventListener('wheel', event => this._onWheel(event));
|
||||
this.addEventListener('click', event => this._onClick(event, pointerTracker));
|
||||
}
|
||||
static get observedAttributes() { return [minScaleAttr]; }
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
|
@ -295,6 +288,49 @@ var PinchZoom = (function () {
|
|||
set minScale(value) {
|
||||
this.setAttribute(minScaleAttr, String(value));
|
||||
}
|
||||
get reachMinScaleStrategy() {
|
||||
const attrValue = this.getAttribute(reachMinScaleStrategyAttr);
|
||||
const v = attrValue;
|
||||
return v || reachMinScaleStrategyDefault;
|
||||
}
|
||||
set reachMinScaleStrategy(value) {
|
||||
this.setAttribute(reachMinScaleStrategyAttr, value);
|
||||
}
|
||||
get allowPanMinScale() {
|
||||
const attrValue = this.getAttribute(allowPanMinScaleAttr);
|
||||
if (!attrValue)
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(ALLOW_PAN_MIN_SCALE, value);
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
}
|
||||
set allowPanMinScale(value) {
|
||||
this.setAttribute(allowPanMinScaleAttr, String(value));
|
||||
}
|
||||
get resetToMinScaleLimit() {
|
||||
const attrValue = this.getAttribute(resetToMinScaleLimitAttr);
|
||||
if (!attrValue)
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(RESET_TO_MIN_SCALE_LIMIT, value);
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
}
|
||||
set resetToMinScaleLimit(value) {
|
||||
this.setAttribute(resetToMinScaleLimitAttr, String(value));
|
||||
}
|
||||
get stopPropagateHandled() {
|
||||
return this.hasAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
set stopPropagateHandled(value) {
|
||||
if (value) {
|
||||
this.setAttribute(stopPropagateHandledAttr, '');
|
||||
}
|
||||
else {
|
||||
this.removeAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this._stageElChange();
|
||||
}
|
||||
|
@ -396,8 +432,9 @@ var PinchZoom = (function () {
|
|||
*/
|
||||
_updateTransform(scale, x, y, allowChangeEvent) {
|
||||
// Avoid scaling to zero
|
||||
if (scale < this.minScale)
|
||||
return;
|
||||
if (scale < this.minScale) {
|
||||
scale = this.minScale;
|
||||
}
|
||||
// Return if there's no change
|
||||
if (scale === this.scale &&
|
||||
x === this.x &&
|
||||
|
@ -445,16 +482,24 @@ var PinchZoom = (function () {
|
|||
// ctrlKey is true when pinch-zooming on a trackpad.
|
||||
const divisor = ctrlKey ? 100 : 300;
|
||||
const scaleDiff = 1 - deltaY / divisor;
|
||||
const isZoomOut = scaleDiff < 1;
|
||||
this._applyChange({
|
||||
scaleDiff,
|
||||
originX: event.clientX - currentRect.left,
|
||||
originY: event.clientY - currentRect.top,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
if (isZoomOut) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
}
|
||||
_onPointerMove(previousPointers, currentPointers) {
|
||||
_onPointerMove(previousPointers, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const isPan = previousPointers.length < 2;
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
// Combine next points with previous points
|
||||
const currentRect = this._positioningEl.getBoundingClientRect();
|
||||
// For calculating panning movement
|
||||
|
@ -473,6 +518,34 @@ var PinchZoom = (function () {
|
|||
panY: newMidpoint.clientY - prevMidpoint.clientY,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_maybeResetScale() {
|
||||
if (roundedCmp(this.scale, this.resetToMinScaleLimit) <= 0) {
|
||||
this._resetToMinScale();
|
||||
}
|
||||
}
|
||||
_onPointerEnd(pointer, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const totalPointers = 1 + currentPointers.length;
|
||||
const isPinch = totalPointers >= 2;
|
||||
const isPan = totalPointers == 1;
|
||||
if (isPinch) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_resetToMinScale() {
|
||||
if (this.reachMinScaleStrategy === 'reset') {
|
||||
this.setTransform({ scale: this.minScale, x: 0, y: 0 });
|
||||
}
|
||||
else {
|
||||
this.setTransform({ scale: this.minScale });
|
||||
}
|
||||
}
|
||||
/** Transform the view & fire a change event */
|
||||
_applyChange(opts = {}) {
|
||||
|
@ -496,6 +569,40 @@ var PinchZoom = (function () {
|
|||
y: matrix.f,
|
||||
});
|
||||
}
|
||||
_maybeStopPropagate(event) {
|
||||
if (this.stopPropagateHandled) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
_allowPan() {
|
||||
return (this.allowPanMinScale > 0
|
||||
&& roundedCmp(this.scale, this.allowPanMinScale) > 0);
|
||||
}
|
||||
_maybeEmitCancel(pointers) {
|
||||
const makeCancelEvent = (pointer) => (new PointerEvent('pointercancel', {
|
||||
pointerId: pointer.id,
|
||||
clientX: pointer.clientX,
|
||||
clientY: pointer.clientY,
|
||||
}));
|
||||
if (this.stopPropagateHandled) {
|
||||
pointers.forEach(p => {
|
||||
if (this.parentElement && typeof this.parentElement.dispatchEvent === 'function') {
|
||||
this.parentElement.dispatchEvent(makeCancelEvent(p));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_onClick(event, pointerTracker) {
|
||||
// We never handle non-left-clicks
|
||||
if (event.button !== BUTTON_LEFT) {
|
||||
return;
|
||||
}
|
||||
const wasPanning = pointerTracker.currentPointers.length === 0;
|
||||
const handled = !(wasPanning && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('pinch-zoom', PinchZoom);
|
||||
|
|
177
dist/pinch-zoom.mjs
vendored
177
dist/pinch-zoom.mjs
vendored
|
@ -1,36 +1,11 @@
|
|||
import PointerTracker from 'pointer-tracker';
|
||||
|
||||
function styleInject(css, ref) {
|
||||
if ( ref === void 0 ) ref = {};
|
||||
var insertAt = ref.insertAt;
|
||||
|
||||
if (!css || typeof document === 'undefined') { return; }
|
||||
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
var style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
|
||||
if (insertAt === 'top') {
|
||||
if (head.firstChild) {
|
||||
head.insertBefore(style, head.firstChild);
|
||||
} else {
|
||||
head.appendChild(style);
|
||||
}
|
||||
} else {
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(css));
|
||||
}
|
||||
}
|
||||
|
||||
var css = "pinch-zoom {\n display: block;\n overflow: hidden;\n touch-action: none;\n --scale: 1;\n --x: 0;\n --y: 0;\n}\n\npinch-zoom > * {\n transform: translate(var(--x), var(--y)) scale(var(--scale));\n transform-origin: 0 0;\n will-change: transform;\n}\n";
|
||||
styleInject(css);
|
||||
|
||||
const minScaleAttr = 'min-scale';
|
||||
const allowPanMinScaleAttr = 'allow-pan-min-scale';
|
||||
const resetToMinScaleLimitAttr = 'reset-to-min-scale-limit';
|
||||
const reachMinScaleStrategyAttr = 'reach-min-scale-strategy';
|
||||
const stopPropagateHandledAttr = 'stop-propagate-handled';
|
||||
const reachMinScaleStrategyDefault = 'none';
|
||||
function getDistance(a, b) {
|
||||
if (!b)
|
||||
return 0;
|
||||
|
@ -65,6 +40,12 @@ function createPoint() {
|
|||
return getSVG().createSVGPoint();
|
||||
}
|
||||
const MIN_SCALE = 0.01;
|
||||
const ALLOW_PAN_MIN_SCALE = -1;
|
||||
const RESET_TO_MIN_SCALE_LIMIT = -1;
|
||||
const BUTTON_LEFT = 0;
|
||||
const roundedCmp = (a, b) => {
|
||||
return Math.round(a * 100) - Math.round(b * 100);
|
||||
};
|
||||
class PinchZoom extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -81,14 +62,26 @@ class PinchZoom extends HTMLElement {
|
|||
// We only want to track 2 pointers at most
|
||||
if (pointerTracker.currentPointers.length === 2 || !this._positioningEl)
|
||||
return false;
|
||||
const isPan = pointerTracker.currentPointers.length + 1 === 1;
|
||||
const handled = !(isPan && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
if (!isPan) { // only cancel if something was propagated
|
||||
this._maybeEmitCancel([pointer, ...pointerTracker.currentPointers]);
|
||||
}
|
||||
}
|
||||
event.preventDefault();
|
||||
return true;
|
||||
},
|
||||
move: (previousPointers) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
||||
move: (previousPointers, _, event) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers, event);
|
||||
},
|
||||
end: (pointer, event) => {
|
||||
this._onPointerEnd(pointer, pointerTracker.currentPointers, event);
|
||||
},
|
||||
});
|
||||
this.addEventListener('wheel', event => this._onWheel(event));
|
||||
this.addEventListener('click', event => this._onClick(event, pointerTracker));
|
||||
}
|
||||
static get observedAttributes() { return [minScaleAttr]; }
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
|
@ -110,6 +103,49 @@ class PinchZoom extends HTMLElement {
|
|||
set minScale(value) {
|
||||
this.setAttribute(minScaleAttr, String(value));
|
||||
}
|
||||
get reachMinScaleStrategy() {
|
||||
const attrValue = this.getAttribute(reachMinScaleStrategyAttr);
|
||||
const v = attrValue;
|
||||
return v || reachMinScaleStrategyDefault;
|
||||
}
|
||||
set reachMinScaleStrategy(value) {
|
||||
this.setAttribute(reachMinScaleStrategyAttr, value);
|
||||
}
|
||||
get allowPanMinScale() {
|
||||
const attrValue = this.getAttribute(allowPanMinScaleAttr);
|
||||
if (!attrValue)
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(ALLOW_PAN_MIN_SCALE, value);
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
}
|
||||
set allowPanMinScale(value) {
|
||||
this.setAttribute(allowPanMinScaleAttr, String(value));
|
||||
}
|
||||
get resetToMinScaleLimit() {
|
||||
const attrValue = this.getAttribute(resetToMinScaleLimitAttr);
|
||||
if (!attrValue)
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value))
|
||||
return Math.max(RESET_TO_MIN_SCALE_LIMIT, value);
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
}
|
||||
set resetToMinScaleLimit(value) {
|
||||
this.setAttribute(resetToMinScaleLimitAttr, String(value));
|
||||
}
|
||||
get stopPropagateHandled() {
|
||||
return this.hasAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
set stopPropagateHandled(value) {
|
||||
if (value) {
|
||||
this.setAttribute(stopPropagateHandledAttr, '');
|
||||
}
|
||||
else {
|
||||
this.removeAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this._stageElChange();
|
||||
}
|
||||
|
@ -211,8 +247,9 @@ class PinchZoom extends HTMLElement {
|
|||
*/
|
||||
_updateTransform(scale, x, y, allowChangeEvent) {
|
||||
// Avoid scaling to zero
|
||||
if (scale < this.minScale)
|
||||
return;
|
||||
if (scale < this.minScale) {
|
||||
scale = this.minScale;
|
||||
}
|
||||
// Return if there's no change
|
||||
if (scale === this.scale &&
|
||||
x === this.x &&
|
||||
|
@ -260,16 +297,24 @@ class PinchZoom extends HTMLElement {
|
|||
// ctrlKey is true when pinch-zooming on a trackpad.
|
||||
const divisor = ctrlKey ? 100 : 300;
|
||||
const scaleDiff = 1 - deltaY / divisor;
|
||||
const isZoomOut = scaleDiff < 1;
|
||||
this._applyChange({
|
||||
scaleDiff,
|
||||
originX: event.clientX - currentRect.left,
|
||||
originY: event.clientY - currentRect.top,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
if (isZoomOut) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
}
|
||||
_onPointerMove(previousPointers, currentPointers) {
|
||||
_onPointerMove(previousPointers, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const isPan = previousPointers.length < 2;
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
// Combine next points with previous points
|
||||
const currentRect = this._positioningEl.getBoundingClientRect();
|
||||
// For calculating panning movement
|
||||
|
@ -288,6 +333,34 @@ class PinchZoom extends HTMLElement {
|
|||
panY: newMidpoint.clientY - prevMidpoint.clientY,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_maybeResetScale() {
|
||||
if (roundedCmp(this.scale, this.resetToMinScaleLimit) <= 0) {
|
||||
this._resetToMinScale();
|
||||
}
|
||||
}
|
||||
_onPointerEnd(pointer, currentPointers, event) {
|
||||
if (!this._positioningEl)
|
||||
return;
|
||||
const totalPointers = 1 + currentPointers.length;
|
||||
const isPinch = totalPointers >= 2;
|
||||
const isPan = totalPointers == 1;
|
||||
if (isPinch) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
_resetToMinScale() {
|
||||
if (this.reachMinScaleStrategy === 'reset') {
|
||||
this.setTransform({ scale: this.minScale, x: 0, y: 0 });
|
||||
}
|
||||
else {
|
||||
this.setTransform({ scale: this.minScale });
|
||||
}
|
||||
}
|
||||
/** Transform the view & fire a change event */
|
||||
_applyChange(opts = {}) {
|
||||
|
@ -311,6 +384,40 @@ class PinchZoom extends HTMLElement {
|
|||
y: matrix.f,
|
||||
});
|
||||
}
|
||||
_maybeStopPropagate(event) {
|
||||
if (this.stopPropagateHandled) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
_allowPan() {
|
||||
return (this.allowPanMinScale > 0
|
||||
&& roundedCmp(this.scale, this.allowPanMinScale) > 0);
|
||||
}
|
||||
_maybeEmitCancel(pointers) {
|
||||
const makeCancelEvent = (pointer) => (new PointerEvent('pointercancel', {
|
||||
pointerId: pointer.id,
|
||||
clientX: pointer.clientX,
|
||||
clientY: pointer.clientY,
|
||||
}));
|
||||
if (this.stopPropagateHandled) {
|
||||
pointers.forEach(p => {
|
||||
if (this.parentElement && typeof this.parentElement.dispatchEvent === 'function') {
|
||||
this.parentElement.dispatchEvent(makeCancelEvent(p));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_onClick(event, pointerTracker) {
|
||||
// We never handle non-left-clicks
|
||||
if (event.button !== BUTTON_LEFT) {
|
||||
return;
|
||||
}
|
||||
const wasPanning = pointerTracker.currentPointers.length === 0;
|
||||
const handled = !(wasPanning && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('pinch-zoom', PinchZoom);
|
||||
|
|
|
@ -30,6 +30,14 @@ interface SetTransformOpts extends ChangeOptions {
|
|||
type ScaleRelativeToValues = 'container' | 'content';
|
||||
|
||||
const minScaleAttr = 'min-scale';
|
||||
const allowPanMinScaleAttr = 'allow-pan-min-scale';
|
||||
const resetToMinScaleLimitAttr = 'reset-to-min-scale-limit';
|
||||
const reachMinScaleStrategyAttr = 'reach-min-scale-strategy';
|
||||
const stopPropagateHandledAttr = 'stop-propagate-handled';
|
||||
|
||||
type ReachMinScaleStrategy = 'reset' | 'none';
|
||||
|
||||
const reachMinScaleStrategyDefault: ReachMinScaleStrategy = 'none';
|
||||
|
||||
export interface ScaleToOpts extends ChangeOptions {
|
||||
/** Transform origin. Can be a number, or string percent, eg "50%" */
|
||||
|
@ -80,6 +88,14 @@ function createPoint(): SVGPoint {
|
|||
}
|
||||
|
||||
const MIN_SCALE = 0.01;
|
||||
const ALLOW_PAN_MIN_SCALE = -1;
|
||||
const RESET_TO_MIN_SCALE_LIMIT = -1;
|
||||
|
||||
const BUTTON_LEFT = 0;
|
||||
|
||||
const roundedCmp = (a: number, b: number) => {
|
||||
return Math.round(a * 100) - Math.round(b * 100)
|
||||
}
|
||||
|
||||
export default class PinchZoom extends HTMLElement {
|
||||
// The element that we'll transform.
|
||||
|
@ -105,15 +121,30 @@ export default class PinchZoom extends HTMLElement {
|
|||
start: (pointer, event) => {
|
||||
// We only want to track 2 pointers at most
|
||||
if (pointerTracker.currentPointers.length === 2 || !this._positioningEl) return false;
|
||||
|
||||
const isPan = pointerTracker.currentPointers.length + 1 === 1;
|
||||
|
||||
const handled = !(isPan && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
if (!isPan) { // only cancel if something was propagated
|
||||
this._maybeEmitCancel([pointer, ...pointerTracker.currentPointers]);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
return true;
|
||||
},
|
||||
move: (previousPointers) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
||||
move: (previousPointers, _, event) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers, event);
|
||||
},
|
||||
end: (pointer, event) => {
|
||||
this._onPointerEnd(pointer, pointerTracker.currentPointers, event);
|
||||
},
|
||||
});
|
||||
|
||||
this.addEventListener('wheel', event => this._onWheel(event));
|
||||
this.addEventListener('click', event => this._onClick(event, pointerTracker));
|
||||
}
|
||||
|
||||
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
||||
|
@ -138,6 +169,58 @@ export default class PinchZoom extends HTMLElement {
|
|||
this.setAttribute(minScaleAttr, String(value));
|
||||
}
|
||||
|
||||
get reachMinScaleStrategy(): ReachMinScaleStrategy {
|
||||
const attrValue = this.getAttribute(reachMinScaleStrategyAttr);
|
||||
|
||||
const v = attrValue as ReachMinScaleStrategy;
|
||||
|
||||
return v || reachMinScaleStrategyDefault;
|
||||
}
|
||||
|
||||
set reachMinScaleStrategy(value: ReachMinScaleStrategy) {
|
||||
this.setAttribute(reachMinScaleStrategyAttr, value as string);
|
||||
}
|
||||
|
||||
get allowPanMinScale(): number {
|
||||
const attrValue = this.getAttribute(allowPanMinScaleAttr);
|
||||
if (!attrValue) return ALLOW_PAN_MIN_SCALE;
|
||||
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value)) return Math.max(ALLOW_PAN_MIN_SCALE, value);
|
||||
|
||||
return ALLOW_PAN_MIN_SCALE;
|
||||
}
|
||||
|
||||
set allowPanMinScale(value: number) {
|
||||
this.setAttribute(allowPanMinScaleAttr, String(value));
|
||||
}
|
||||
|
||||
get resetToMinScaleLimit(): number {
|
||||
const attrValue = this.getAttribute(resetToMinScaleLimitAttr);
|
||||
if (!attrValue) return RESET_TO_MIN_SCALE_LIMIT;
|
||||
|
||||
const value = parseFloat(attrValue);
|
||||
if (Number.isFinite(value)) return Math.max(RESET_TO_MIN_SCALE_LIMIT, value);
|
||||
|
||||
return RESET_TO_MIN_SCALE_LIMIT;
|
||||
}
|
||||
|
||||
set resetToMinScaleLimit(value: number) {
|
||||
this.setAttribute(resetToMinScaleLimitAttr, String(value));
|
||||
}
|
||||
|
||||
get stopPropagateHandled() {
|
||||
return this.hasAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
|
||||
set stopPropagateHandled(value: boolean) {
|
||||
if (value) {
|
||||
this.setAttribute(stopPropagateHandledAttr, '');
|
||||
} else {
|
||||
this.removeAttribute(stopPropagateHandledAttr);
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this._stageElChange();
|
||||
}
|
||||
|
@ -270,7 +353,9 @@ export default class PinchZoom extends HTMLElement {
|
|||
*/
|
||||
private _updateTransform(scale: number, x: number, y: number, allowChangeEvent: boolean) {
|
||||
// Avoid scaling to zero
|
||||
if (scale < this.minScale) return;
|
||||
if (scale < this.minScale) {
|
||||
scale = this.minScale;
|
||||
}
|
||||
|
||||
// Return if there's no change
|
||||
if (
|
||||
|
@ -331,17 +416,29 @@ export default class PinchZoom extends HTMLElement {
|
|||
const divisor = ctrlKey ? 100 : 300;
|
||||
const scaleDiff = 1 - deltaY / divisor;
|
||||
|
||||
const isZoomOut = scaleDiff < 1;
|
||||
|
||||
this._applyChange({
|
||||
scaleDiff,
|
||||
originX: event.clientX - currentRect.left,
|
||||
originY: event.clientY - currentRect.top,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
|
||||
if (isZoomOut) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
}
|
||||
|
||||
private _onPointerMove(previousPointers: Pointer[], currentPointers: Pointer[]) {
|
||||
private _onPointerMove(previousPointers: Pointer[], currentPointers: Pointer[], event: Event) {
|
||||
if (!this._positioningEl) return;
|
||||
|
||||
const isPan = previousPointers.length < 2;
|
||||
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Combine next points with previous points
|
||||
const currentRect = this._positioningEl.getBoundingClientRect();
|
||||
|
||||
|
@ -364,8 +461,42 @@ export default class PinchZoom extends HTMLElement {
|
|||
panY: newMidpoint.clientY - prevMidpoint.clientY,
|
||||
allowChangeEvent: true,
|
||||
});
|
||||
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
|
||||
private _maybeResetScale() {
|
||||
if (roundedCmp(this.scale, this.resetToMinScaleLimit) <= 0) {
|
||||
this._resetToMinScale();
|
||||
}
|
||||
}
|
||||
|
||||
private _onPointerEnd(pointer: Pointer, currentPointers: Pointer[], event: Event) {
|
||||
if (!this._positioningEl) return;
|
||||
|
||||
const totalPointers = 1 + currentPointers.length;
|
||||
const isPinch = totalPointers >= 2;
|
||||
const isPan = totalPointers == 1;
|
||||
|
||||
if (isPinch) {
|
||||
this._maybeResetScale();
|
||||
}
|
||||
|
||||
if (isPan && !this._allowPan()) {
|
||||
return;
|
||||
}
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
|
||||
private _resetToMinScale() {
|
||||
if (this.reachMinScaleStrategy === 'reset') {
|
||||
this.setTransform({ scale: this.minScale, x: 0, y: 0 });
|
||||
} else {
|
||||
this.setTransform({ scale: this.minScale });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Transform the view & fire a change event */
|
||||
private _applyChange(opts: ApplyChangeOpts = {}) {
|
||||
const {
|
||||
|
@ -395,4 +526,48 @@ export default class PinchZoom extends HTMLElement {
|
|||
y: matrix.f,
|
||||
});
|
||||
}
|
||||
|
||||
private _maybeStopPropagate(event: Event) {
|
||||
if (this.stopPropagateHandled) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
private _allowPan() {
|
||||
return (
|
||||
this.allowPanMinScale > 0
|
||||
&& roundedCmp(this.scale, this.allowPanMinScale) > 0
|
||||
);
|
||||
}
|
||||
|
||||
private _maybeEmitCancel(pointers: Pointer[]) {
|
||||
const makeCancelEvent = (pointer: Pointer) => (
|
||||
new PointerEvent(
|
||||
'pointercancel', {
|
||||
pointerId: pointer.id,
|
||||
clientX: pointer.clientX,
|
||||
clientY: pointer.clientY,
|
||||
}
|
||||
));
|
||||
if (this.stopPropagateHandled) {
|
||||
pointers.forEach(p => {
|
||||
if (this.parentElement && typeof this.parentElement.dispatchEvent === 'function') {
|
||||
this.parentElement.dispatchEvent(makeCancelEvent(p));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _onClick(event: MouseEvent, pointerTracker: PointerTracker) {
|
||||
// We never handle non-left-clicks
|
||||
if (event.button !== BUTTON_LEFT) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wasPanning = pointerTracker.currentPointers.length === 0;
|
||||
const handled = !(wasPanning && !this._allowPan());
|
||||
if (handled) {
|
||||
this._maybeStopPropagate(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
5025
package-lock.json
generated
5025
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
@ -1,16 +1,16 @@
|
|||
{
|
||||
"name": "pinch-zoom-element",
|
||||
"version": "1.1.0",
|
||||
"version": "1.3.0",
|
||||
"description": "Put stuff in an element, now you can pinch-zoom it!",
|
||||
"main": "dist/pinch-zoom.cjs.js",
|
||||
"module": "dist/pinch-zoom.es.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "rm -r dist && rollup -c"
|
||||
"build": "rm -rf dist && rollup -c"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/GoogleChromeLabs/pinch-zoom.git"
|
||||
"url": "https://akkoma.dev/pinch-zoom-element.git"
|
||||
},
|
||||
"keywords": [
|
||||
"pointer",
|
||||
|
@ -19,12 +19,12 @@
|
|||
"pinch zoom",
|
||||
"pan"
|
||||
],
|
||||
"author": "Jake Archibald",
|
||||
"author": "Jake Archibald, tusooa, FloatingGhost",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/GoogleChromeLabs/pinch-zoom/issues"
|
||||
"url": "https://lily.kazv.moe/infra/pinch-zoom-element/-/issues"
|
||||
},
|
||||
"homepage": "https://github.com/GoogleChromeLabs/pinch-zoom#readme",
|
||||
"homepage": "https://lily.kazv.moe/infra/pinch-zoom-element",
|
||||
"dependencies": {
|
||||
"pointer-tracker": "^2.0.3"
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ import { dependencies } from './package.json';
|
|||
const mjs = {
|
||||
plugins: [
|
||||
typescript({ useTsconfigDeclarationDir: false }),
|
||||
postcss()
|
||||
postcss({ extract: 'dist/pinch-zoom.css' })
|
||||
],
|
||||
external: Object.keys(dependencies),
|
||||
input: 'lib/index.ts',
|
||||
|
|
Loading…
Reference in a new issue