From 713b5d3f89492a8eda66bf5722bab146071894dc Mon Sep 17 00:00:00 2001 From: Kenneth Rohde Christiansen Date: Wed, 16 Jan 2019 14:18:09 +0100 Subject: [PATCH] Add minScale attribute/property (#6) --- dist/pinch-zoom-min.js | 2 +- dist/pinch-zoom.d.ts | 3 +++ dist/pinch-zoom.js | 23 ++++++++++++++++++++++- dist/pinch-zoom.mjs | 23 ++++++++++++++++++++++- lib/pinch-zoom.ts | 28 +++++++++++++++++++++++++++- package-lock.json | 3 +-- 6 files changed, 76 insertions(+), 6 deletions(-) diff --git a/dist/pinch-zoom-min.js b/dist/pinch-zoom-min.js index ea5a705..01d3b0f 100644 --- a/dist/pinch-zoom-min.js +++ b/dist/pinch-zoom-min.js @@ -1 +1 @@ -!function(){"use strict";!function(){class t{constructor(t){this.id=-1,this.nativePointer=t,this.pageX=t.pageX,this.pageY=t.pageY,this.clientX=t.clientX,this.clientY=t.clientY,self.Touch&&t instanceof Touch?this.id=t.identifier:e(t)&&(this.id=t.pointerId)}getCoalesced(){return"getCoalescedEvents"in this.nativePointer?this.nativePointer.getCoalescedEvents().map(e=>new t(e)):[this]}}const e=t=>self.PointerEvent&&t instanceof PointerEvent,n=()=>{};class i{constructor(t,e){this._element=t,this.startPointers=[],this.currentPointers=[];const{start:i=(()=>!0),move:s=n,end:r=n}=e;this._startCallback=i,this._moveCallback=s,this._endCallback=r,this._pointerStart=this._pointerStart.bind(this),this._touchStart=this._touchStart.bind(this),this._move=this._move.bind(this),this._triggerPointerEnd=this._triggerPointerEnd.bind(this),this._pointerEnd=this._pointerEnd.bind(this),this._touchEnd=this._touchEnd.bind(this),self.PointerEvent?this._element.addEventListener("pointerdown",this._pointerStart):(this._element.addEventListener("mousedown",this._pointerStart),this._element.addEventListener("touchstart",this._touchStart),this._element.addEventListener("touchmove",this._move),this._element.addEventListener("touchend",this._touchEnd))}_triggerPointerStart(t,e){return!!this._startCallback(t,e)&&(this.currentPointers.push(t),this.startPointers.push(t),!0)}_pointerStart(n){0===n.button&&this._triggerPointerStart(new t(n),n)&&(e(n)?(this._element.setPointerCapture(n.pointerId),this._element.addEventListener("pointermove",this._move),this._element.addEventListener("pointerup",this._pointerEnd)):(window.addEventListener("mousemove",this._move),window.addEventListener("mouseup",this._pointerEnd)))}_touchStart(e){for(const n of Array.from(e.changedTouches))this._triggerPointerStart(new t(n),e)}_move(e){const n=this.currentPointers.slice(),i="changedTouches"in e?Array.from(e.changedTouches).map(e=>new t(e)):[new t(e)],s=[];for(const t of i){const e=this.currentPointers.findIndex(e=>e.id===t.id);-1!==e&&(s.push(t),this.currentPointers[e]=t)}0!==s.length&&this._moveCallback(n,s,e)}_triggerPointerEnd(t,e){const n=this.currentPointers.findIndex(e=>e.id===t.id);return-1!==n&&(this.currentPointers.splice(n,1),this.startPointers.splice(n,1),this._endCallback(t,e),!0)}_pointerEnd(n){if(this._triggerPointerEnd(new t(n),n))if(e(n)){if(this.currentPointers.length)return;this._element.removeEventListener("pointermove",this._move),this._element.removeEventListener("pointerup",this._pointerEnd)}else window.removeEventListener("mousemove",this._move),window.removeEventListener("mouseup",this._pointerEnd)}_touchEnd(e){for(const n of Array.from(e.changedTouches))this._triggerPointerEnd(new t(n),e)}}function s(t,e){return e?Math.sqrt((e.clientX-t.clientX)**2+(e.clientY-t.clientY)**2):0}function r(t,e){return e?{clientX:(t.clientX+e.clientX)/2,clientY:(t.clientY+e.clientY)/2}:t}function o(t,e){return"number"==typeof t?t:t.trimRight().endsWith("%")?e*parseFloat(t)/100:parseFloat(t)}let h;function a(){return h||(h=document.createElementNS("http://www.w3.org/2000/svg","svg"))}function l(){return a().createSVGMatrix()}function c(){return a().createSVGPoint()}!function(t,e){void 0===e&&(e={});var n=e.insertAt;if(t&&"undefined"!=typeof document){var i=document.head||document.getElementsByTagName("head")[0],s=document.createElement("style");s.type="text/css","top"===n&&i.firstChild?i.insertBefore(s,i.firstChild):i.appendChild(s),s.styleSheet?s.styleSheet.cssText=t:s.appendChild(document.createTextNode(t))}}("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");const d=.01;class g extends HTMLElement{constructor(){super(),this._transform=l(),new MutationObserver(()=>this._stageElChange()).observe(this,{childList:!0});const t=new i(this,{start:(e,n)=>!(2===t.currentPointers.length||!this._positioningEl)&&(n.preventDefault(),!0),move:e=>{this._onPointerMove(e,t.currentPointers)}});this.addEventListener("wheel",t=>this._onWheel(t))}connectedCallback(){this._stageElChange()}get x(){return this._transform.e}get y(){return this._transform.f}get scale(){return this._transform.a}scaleTo(t,e={}){let{originX:n=0,originY:i=0}=e;const{relativeTo:s="content",allowChangeEvent:r=!1}=e,h="content"===s?this._positioningEl:this;if(!h||!this._positioningEl)return void this.setTransform({scale:t,allowChangeEvent:r});const a=h.getBoundingClientRect();if(n=o(n,a.width),i=o(i,a.height),"content"===s)n+=this.x,i+=this.y;else{const t=this._positioningEl.getBoundingClientRect();n-=t.left,i-=t.top}this._applyChange({allowChangeEvent:r,originX:n,originY:i,scaleDiff:t/this.scale})}setTransform(t={}){const{scale:e=this.scale,allowChangeEvent:n=!1}=t;let{x:i=this.x,y:s=this.y}=t;if(!this._positioningEl)return void this._updateTransform(e,i,s,n);const r=this.getBoundingClientRect(),o=this._positioningEl.getBoundingClientRect();if(!r.width||!r.height)return void this._updateTransform(e,i,s,n);let h=c();h.x=o.left-r.left,h.y=o.top-r.top;let a=c();a.x=o.width+h.x,a.y=o.height+h.y;const d=l().translate(i,s).scale(e).multiply(this._transform.inverse());h=h.matrixTransform(d),a=a.matrixTransform(d),h.x>r.width?i+=r.width-h.x:a.x<0&&(i+=-a.x),h.y>r.height?s+=r.height-h.y:a.y<0&&(s+=-a.y),this._updateTransform(e,i,s,n)}_updateTransform(t,e,n,i){if(!(t1&&console.warn(" must not have more than one child."),this.setTransform({allowChangeEvent:!0}))}_onWheel(t){if(!this._positioningEl)return;t.preventDefault();const e=this._positioningEl.getBoundingClientRect();let{deltaY:n}=t;const{ctrlKey:i,deltaMode:s}=t;1===s&&(n*=15);const r=1-n/(i?100:300);this._applyChange({scaleDiff:r,originX:t.clientX-e.left,originY:t.clientY-e.top,allowChangeEvent:!0})}_onPointerMove(t,e){if(!this._positioningEl)return;const n=this._positioningEl.getBoundingClientRect(),i=r(t[0],t[1]),o=r(e[0],e[1]),h=i.clientX-n.left,a=i.clientY-n.top,l=s(t[0],t[1]),c=s(e[0],e[1]),d=l?c/l:1;this._applyChange({originX:h,originY:a,scaleDiff:d,panX:o.clientX-i.clientX,panY:o.clientY-i.clientY,allowChangeEvent:!0})}_applyChange(t={}){const{panX:e=0,panY:n=0,originX:i=0,originY:s=0,scaleDiff:r=1,allowChangeEvent:o=!1}=t,h=l().translate(e,n).translate(i,s).translate(this.x,this.y).scale(r).translate(-i,-s).scale(this.scale);this.setTransform({allowChangeEvent:o,scale:h.a,x:h.e,y:h.f})}}customElements.define("pinch-zoom",g)}()}(); +!function(){"use strict";!function(){class t{constructor(t){this.id=-1,this.nativePointer=t,this.pageX=t.pageX,this.pageY=t.pageY,this.clientX=t.clientX,this.clientY=t.clientY,self.Touch&&t instanceof Touch?this.id=t.identifier:e(t)&&(this.id=t.pointerId)}getCoalesced(){return"getCoalescedEvents"in this.nativePointer?this.nativePointer.getCoalescedEvents().map(e=>new t(e)):[this]}}const e=t=>self.PointerEvent&&t instanceof PointerEvent,n=()=>{};class i{constructor(t,e){this._element=t,this.startPointers=[],this.currentPointers=[];const{start:i=(()=>!0),move:s=n,end:r=n}=e;this._startCallback=i,this._moveCallback=s,this._endCallback=r,this._pointerStart=this._pointerStart.bind(this),this._touchStart=this._touchStart.bind(this),this._move=this._move.bind(this),this._triggerPointerEnd=this._triggerPointerEnd.bind(this),this._pointerEnd=this._pointerEnd.bind(this),this._touchEnd=this._touchEnd.bind(this),self.PointerEvent?this._element.addEventListener("pointerdown",this._pointerStart):(this._element.addEventListener("mousedown",this._pointerStart),this._element.addEventListener("touchstart",this._touchStart),this._element.addEventListener("touchmove",this._move),this._element.addEventListener("touchend",this._touchEnd))}_triggerPointerStart(t,e){return!!this._startCallback(t,e)&&(this.currentPointers.push(t),this.startPointers.push(t),!0)}_pointerStart(n){0===n.button&&this._triggerPointerStart(new t(n),n)&&(e(n)?(this._element.setPointerCapture(n.pointerId),this._element.addEventListener("pointermove",this._move),this._element.addEventListener("pointerup",this._pointerEnd)):(window.addEventListener("mousemove",this._move),window.addEventListener("mouseup",this._pointerEnd)))}_touchStart(e){for(const n of Array.from(e.changedTouches))this._triggerPointerStart(new t(n),e)}_move(e){const n=this.currentPointers.slice(),i="changedTouches"in e?Array.from(e.changedTouches).map(e=>new t(e)):[new t(e)],s=[];for(const t of i){const e=this.currentPointers.findIndex(e=>e.id===t.id);-1!==e&&(s.push(t),this.currentPointers[e]=t)}0!==s.length&&this._moveCallback(n,s,e)}_triggerPointerEnd(t,e){const n=this.currentPointers.findIndex(e=>e.id===t.id);return-1!==n&&(this.currentPointers.splice(n,1),this.startPointers.splice(n,1),this._endCallback(t,e),!0)}_pointerEnd(n){if(this._triggerPointerEnd(new t(n),n))if(e(n)){if(this.currentPointers.length)return;this._element.removeEventListener("pointermove",this._move),this._element.removeEventListener("pointerup",this._pointerEnd)}else window.removeEventListener("mousemove",this._move),window.removeEventListener("mouseup",this._pointerEnd)}_touchEnd(e){for(const n of Array.from(e.changedTouches))this._triggerPointerEnd(new t(n),e)}}!function(t,e){void 0===e&&(e={});var n=e.insertAt;if(t&&"undefined"!=typeof document){var i=document.head||document.getElementsByTagName("head")[0],s=document.createElement("style");s.type="text/css","top"===n&&i.firstChild?i.insertBefore(s,i.firstChild):i.appendChild(s),s.styleSheet?s.styleSheet.cssText=t:s.appendChild(document.createTextNode(t))}}("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");const s="min-scale";function r(t,e){return e?Math.sqrt((e.clientX-t.clientX)**2+(e.clientY-t.clientY)**2):0}function o(t,e){return e?{clientX:(t.clientX+e.clientX)/2,clientY:(t.clientY+e.clientY)/2}:t}function h(t,e){return"number"==typeof t?t:t.trimRight().endsWith("%")?e*parseFloat(t)/100:parseFloat(t)}let a;function l(){return a||(a=document.createElementNS("http://www.w3.org/2000/svg","svg"))}function c(){return l().createSVGMatrix()}function d(){return l().createSVGPoint()}const g=.01;class u extends HTMLElement{constructor(){super(),this._transform=c(),new MutationObserver(()=>this._stageElChange()).observe(this,{childList:!0});const t=new i(this,{start:(e,n)=>!(2===t.currentPointers.length||!this._positioningEl)&&(n.preventDefault(),!0),move:e=>{this._onPointerMove(e,t.currentPointers)}});this.addEventListener("wheel",t=>this._onWheel(t))}static get observedAttributes(){return[s]}attributeChangedCallback(t,e,n){t===s&&this.scaler.width?i+=r.width-h.x:a.x<0&&(i+=-a.x),h.y>r.height?s+=r.height-h.y:a.y<0&&(s+=-a.y),this._updateTransform(e,i,s,n)}_updateTransform(t,e,n,i){if(!(t1&&console.warn(" must not have more than one child."),this.setTransform({allowChangeEvent:!0}))}_onWheel(t){if(!this._positioningEl)return;t.preventDefault();const e=this._positioningEl.getBoundingClientRect();let{deltaY:n}=t;const{ctrlKey:i,deltaMode:s}=t;1===s&&(n*=15);const r=1-n/(i?100:300);this._applyChange({scaleDiff:r,originX:t.clientX-e.left,originY:t.clientY-e.top,allowChangeEvent:!0})}_onPointerMove(t,e){if(!this._positioningEl)return;const n=this._positioningEl.getBoundingClientRect(),i=o(t[0],t[1]),s=o(e[0],e[1]),h=i.clientX-n.left,a=i.clientY-n.top,l=r(t[0],t[1]),c=r(e[0],e[1]),d=l?c/l:1;this._applyChange({originX:h,originY:a,scaleDiff:d,panX:s.clientX-i.clientX,panY:s.clientY-i.clientY,allowChangeEvent:!0})}_applyChange(t={}){const{panX:e=0,panY:n=0,originX:i=0,originY:s=0,scaleDiff:r=1,allowChangeEvent:o=!1}=t,h=c().translate(e,n).translate(i,s).translate(this.x,this.y).scale(r).translate(-i,-s).scale(this.scale);this.setTransform({allowChangeEvent:o,scale:h.a,x:h.e,y:h.f})}}customElements.define("pinch-zoom",u)}()}(); diff --git a/dist/pinch-zoom.d.ts b/dist/pinch-zoom.d.ts index 6ee1f04..1639e0a 100644 --- a/dist/pinch-zoom.d.ts +++ b/dist/pinch-zoom.d.ts @@ -22,7 +22,10 @@ export interface ScaleToOpts extends ChangeOptions { export default class PinchZoom extends HTMLElement { private _positioningEl?; private _transform; + static readonly observedAttributes: string[]; constructor(); + attributeChangedCallback(name: string, oldValue: string, newValue: string): void; + minScale: number; connectedCallback(): void; readonly x: number; readonly y: number; diff --git a/dist/pinch-zoom.js b/dist/pinch-zoom.js index 2b7c59c..4c3eabb 100644 --- a/dist/pinch-zoom.js +++ b/dist/pinch-zoom.js @@ -215,6 +215,7 @@ var PinchZoom = (function () { 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'; function getDistance(a, b) { if (!b) return 0; @@ -274,6 +275,26 @@ var PinchZoom = (function () { }); this.addEventListener('wheel', event => this._onWheel(event)); } + static get observedAttributes() { return [minScaleAttr]; } + attributeChangedCallback(name, oldValue, newValue) { + if (name === minScaleAttr) { + if (this.scale < this.minScale) { + this.setTransform({ scale: this.minScale }); + } + } + } + get minScale() { + const attrValue = this.getAttribute(minScaleAttr); + if (!attrValue) + return MIN_SCALE; + const value = parseFloat(attrValue); + if (Number.isFinite(value)) + return Math.max(MIN_SCALE, value); + return MIN_SCALE; + } + set minScale(value) { + this.setAttribute(minScaleAttr, String(value)); + } connectedCallback() { this._stageElChange(); } @@ -375,7 +396,7 @@ var PinchZoom = (function () { */ _updateTransform(scale, x, y, allowChangeEvent) { // Avoid scaling to zero - if (scale < MIN_SCALE) + if (scale < this.minScale) return; // Return if there's no change if (scale === this.scale && diff --git a/dist/pinch-zoom.mjs b/dist/pinch-zoom.mjs index 146c9bb..75f27a7 100644 --- a/dist/pinch-zoom.mjs +++ b/dist/pinch-zoom.mjs @@ -30,6 +30,7 @@ function styleInject(css, ref) { 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'; function getDistance(a, b) { if (!b) return 0; @@ -89,6 +90,26 @@ class PinchZoom extends HTMLElement { }); this.addEventListener('wheel', event => this._onWheel(event)); } + static get observedAttributes() { return [minScaleAttr]; } + attributeChangedCallback(name, oldValue, newValue) { + if (name === minScaleAttr) { + if (this.scale < this.minScale) { + this.setTransform({ scale: this.minScale }); + } + } + } + get minScale() { + const attrValue = this.getAttribute(minScaleAttr); + if (!attrValue) + return MIN_SCALE; + const value = parseFloat(attrValue); + if (Number.isFinite(value)) + return Math.max(MIN_SCALE, value); + return MIN_SCALE; + } + set minScale(value) { + this.setAttribute(minScaleAttr, String(value)); + } connectedCallback() { this._stageElChange(); } @@ -190,7 +211,7 @@ class PinchZoom extends HTMLElement { */ _updateTransform(scale, x, y, allowChangeEvent) { // Avoid scaling to zero - if (scale < MIN_SCALE) + if (scale < this.minScale) return; // Return if there's no change if (scale === this.scale && diff --git a/lib/pinch-zoom.ts b/lib/pinch-zoom.ts index ae77070..67a41b4 100644 --- a/lib/pinch-zoom.ts +++ b/lib/pinch-zoom.ts @@ -29,6 +29,8 @@ interface SetTransformOpts extends ChangeOptions { type ScaleRelativeToValues = 'container' | 'content'; +const minScaleAttr = 'min-scale'; + export interface ScaleToOpts extends ChangeOptions { /** Transform origin. Can be a number, or string percent, eg "50%" */ originX?: number | string; @@ -87,6 +89,8 @@ export default class PinchZoom extends HTMLElement { // Current transform. private _transform: SVGMatrix = createMatrix(); + static get observedAttributes() { return [minScaleAttr]; } + constructor() { super(); @@ -112,6 +116,28 @@ export default class PinchZoom extends HTMLElement { this.addEventListener('wheel', event => this._onWheel(event)); } + attributeChangedCallback(name: string, oldValue: string, newValue: string) { + if (name === minScaleAttr) { + if (this.scale < this.minScale) { + this.setTransform({scale: this.minScale}); + } + } + } + + get minScale(): number { + const attrValue = this.getAttribute(minScaleAttr); + if (!attrValue) return MIN_SCALE; + + const value = parseFloat(attrValue); + if (Number.isFinite(value)) return Math.max(MIN_SCALE, value); + + return MIN_SCALE; + } + + set minScale(value: number) { + this.setAttribute(minScaleAttr, String(value)); + } + connectedCallback() { this._stageElChange(); } @@ -244,7 +270,7 @@ export default class PinchZoom extends HTMLElement { */ private _updateTransform(scale: number, x: number, y: number, allowChangeEvent: boolean) { // Avoid scaling to zero - if (scale < MIN_SCALE) return; + if (scale < this.minScale) return; // Return if there's no change if ( diff --git a/package-lock.json b/package-lock.json index 7d39306..ed33efa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1481,8 +1481,7 @@ "pointer-tracker": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.0.3.tgz", - "integrity": "sha512-PURBF4oc45JPECuguX6oPL3pJU5AlF0Nb/4sZdmqzPNAkV4LGL9MJMqb0smWDtmQ0F0KpbxEJn4/Lf5ugN1keQ==", - "dev": true + "integrity": "sha512-PURBF4oc45JPECuguX6oPL3pJU5AlF0Nb/4sZdmqzPNAkV4LGL9MJMqb0smWDtmQ0F0KpbxEJn4/Lf5ugN1keQ==" }, "postcss": { "version": "6.0.23",