diff --git a/index.js b/index.js index 2a09d0a..cadf71d 100644 --- a/index.js +++ b/index.js @@ -16,13 +16,14 @@ tmpl.innerHTML = ` ` -const startPositions = new WeakMap() -const dragStartPositions = new WeakMap() -const constructedElements = new WeakMap() +const startPositions: WeakMap = new WeakMap() +const dragStartPositions: WeakMap = new WeakMap() +const constructedElements: WeakMap = new WeakMap() function moveCropArea(event: MouseEvent | KeyboardEvent) { const el = event.currentTarget if (!(el instanceof ImageCropElement)) return + const {box, image} = constructedElements.get(el) || {} let deltaX = 0 let deltaY = 0 @@ -44,12 +45,12 @@ function moveCropArea(event: MouseEvent | KeyboardEvent) { } if (deltaX !== 0 || deltaY !== 0) { - const x = Math.min(Math.max(0, el.box.offsetLeft + deltaX), el.image.width - el.box.offsetWidth) - const y = Math.min(Math.max(0, el.box.offsetTop + deltaY), el.image.height - el.box.offsetHeight) - el.box.style.left = `${x}px` - el.box.style.top = `${y}px` + const x = Math.min(Math.max(0, box.offsetLeft + deltaX), image.width - box.offsetWidth) + const y = Math.min(Math.max(0, box.offsetTop + deltaY), image.height - box.offsetHeight) + box.style.left = `${x}px` + box.style.top = `${y}px` - fireChangeEvent(el, {x, y, width: el.box.offsetWidth, height: el.box.offsetHeight}) + fireChangeEvent(el, {x, y, width: box.offsetWidth, height: box.offsetHeight}) } if (event instanceof MouseEvent) { @@ -66,6 +67,7 @@ function updateCropArea(event: MouseEvent | KeyboardEvent) { const el = target.closest('image-crop') if (!(el instanceof ImageCropElement)) return + const {box} = constructedElements.get(el) || {} const rect = el.getBoundingClientRect() let deltaX, deltaY, delta @@ -74,9 +76,9 @@ function updateCropArea(event: MouseEvent | KeyboardEvent) { if (event.key === '-') delta = -10 if (event.key === '=') delta = +10 if (!delta) return - deltaX = el.box.offsetWidth + delta - deltaY = el.box.offsetHeight + delta - startPositions.set(el, {startX: el.box.offsetLeft, startY: el.box.offsetTop}) + deltaX = box.offsetWidth + delta + deltaY = box.offsetHeight + delta + startPositions.set(el, {startX: box.offsetLeft, startY: box.offsetTop}) } else if (event instanceof MouseEvent) { const pos = startPositions.get(el) if (!pos) return @@ -93,6 +95,7 @@ function startUpdate(event: MouseEvent) { const el = currentTarget.closest('image-crop') if (!(el instanceof ImageCropElement)) return + const {box} = constructedElements.get(el) || {} const target = event.target if (!(target instanceof HTMLElement)) return @@ -104,8 +107,8 @@ function startUpdate(event: MouseEvent) { if (['nw', 'se'].indexOf(direction) >= 0) el.classList.add('nwse') if (['ne', 'sw'].indexOf(direction) >= 0) el.classList.add('nesw') startPositions.set(el, { - startX: el.box.offsetLeft + (['se', 'ne'].indexOf(direction) >= 0 ? 0 : el.box.offsetWidth), - startY: el.box.offsetTop + (['se', 'sw'].indexOf(direction) >= 0 ? 0 : el.box.offsetHeight) + startX: box.offsetLeft + (['se', 'ne'].indexOf(direction) >= 0 ? 0 : box.offsetWidth), + startY: box.offsetTop + (['se', 'sw'].indexOf(direction) >= 0 ? 0 : box.offsetHeight) }) updateCropArea(event) } else { @@ -118,20 +121,21 @@ function updateDimensions(target, deltaX, deltaY, reposition = true) { let newSide = Math.max(Math.abs(deltaX), Math.abs(deltaY), 10) const pos = startPositions.get(target) if (!pos) return + const {box, image} = constructedElements.get(target) || {} newSide = Math.min( newSide, - deltaY > 0 ? target.image.height - pos.startY : pos.startY, - deltaX > 0 ? target.image.width - pos.startX : pos.startX + deltaY > 0 ? image.height - pos.startY : pos.startY, + deltaX > 0 ? image.width - pos.startX : pos.startX ) - const x = reposition ? Math.round(Math.max(0, deltaX > 0 ? pos.startX : pos.startX - newSide)) : target.box.offsetLeft - const y = reposition ? Math.round(Math.max(0, deltaY > 0 ? pos.startY : pos.startY - newSide)) : target.box.offsetTop + const x = reposition ? Math.round(Math.max(0, deltaX > 0 ? pos.startX : pos.startX - newSide)) : box.offsetLeft + const y = reposition ? Math.round(Math.max(0, deltaY > 0 ? pos.startY : pos.startY - newSide)) : box.offsetTop - target.box.style.left = `${x}px` - target.box.style.top = `${y}px` + box.style.left = `${x}px` + box.style.top = `${y}px` - target.box.style.width = `${newSide}px` - target.box.style.height = `${newSide}px` + box.style.width = `${newSide}px` + box.style.height = `${newSide}px` fireChangeEvent(target, {x, y, width: newSide, height: newSide}) } @@ -147,7 +151,7 @@ function imageReady(event: Event) { } function setInitialPosition(el) { - const image = el.image + const {image} = constructedElements.get(el) || {} const side = Math.round(image.clientWidth > image.clientHeight ? image.clientHeight : image.clientWidth) startPositions.set(el, { startX: (image.clientWidth - side) / 2, @@ -167,7 +171,8 @@ function stopUpdate(event: MouseEvent) { } function fireChangeEvent(target: ImageCropElement, result: {x: number, y: number, width: number, height: number}) { - const ratio = target.image.naturalWidth / target.image.width + const {image} = constructedElements.get(target) || {} + const ratio = image.naturalWidth / image.width for (const key in result) { const value = Math.round(result[key] * ratio) result[key] = value @@ -179,31 +184,23 @@ function fireChangeEvent(target: ImageCropElement, result: {x: number, y: number } class ImageCropElement extends HTMLElement { - image: HTMLImageElement - box: HTMLElement - connectedCallback() { if (constructedElements.has(this)) return - constructedElements.set(this, true) - this.appendChild(document.importNode(tmpl.content, true)) - - const image = this.querySelector('img') - if (!(image instanceof HTMLImageElement)) return - this.image = image - const box = this.querySelector('[data-crop-box]') if (!(box instanceof HTMLElement)) return - this.box = box + const image = this.querySelector('img') + if (!(image instanceof HTMLImageElement)) return + constructedElements.set(this, {box, image}) - this.image.addEventListener('load', imageReady) + image.addEventListener('load', imageReady) this.addEventListener('mouseleave', stopUpdate) this.addEventListener('mouseup', stopUpdate) - this.box.addEventListener('mousedown', startUpdate) + box.addEventListener('mousedown', startUpdate) this.addEventListener('keydown', moveCropArea) this.addEventListener('keydown', updateCropArea) - if (this.src) this.image.src = this.src + if (this.src) image.src = this.src } static get observedAttributes() { @@ -235,9 +232,10 @@ class ImageCropElement extends HTMLElement { } attributeChangedCallback(attribute: string, oldValue: string, newValue: string) { + const {image} = constructedElements.get(this) || {} if (attribute === 'src') { this.loaded = false - if (this.image) this.image.src = newValue + if (image) image.src = newValue } } } diff --git a/index.js.flow b/index.js.flow index 9f4296d..47d9cbe 100644 --- a/index.js.flow +++ b/index.js.flow @@ -2,9 +2,6 @@ declare module '@github/image-crop-element' { declare export default class ImageCropElement extends HTMLElement { - image: HTMLImageElement; - box: HTMLElement; - get src(): ?string; set src(val: ?string): void; get loaded(): boolean;