Parse
File Parse automouse.js
This tree is parsed live from the source file.
Classes
-
{{ item.name }}
- {{ key }}
Not Classes
{{ getTree() }}
Comments
{{ getTreeComments() }}
Source
/*
title: Auto Mouse
tags: mouse
Automouse provides a point at the mouse position.
It's the _last known position_ of the mouse; updated upon mousemove.
let point = Point.mouse.position
*/
const getLastMousePos = function(){
return autoMouse.getMousePos(canvas)
}
class AutoMouse {
/* The _AutoMouse_ class cares for much of the mouse pointer functionality
for a stage and the a point.
It's functionality is designed to hook onto preparing `stages` and their canvas.
It exists as a singleton `autoMouse`:
autoMouse = new AutoMouse()
autoMouse == stage.mouse
Point.mouse == stage.mouse
Initiate like this:
Point.mouse?.mount(stage.canvas)
Synonymous to:
installCanvas(canvas, stage){
this.canvasMap.set(canvas, stage)
Point.mouse?.listen(canvas)
}
*/
constructor(parentClass) {
this.parentClass = parentClass
this.mouseCache = {x: 0, y: 0}
// a reference of clicks
this.buttons = {}
// A stash of the movement position, in relative forms.
this.positions = {}
// layer x,y selection - for position
this.zIndex = 'bound'
// this.zIndex = 'local'
this.handlers = {}
this._announce()
this._peristentPoint = new Point
}
_announce() {
/* Wait for a stage prep event. */
events.on('stage:prepare', this.stagePrepareHandler.bind(this))
// addEventListener('stage:prepare', this.stagePrepareHandler.bind(this))
// Announce this module is mountable.
let data = {
target: this
}
events.emit('addon:announce', data)
// dispatchEvent(new CustomEvent('addon:announce', {detail: data}))
}
/* The given event has a stage ready for mounting. Perform any changes,
such as adding a reference to the mouse. */
stagePrepareHandler(ev) {
let d = ev.detail
// console.log('AutoMouse::stagePrepareHandler', d)
return this.announcementResponse(ev, false)
}
announcementResponse(ev, log=true){
let d = ev.detail
if(log) {
console.log('AutoMouse::announcementResponse', ev, d)
}
let stage = d.stage
stage.addComponent('mouse', this)
}
getMousePos(canvas) {
var rect = this.getBoundingClientRect();
let mouseCache = this.mouseCache
return {
x: mouseCache.x - rect.left,
y: mouseCache.y - rect.top
};
}
get xy() {
return this.mouseCache
}
get position() {
/* The `this.position` method returns a new Point, with the mouse XY
(from the last motion).
Use `this.point` for a persisent single Point instance.
*/
return new Point(this.mouseCache)
}
get point() {
/*
Return a persistent _point_, of which is updated rather than replaced
when the mouse state changes.
*/
return this._peristentPoint
}
// [Symbol.toPrimitive](hint) {
// if (hint === 'string') {
// return this.toString()
// }
// return Reflect.apply(...arguments)
// }
getBoundingClientRect(canvas) {
if(canvas === undefined) {
canvas = this.canvas;
}
return canvas.getBoundingClientRect();
}
getListenerMethods() {
if(this._methods) {
return this._methods
}
// console.log('Setting up mouse events')
const methods = {
mousemove: this.mousemoveHandler.bind(this)
, mousedown: this.mousedownHandler.bind(this)
, mouseup: this.mouseupHandler.bind(this)
, wheel: [this.wheelHandler.bind(this), {passive: true}]
}
this._methods = methods
return this._methods;
}
mount(canvas) {
/* Call upon `mount` to initate listening of all events on a canvas. */
if(!this.canvas) {
this.canvas = canvas
}
if(!this.canvas) {
console.warn('automouse:mount - Cannot mouse Listeners; no canvas.')
return
}
let methods = this.getListenerMethods()
for(let k in methods) {
let f = methods[k]
if(isFunction(f)){
this.listen(canvas, k, f)
continue
}
// f is an array.
this.listen(canvas, k, f[0], f[1])
}
}
listen(canvas, eventName, handler, opts) {
canvas.addEventListener(eventName, e => handler(canvas, event), opts);
return this;
}
mousemoveHandler(canvas, event) {
// let bound = this.getBoundingClientRect();
// let x = event.clientX - bound.left - canvas.clientLeft; // same as _local_
// let y = event.clientY - bound.top - canvas.clientTop; // same as _local_
// console.log('Mousemove', event)
// Relative to the stage 0,0
this._lastEvent = event
var rect = canvas.getBoundingClientRect();
let positions = {
local: {
x: event.offsetX
, y: event.offsetY
}
// relative to the page, including canvas offset (e.g 10, 490)
, page: {
x: event.pageX
, y: event.pageY
}
// global; where on the display (e.g. 500, 500)
, screen: {
x: event.screenX
, y: event.screenY
}
, client: {
x: event.clientX
, y: event.clientY
}
, layer: {
x: event.layerX
, y: event.layerY
}
, vector: {
x: event.movementX
, y: event.movementY
}
, absolute: {
x: event.x
, y: event.y
}
, custom: {
x: event.clientX - rect.left - canvas.clientLeft
, y: event.clientY - rect.top - canvas.clientTop
// x: event.pageX - event.target.offsetLeft
// , y: event.pageY - event.target.offsetTop
// x: event.target.offsetLeft + event.layerX
// , y: event.target.offsetTop + event.layerY
}
, bound: {
x: event.clientX - rect.left
, y: event.clientY - rect.top
}
}
this.positions = positions;
let state = positions[this.zIndex] //{x,y}
this.mouseCache = state
this._peristentPoint.x = state.x
this._peristentPoint.y = state.y
this.callHandlers('mousemove', canvas, event)
return this;
}
callHandlers(name, canvas, event) {
let handlers = this.handlers[name]
for (let key in handlers) {
let func = handlers[key]
func(canvas, event)
}
}
mousedownHandler(canvas, event) {
let space = this.getActionSpace(event.button)
space.down = true
this.applyPositionIncrement(space, event)
this.callHandlers('mousedown', canvas, event)
return this;
}
mouseupHandler(canvas, event) {
this.callHandlers('mouseup', canvas, event)
let space = this.getActionSpace(event.button)
space.down = false
this.applyPositionIncrement(space, event)
return this;
}
applyPositionIncrement(space, event) {
space.count += 1
space.position = {
x: event.offsetX
, y: event.offsetY
}
return space
}
wheelHandler(canvas, event) {
let space = this.getActionSpace('wheel', { value: 0 })
this.applyPositionIncrement(space, event)
let delta = {
x: event.deltaX
, y: event.deltaY
, mode: event.deltaMode
}
let direction = event.wheelDelta > 0
space.value += direction? 1: -1
space.delta = delta
let rel = space.relative
if(rel == undefined) {
rel = { x: 0, y: 0 }
}
rel.x += delta.x
rel.y += delta.y
space.relative = rel
this.callHandlers('wheel', canvas, event)
return this;
}
isDown(index) {
let d = this.buttons[index]?.down
return d == undefined? false: d
}
wheelSize(abs=false) {
let v = (this.buttons.wheel?.value) || 1
let sq = isFunction(abs) ? abs(v): v//(v * v)
if(abs==true) { return sq }
return v < 0? -sq: sq
}
/*
A function to help clamp the wheel scroll within a range. By using this
function, the wheel value doesn't exceed the clamp.
let size = mouse.clampWheelSize(5, 20)
The same can be done with the clamp function
let size = clamp(mouse.wheelSize(), 30, 300)
but when the wheel exceeds the clamp, it'll scroll into an unused range.
*/
clampWheelSize(min, max, abs=false) {
let ws = this.wheelSize(abs);
let v = clamp(ws, min, max)
if(this.buttons.wheel!=undefined){
this.buttons.wheel.value = v
}
return v
}
on(canvas, name, handler, opts) {
let hs = this.handlers[name] || []
hs.push(handler)
this.handlers[name] = hs;
let methods = this.getListenerMethods()
if(methods[name] == undefined) {
console.warn('Installing generic handler', name)
this.listen(canvas, name, handler, opts)
}
}
getActionSpace(name, defaults={}) {
let space = this.buttons[name]
if(space == undefined) {
space = { count: 0 }
Object.assign(space, defaults)
this.buttons[name] = space
}
return space
}
speed(previous=this._speedPrevious, delta=1) {
/* Return the speed of the point over a delta.
This is a lot like the velocity functionality, but cut-down for
efficiency. */
let current = this.xy
if(previous == undefined) {
this._speedPrevious = current
return -1
}
this._speedPrevious = current
return this.computeSpeed(previous, current)
}
_moduloTicker = 0
modulatedSpeed(mod=15, previous=this._speedPrevious, delta=1){
this._moduloTicker += 1
let current = this.xy
if(this._moduloTicker % mod == 0) {
this._moduloTickerCompute = this.speed(previous, delta)
return this._moduloTickerCompute
}
this._speedPrevious = current
return this._moduloTickerCompute
}
/* CREATE use a smoothtext function
share for this anf fps text*/
computeSpeed(previous, current, delta=1) {
const dx = (current.x - previous.x) / delta
const dy = (current.y - previous.y) / delta
return dx * dx + dy * dy
}
}
Polypoint.head.install(AutoMouse)
const autoMouse = (new AutoMouse(Point))
/*
Install the `Point.mouse` static method.
*/
Polypoint.head.static('Point', {
mouse: {
value: autoMouse
// , writable: true
// , enumerable: false
// , configurable: true
}
})
addEventListener('stage:prepare', (e)=>{
/* Upon the event `stage:prepare`, create a listener to monitor
the system resize event. Call the stage `resizeHandler` when an event
occurs.
*/
Point.mouse?.mount(e.detail.stage.canvas)
// this.stage.resizeHandler(e)
});
try{
// Point.mouse = autoMouse
Point.pointArray = pointArray
} catch {
console.warn('pointArray is not defined')
}
copy