Last active
July 12, 2021 09:53
-
-
Save iyobo/4d2ff6a370011675ebf1d561e830be1e to your computer and use it in GitHub Desktop.
A PixiViewport plugin: Better Scroll/Zoom Behavior on trackpads with two-finger scoll and zoom.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Plugin, Viewport} from 'pixi-viewport'; | |
import {DisplayObject} from 'pixi.js'; | |
type Options = { | |
moveSpeed: number; | |
moveReverse: boolean; | |
zoomSpeed: number; | |
zoomReverse: boolean; | |
limitToObjectBounds?: DisplayObject; | |
limitToObjectBoundsBy?: number; | |
}; | |
const defaults: Options = { | |
moveSpeed: 1, | |
moveReverse: false, | |
zoomSpeed: 1, | |
zoomReverse: false, | |
limitToObjectBoundsBy: 500 | |
}; | |
/** | |
Better Scroll/Zoom Behavior on trackpads or devices that with two-finger scoll and zoom. | |
Usage example: | |
const viewport = new Viewport({ | |
screenWidth: window.innerWidth, | |
screenHeight: window.innerHeight, | |
worldWidth: 1000, | |
worldHeight: 1000, | |
interaction: app.renderer.plugins.interaction, | |
}); | |
... | |
viewport.plugins.add('wheel', new PinchToZoomAndMove(viewport, { | |
zoomReverse: true | |
})); | |
**/ | |
export class PinchToZoomAndMove extends Plugin { | |
private parent: Viewport; | |
private options: Options; | |
private limitToObjectBounds: DisplayObject; | |
private limitToObjectBoundsBy: number; | |
private moveReverse: boolean; | |
private zoomReverse: boolean; | |
constructor(parent: Viewport, options: Partial<Options>) { | |
super(parent); | |
this.parent = parent; | |
this.options = {...defaults, ...options}; | |
this.moveReverse = this.options.moveReverse ? 1 : -1; | |
this.zoomReverse = this.options.zoomReverse ? 1 : -1; | |
this.limitToObjectBounds = this.options.limitToObjectBounds; | |
this.limitToObjectBoundsBy = this.options.limitToObjectBoundsBy; | |
} | |
wheel(event: WheelEvent) { | |
if (event.ctrlKey) { | |
this.zoom(event); | |
} else { | |
this.pan(event); | |
} | |
} | |
private pan(event: WheelEvent) { | |
// console.log('pan') | |
this.parent.x += | |
event.deltaX * this.options.moveSpeed * this.moveReverse; | |
this.parent.y += | |
event.deltaY * this.options.moveSpeed * this.moveReverse; | |
// FIXME: Attempt to limit panning to specific bounds. WIP. Can't figure this out. | |
if (this.limitToObjectBounds) { | |
// const b = this.parent.getBounds(); | |
// const p = this.limitToObjectBoundsBy; | |
// | |
// | |
// const leftLimit = b.x - p; | |
// // @ts-ignore | |
// const rightLimit = b.x - b.width + p; | |
// const topLimit = b.y - p; | |
// // @ts-ignore | |
// const bottomLimit = b.y + b.height + p; | |
// if (this.parent.x < leftLimit) this.parent.x = leftLimit; | |
// if (this.parent.x > rightLimit) this.parent.x = rightLimit; | |
// if (this.parent.y < topLimit) this.parent.y = topLimit; | |
// if (this.parent.y > bottomLimit) this.parent.y = bottomLimit; | |
// console.log('pan limit', { | |
// x: this.parent.x, | |
// y: this.parent.y, | |
// xfac: this.parent.x - b.x, | |
// xScale: this.parent.scale.x, | |
// xKol: b.x / this.parent.scale.x, | |
// dx: event.deltaX, | |
// dy: event.deltaY, | |
// b, leftLimit, rightLimit, topLimit, bottomLimit, p | |
// },); | |
} | |
} | |
minZoom = 0.25; | |
maxZoom = 0.95; | |
private zoom(event: WheelEvent) { | |
const delta = 1 - ((this.zoomReverse * event.deltaY * this.options.zoomSpeed) / 250); | |
const point = (this.parent as any).input.getPointerPosition(event); | |
const oldPoint = this.parent.toLocal(point); | |
this.parent.scale.x *= delta; | |
this.parent.scale.y *= delta; | |
// FIXME: zoom clamping (WIP) | |
let doProtoMove = true; | |
if (this.parent.scale.x < this.minZoom) { | |
this.parent.scale.x = this.minZoom; | |
doProtoMove = false; | |
} else if (this.parent.scale.x > this.maxZoom) { | |
this.parent.scale.x = this.maxZoom; | |
doProtoMove = false; | |
} | |
if (this.parent.scale.y < this.minZoom) { | |
this.parent.scale.y = this.minZoom; | |
doProtoMove = false; | |
} else if (this.parent.scale.y > this.maxZoom) { | |
this.parent.scale.y = this.maxZoom; | |
doProtoMove = false; | |
} | |
const newPoint = this.parent.toGlobal(oldPoint); | |
this.parent.x += point.x - newPoint.x; | |
this.parent.y += point.y - newPoint.y; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You should emit the wheel event inside the wheel method:
And you can take advantage of the zoomClamp build in mechanism.