Skip to content

Instantly share code, notes, and snippets.

@aarongeorge
Last active June 24, 2020 00:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aarongeorge/2e1c970a4d0032e98fa2eb51bb40e00b to your computer and use it in GitHub Desktop.
Save aarongeorge/2e1c970a4d0032e98fa2eb51bb40e00b to your computer and use it in GitHub Desktop.
A library for dragging DOM elements around
/**
* Draggie.ts
*
* A library for dragging elements around
*
* Github: https://gist.github.com/aarongeorge/2e1c970a4d0032e98fa2eb51bb40e00b
* Demo: https://codepen.io/AaronGeorge/full/oGZoBg/
*/
interface Constraints {
x: { min: number, max: number }
y: { min: number, max: number }
}
interface Coordinates2D {
x: number
y: number
}
const defaultConstraints: Constraints = {
x: { min: -Infinity, max: Infinity },
y: { min: -Infinity, max: Infinity }
}
const Draggie = class {
constraints: Constraints
dragPosition: Coordinates2D
element: HTMLElement
isDragging: boolean
mousePosition: Coordinates2D
constructor (element: HTMLElement, constraints = defaultConstraints) {
this.element = element
this.setConstraints(constraints)
this.isDragging = false
this.inputDown = this.inputDown.bind(this)
this.inputMove = this.inputMove.bind(this)
this.inputUp = this.inputUp.bind(this)
const { a, b, c, d, e, f } = this.getMatrix(this.element)
this.applyMatrix(this.element, a, b, c, d, e, f)
this.dragPosition = { x: e, y: f }
this.bindEvents()
}
inputDown (e) {
this.mousePosition = {
x: e.pageX ? e.pageX : e.targetTouches[0].pageX,
y: e.pageY ? e.pageY : e.targetTouches[0].pageY
}
this.isDragging = true
}
inputMove (e) {
if (this.isDragging) {
const currentTransform = this.getMatrix(this.element)
const newTransform = {
x: currentTransform.e + ((e.pageX ? e.pageX : e.targetTouches[0].pageX) - this.mousePosition.x),
y: currentTransform.f + ((e.pageY ? e.pageY : e.targetTouches[0].pageY) - this.mousePosition.y)
}
const clampedX = Math.min(Math.max(newTransform.x, this.constraints.x.min), this.constraints.x.max)
const clampedY = Math.min(Math.max(newTransform.y, this.constraints.y.min), this.constraints.y.max)
this.applyMatrix(this.element, currentTransform.a, currentTransform.b, currentTransform.c, currentTransform.d, clampedX, clampedY)
this.mousePosition = {
x: e.pageX ? e.pageX : e.targetTouches[0].pageX,
y: e.pageY ? e.pageY : e.targetTouches[0].pageY
}
this.dragPosition = { x: clampedX, y: clampedY }
}
}
inputUp () {
this.isDragging = false
}
bindEvents () {
this.element.addEventListener('mousedown', this.inputDown)
this.element.addEventListener('touchstart', this.inputDown)
document.addEventListener('mousemove', this.inputMove)
document.addEventListener('touchmove', this.inputMove)
document.addEventListener('mouseup', this.inputUp)
document.addEventListener('touchend', this.inputUp)
}
unbindEvents () {
this.element.removeEventListener('mousedown', this.inputDown)
this.element.removeEventListener('touchstart', this.inputDown)
document.removeEventListener('mousemove', this.inputMove)
document.removeEventListener('touchmove', this.inputMove)
document.removeEventListener('mouseup', this.inputUp)
document.removeEventListener('touchend', this.inputUp)
}
applyMatrix (element, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
element.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, ${e}, ${f})`
}
setConstraints (userConstraints: Constraints) {
this.constraints = Object.assign({}, defaultConstraints, userConstraints)
}
getMatrix (element: HTMLElement) {
return new DOMMatrix(window.getComputedStyle(element).transform)
}
}
/**
* Initialise Draggie
*/
// Create instance of `Draggie`
const test = new Draggie(document.querySelector('div'))
// Bind horizontal constraints
document.querySelector('[data-value=horizontal]').addEventListener('click', () => {
const {f} = test.getMatrix(test.element)
test.setConstraints({
x: { min: -Infinity, max: Infinity },
y: { min: f, max: f }
})
test.element.innerText = 'Drag Me (Horizontal)'
})
// Bind vertical constraints
document.querySelector('[data-value=vertical]').addEventListener('click', () => {
const {e} = test.getMatrix(test.element)
test.setConstraints({
x: { min: e, max: e },
y: { min: -Infinity, max: Infinity }
})
test.element.innerText = 'Drag Me (Vertical)'
})
// Bind remove constraints
document.querySelector('[data-value=remove]').addEventListener('click', () => {
test.setConstraints(defaultConstraints)
test.element.innerText = 'Drag Me (Unconstrained)'
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment