Skip to content

Instantly share code, notes, and snippets.

@caesarsol
Last active January 17, 2021 01:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save caesarsol/d229e9db4410b1141e22 to your computer and use it in GitHub Desktop.
Save caesarsol/d229e9db4410b1141e22 to your computer and use it in GitHub Desktop.
function loopFrames(func) {
let loopControl = {
_continueFlag: true,
break: function() { this._continueFlag = false },
continued: function() { return (this._continueFlag === true) }
}
requestAnimationFrame(() => {
func(loopControl)
if (loopControl.continued()) loopFrames(func)
})
}
function switchCase(subject) {
const noop = {when: () => noop, default: () => noop}
return {
when(compare, then) {
if (subject === compare) {
then()
return noop
} else {
return this
}
},
default: (then) => then()
}
}
$.fn.transform = function(transf, ...values) {
let transforms = this.data('transforms')
if (!transforms || transforms.length === 0) {
transforms = new Map()
}
switchCase(transf)
.when('translate', () => {
let [x, y] = values
transforms.set('translate', `${x}px, ${y}px`)
})
.when('rotate', () => {
let [degrees] = values
transforms.set('rotate', `${degrees}deg`)
})
.default(() => {
throw ("Transformation not supported")
})
let transfString = Array.from(transforms.entries()).map(([k,v]) => `${k}(${v})`).join(' ')
this.css('transform', transfString)
this.data('transforms', transforms)
return this
}
$.fn.translate = function(x,y) {
/* Position object absolutely */
requestAnimationFrame(() =>
this.transform('translate', x, y)
)
return this
}//initialPoint
$.fn.rotate = function(degrees, {center} = {}) {
if (center) {
let originCss = `${center.x}px ${center.y}px`
if (!this.css('transform-origin') !== originCss)
this.css('transform-origin', originCss)
}
requestAnimationFrame(() =>
this.transform('rotate', degrees)
)
return this
}
$.fn.translatePoint = function(point) {
return this.translate(point.x, point.y)
}
class PointLogger {
constructor(point, name, {color, centerOn}) {
const width = 5;
this.point = point
this.name = name
this.center = centerOn || new Point()
this.$marker = $('<div>', {
id: this.name,
css: {
position: 'absolute',
top: 0,
left: 0,
width: 1 + width*2,
height: 1 + width*2,
margin: 0,
padding: 0,
background: color,
marginLeft: -(1 + width),
marginTop: -(width),
}
})
this.$label = $('<span>', {
html: this.name,
css: {
display: 'inline-block',
width: 120,
},
append: $('<div>', { css: { background: color }}),
})
this.$coordinateLoggerX = $('<input>', {
width: 120,
})
this.$coordinateLoggerY = $('<input>', {
width: 120,
})
this.$coordinateLoggerM = $('<input>', {
width: 120,
})
}
watch() {
this.point.addWatcher(() => this.log())
}
mount() {
this.$marker.appendTo($(document.body))
$('<div>').append([
this.$label,
this.$coordinateLoggerX,
this.$coordinateLoggerY,
this.$coordinateLoggerM,
]).appendTo($(document.body))
this.watch()
this.log()
return this
}
log() {
this.$marker.translatePoint(this.point.added(this.center))
this.$coordinateLoggerX.val(this.point.x)
this.$coordinateLoggerY.val(this.point.y)
this.$coordinateLoggerM.val(this.point.module())
}
}
class Point {
constructor(x = 0, y = 0) {
this._x = x
this._y = y
// list of callbacks we call after each change
this._watchers = []
this._callWatchers()
}
addWatcher(callback) {
this._watchers.push(callback.bind(this))
}
_callWatchers() {
this._watchers.forEach((callback) => callback(this))
}
addUpdater(updaterFunc) {
updaterFunc(this)
}
get x() { return this._x }
get y() { return this._y }
get position() {
return [this.x, this.y]
}
set(x, y) {
this._x = x
this._y = y
this._callWatchers()
return this
}
setWith(transformX, transformY = null) {
/*
* Accepts one or two callbacks of the form:
* function(coordinateValue, coordinateName)
*
*/
if (!transformY) transformY = transformX
this.set(transformX(this.x, 'x'), transformY(this.y, 'y'))
return this
}
/* Other Points */
setOn(otherPoint) {
this.set(otherPoint.x, otherPoint.y)
return this
}
follow(otherPoint) {
otherPoint.addWatcher(() => {
this.setOn(otherPoint)
})
return this
}
followDampened(otherPoint, dampening) {
let moving = false
let remains = new Point()
otherPoint.addWatcher(() => {
if (moving) return
loopFrames((loop) => {
remains.setOn(otherPoint.subtracted(this))
if (remains.module() > 0.1) {
this.add(remains.multiplied(0.1))
moving = true
} else {
this.setOn(otherPoint)
moving = false
loop.break()
}
})
})
return this
}
/* Math */
add(otherPoint) {
this.setWith((c, cn) => c + otherPoint[cn])
return this
}
subtract(otherPoint) {
this.setWith((c, cn) => c - otherPoint[cn])
return this
}
multiply(scalar) {
this.setWith(c => c * scalar)
return this
}
/* Immutable Math */
duplicated() {
return new Point().setOn(this)
}
added(otherPoint) {
return this.duplicated().add(otherPoint)
}
subtracted(otherPoint) {
return this.duplicated().subtract(otherPoint)
}
multiplied(scalar) {
return this.duplicated().multiply(scalar)
}
/* Scalars */
moduleSquared() {
return Math.pow(this.x, 2) + Math.pow(this.y, 2)
}
module() {
return Math.sqrt(this.moduleSquared())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment