Skip to content

Commit

Permalink
feat(events): add originalEvent to custom events detail
Browse files Browse the repository at this point in the history
Fixes gh-522
  • Loading branch information
timmywil committed Nov 17, 2020
1 parent 373819f commit 82722ff
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 17 deletions.
49 changes: 35 additions & 14 deletions src/panzoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
*/
import './polyfills'

import { PanOptions, PanzoomEvent, PanzoomObject, PanzoomOptions, ZoomOptions } from './types'
import {
PanOptions,
PanzoomEvent,
PanzoomEventDetail,
PanzoomObject,
PanzoomOptions,
ZoomOptions
} from './types'
import { addPointer, getDistance, getMiddle, removePointer } from './pointers'
import { destroyPointer, eventNames, onPointer } from './events'
import { getDimensions, setStyle, setTransform, setTransition } from './css'
Expand Down Expand Up @@ -131,17 +138,20 @@ function Panzoom(
pan(options.startX, options.startY, { animate: false })
})

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function trigger(eventName: PanzoomEvent, detail: any, opts: PanzoomOptions) {
function trigger(eventName: PanzoomEvent, detail: PanzoomEventDetail, opts: PanzoomOptions) {
if (opts.silent) {
return
}
const event = new CustomEvent(eventName, { detail })
elem.dispatchEvent(event)
}

function setTransformWithEvent(eventName: PanzoomEvent, opts: PanzoomOptions) {
const value = { x, y, scale, isSVG }
function setTransformWithEvent(
eventName: PanzoomEvent,
opts: PanzoomOptions,
originalEvent?: PanzoomEventDetail['originalEvent']
) {
const value = { x, y, scale, isSVG, originalEvent }
requestAnimationFrame(() => {
if (typeof opts.animate === 'boolean') {
if (opts.animate) {
Expand Down Expand Up @@ -262,17 +272,26 @@ function Panzoom(
return result
}

function pan(toX: number | string, toY: number | string, panOptions?: PanOptions) {
function pan(
toX: number | string,
toY: number | string,
panOptions?: PanOptions,
originalEvent?: PanzoomEventDetail['originalEvent']
) {
const result = constrainXY(toX, toY, scale, panOptions)
const opts = result.opts

x = result.x
y = result.y

return setTransformWithEvent('panzoompan', opts)
return setTransformWithEvent('panzoompan', opts, originalEvent)
}

function zoom(toScale: number, zoomOptions?: ZoomOptions) {
function zoom(
toScale: number,
zoomOptions?: ZoomOptions,
originalEvent?: PanzoomEventDetail['originalEvent']
) {
const result = constrainScale(toScale, zoomOptions)
const opts = result.opts
if (!opts.force && opts.disableZoom) {
Expand All @@ -294,7 +313,7 @@ function Panzoom(
x = panResult.x
y = panResult.y
scale = toScale
return setTransformWithEvent('panzoomzoom', opts)
return setTransformWithEvent('panzoomzoom', opts, originalEvent)
}

function zoomInOut(isIn: boolean, zoomOptions?: ZoomOptions) {
Expand All @@ -313,7 +332,8 @@ function Panzoom(
function zoomToPoint(
toScale: number,
point: { clientX: number; clientY: number },
zoomOptions?: ZoomOptions
zoomOptions?: ZoomOptions,
originalEvent?: PanzoomEventDetail['originalEvent']
) {
const dims = getDimensions(elem)

Expand Down Expand Up @@ -366,7 +386,7 @@ function Panzoom(
y: (clientY / effectiveArea.height) * (effectiveArea.height * toScale)
}

return zoom(toScale, { animate: false, ...zoomOptions, focal })
return zoom(toScale, { animate: false, ...zoomOptions, focal }, originalEvent)
}

function zoomWithWheel(event: WheelEvent, zoomOptions?: ZoomOptions) {
Expand Down Expand Up @@ -412,7 +432,7 @@ function Panzoom(
origX = x
origY = y

trigger('panzoomstart', { x, y, scale }, options)
trigger('panzoomstart', { x, y, scale, isSVG, originalEvent: event }, options)

// This works whether there are multiple
// pointers or not
Expand Down Expand Up @@ -448,15 +468,16 @@ function Panzoom(
origY + (current.clientY - startClientY) / scale,
{
animate: false
}
},
event
)
}

function handleUp(event: PointerEvent) {
// Don't call panzoomend when panning with 2 touches
// until both touches end
if (pointers.length === 1) {
trigger('panzoomend', { x, y, scale }, options)
trigger('panzoomend', { x, y, scale, isSVG, originalEvent: event }, options)
}
// Note: don't remove all pointers
// Can restart without having to reinitiate all of them
Expand Down
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export type PanzoomEvent =
| 'panzoomreset'
| 'panzoomend'

export interface PanzoomEventDetail {
x: number
y: number
scale: number
isSVG: boolean
originalEvent: PointerEvent | TouchEvent | MouseEvent
}

interface MiscOptions {
/** Whether to animate transitions */
animate?: boolean
Expand Down
6 changes: 5 additions & 1 deletion tasks/EVENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ elem.addEventListener('panzoomchange', (event) => {

### Notes about all events

- The event object passed as an argument to the listener will always have a `detail` property with the current `x`, `y`, and `scale` values.
- The event object passed as an argument to the listener will always have a `detail` object with the following properties:
- The current `x` value
- The current `y` value
- The current `scale`
- An `originalEvent` property with the original event that triggered the panzoom event, if applicable. For example, the `originalEvent` property for a `panzoomstart` event would be either a `pointerdown`, `touchstart`, or `mousedown` event.
- Events can be silenced when the `silent` option is set to `true`, either globally or when passed to `pan`, any `zoom` method, or `reset`.
- Avoid putting too much logic in these event handlers as it could effect the performance of panning or zooming.

Expand Down
47 changes: 45 additions & 2 deletions test/unit/panzoom.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { strict as assert, throws } from 'assert'

import Panzoom from '../../src/panzoom'
import { PanzoomEventDetail } from '../../src/types'

function assertStyleMatches(elem: HTMLElement | SVGElement, name: string, value: string) {
const capName = name[0].toUpperCase() + name.slice(1)
Expand Down Expand Up @@ -53,6 +54,48 @@ describe('Panzoom', () => {
assertStyleMatches(div, 'transformOrigin', '50% 50%')
document.body.removeChild(div)
})
it('has expected properties on event detail for panzoom events', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const events: any = {} // eslint-disable-line
const addEvent = Element.prototype.addEventListener
const removeEvent = Element.prototype.removeEventListener
// eslint-disable-next-line
Element.prototype.addEventListener = function (event: any, fn: any, options: any) {
events[event] = fn
addEvent.call(this, event, fn, options)
}
// eslint-disable-next-line
Element.prototype.removeEventListener = function (event: any, fn: any, options: any) {
delete events[event]
removeEvent.call(this, event, fn, options)
}
Panzoom(div)
assert(Object.keys(events).length > 0)
function checkEvent(event: CustomEvent<PanzoomEventDetail>) {
console.log(`${event.type} called`)
assert.ok(event.detail, 'Event detail exists')
assert.ok(event.detail.hasOwnProperty('x'), 'Event detail has x value')
assert.ok(event.detail.hasOwnProperty('y'), 'Event detail has y value')
assert.ok(event.detail.hasOwnProperty('scale'), 'Event detail has scale value')
assert.ok(event.detail.hasOwnProperty('isSVG'), 'Event detail has isSVG value')
assert.ok(
event.detail.hasOwnProperty('originalEvent'),
'Event detail has originalEvent value'
)
}
;(div as any).addEventListener('panzoomstart', checkEvent)
;(div as any).addEventListener('panzoomchange', checkEvent)
;(div as any).addEventListener('panzoompan', checkEvent)
;(div as any).addEventListener('panzoomzoom', checkEvent)
;(div as any).addEventListener('panzoomend', checkEvent)
div.dispatchEvent(new PointerEvent('pointerdown'))
document.dispatchEvent(new PointerEvent('pointermove', { clientX: 10, clientY: 10 }))
document.dispatchEvent(new PointerEvent('pointerup'))
Element.prototype.addEventListener = addEvent
Element.prototype.removeEventListener = removeEvent
document.body.removeChild(div)
})
it('removes the events when using the destroy method', () => {
const div = document.createElement('div')
document.body.appendChild(div)
Expand All @@ -75,11 +118,11 @@ describe('Panzoom', () => {
console.log('panzoomend called')
assert.ok('panzoomend called on pan')
}
div.addEventListener('panzoomend', endListener)
;(div as any).addEventListener('panzoomend', endListener)
div.dispatchEvent(new PointerEvent('pointerdown'))
document.dispatchEvent(new PointerEvent('pointerup'))
panzoom.destroy()
div.removeEventListener('panzoomend', endListener)
;(div as any).removeEventListener('panzoomend', endListener)
assert(Object.keys(events).length === 0)
Element.prototype.addEventListener = addEvent
Element.prototype.removeEventListener = removeEvent
Expand Down

0 comments on commit 82722ff

Please sign in to comment.