diff --git a/docs/parameterData.json b/docs/parameterData.json index b99217ebe8..3fc79dbeed 100644 --- a/docs/parameterData.json +++ b/docs/parameterData.json @@ -325,7 +325,7 @@ "windowResized": { "overloads": [ [ - "UIEvent?" + "Event?" ] ] }, diff --git a/src/core/environment.js b/src/core/environment.js index 7f117993c7..c4e9ad2f8a 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -9,7 +9,7 @@ import * as C from './constants'; import { Vector } from '../math/p5.Vector'; -function environment(p5, fn){ +function environment(p5, fn, lifecycles){ const standardCursors = [C.ARROW, C.CROSS, C.HAND, C.MOVE, C.TEXT, C.WAIT]; fn._frameRate = 0; @@ -19,6 +19,19 @@ function environment(p5, fn){ const _windowPrint = window.print; let windowPrintDisabled = false; + lifecycles.presetup = function(){ + const events = [ + 'resize' + ]; + + for(const event of events){ + window.addEventListener(event, this[`_on${event}`].bind(this), { + passive: false, + signal: this._removeSignal + }); + } + }; + /** * Displays text in the web browser's console. * @@ -715,7 +728,7 @@ function environment(p5, fn){ * can be used for debugging or other purposes. * * @method windowResized - * @param {UIEvent} [event] optional resize Event. + * @param {Event} [event] optional resize Event. * @example *
* @@ -770,10 +783,9 @@ function environment(p5, fn){ fn._onresize = function(e) { this.windowWidth = getWindowWidth(); this.windowHeight = getWindowHeight(); - const context = this._isGlobal ? window : this; let executeDefault; - if (typeof context.windowResized === 'function') { - executeDefault = context.windowResized(e); + if (this.userDefinedFunctions.windowResized) { + executeDefault = this.userDefinedFunctions.windowResized(e); if (executeDefault !== undefined && !executeDefault) { e.preventDefault(); } diff --git a/src/core/main.js b/src/core/main.js index a5c9a6c93d..d1622983a0 100644 --- a/src/core/main.js +++ b/src/core/main.js @@ -64,23 +64,9 @@ class p5 { this._startListener = null; this._initializeInstanceVariables(); this._events = { - // keep track of user-events for unregistering later - pointerdown: null, - pointerup: null, - pointermove: null, - dragend: null, - dragover: null, - click: null, - dblclick: null, - mouseover: null, - mouseout: null, - keydown: null, - keyup: null, - keypress: null, - wheel: null, - resize: null, - blur: null }; + this._removeAbortController = new AbortController(); + this._removeSignal = this._removeAbortController.signal; this._millisStart = -1; this._recording = false; @@ -88,13 +74,6 @@ class p5 { this._lcg_random_state = null; // NOTE: move to random.js this._gaussian_previous = false; // NOTE: move to random.js - if (window.DeviceOrientationEvent) { - this._events.deviceorientation = null; - } - if (window.DeviceMotionEvent && !window._isNodeWebkit) { - this._events.devicemotion = null; - } - // ensure correct reporting of window dimensions this._updateWindowSize(); @@ -156,16 +135,6 @@ class p5 { p5._checkForUserDefinedFunctions(this); } - // Bind events to window (not using container div bc key events don't work) - for (const e in this._events) { - const f = this[`_on${e}`]; - if (f) { - const m = f.bind(this); - window.addEventListener(e, m, { passive: false }); - this._events[e] = m; - } - } - const focusHandler = () => { this.focused = true; }; @@ -208,6 +177,20 @@ class p5 { } } + _userDefinedFunctions = {}; + userDefinedFunctions = new Proxy({}, { + get: (target, prop) => { + if(!this._userDefinedFunctions[prop]){ + const context = this._isGlobal ? window : this; + if(typeof context[prop] === 'function'){ + this._userDefinedFunctions[prop] = context[prop].bind(this); + } + } + + return this._userDefinedFunctions[prop]; + } + }) + async #_start() { if (this.hitCriticalError) return; // Find node if id given @@ -247,18 +230,13 @@ class p5 { } if (this.hitCriticalError) return; - // unhide any hidden canvases that were created const canvases = document.getElementsByTagName('canvas'); - - // Apply touchAction = 'none' to canvases if pointer events exist - if (Object.keys(this._events).some(event => event.startsWith('pointer'))) { - for (const k of canvases) { - k.style.touchAction = 'none'; - } - } - - for (const k of canvases) { + // Apply touchAction = 'none' to canvases to prevent scrolling + // when dragging on canvas elements + k.style.touchAction = 'none'; + + // unhide any hidden canvases that were created if (k.dataset.hidden === 'true') { k.style.visibility = ''; delete k.dataset.hidden; @@ -380,19 +358,14 @@ class p5 { window.cancelAnimationFrame(this._requestAnimId); } - // unregister events sketch-wide - for (const ev in this._events) { - window.removeEventListener(ev, this._events[ev]); - } + // Send sketch remove signal + this._removeAbortController.abort(); - // remove DOM elements created by p5, and listeners + // remove DOM elements created by p5 for (const e of this._elements) { if (e.elt && e.elt.parentNode) { e.elt.parentNode.removeChild(e.elt); } - for (const elt_ev in e._events) { - e.elt.removeEventListener(elt_ev, e._events[elt_ev]); - } } // Run `remove` hooks @@ -422,9 +395,9 @@ class p5 { } async _runLifecycleHook(hookName) { - for(const hook of p5.lifecycleHooks[hookName]){ - await hook.call(this); - } + await Promise.all(p5.lifecycleHooks[hookName].map(hook => { + return hook.call(this); + })); } _initializeInstanceVariables() { diff --git a/src/dom/p5.Element.js b/src/dom/p5.Element.js index 0265a64c42..b824d4ce71 100644 --- a/src/dom/p5.Element.js +++ b/src/dom/p5.Element.js @@ -67,23 +67,22 @@ class Element { // `_elements` array. But when an element lives inside an off-screen // `p5.Graphics` layer, `this._pInst` is that wrapper Graphics object // instead. The wrapper keeps a back–pointer (`_pInst`) to the real - // sketch but has no `_elements` array of its own. - - let sketch = this._pInst; - + // sketch but has no `_elements` array of its own. + + let sketch = this._pInst; + // If `sketch` doesn’t own an `_elements` array it means - // we’re still at the graphics-layer “wrapper”. + // we’re still at the graphics-layer “wrapper”. // Jump one level up to the real p5 sketch stored in sketch._pInst. if (sketch && !sketch._elements && sketch._pInst) { - sketch = sketch._pInst; // climb one level up + sketch = sketch._pInst; // climb one level up } - + if (sketch && sketch._elements) { // only if the array exists const i = sketch._elements.indexOf(this); if (i !== -1) sketch._elements.splice(i, 1); } - // deregister events for (let ev in this._events) { @@ -1865,7 +1864,7 @@ class Element { return this; } - /** + /** * Calls a function when a file is dragged over the element. * * Calling `myElement.dragOver(false)` disables the function. @@ -2416,7 +2415,10 @@ class Element { Element._detachListener(ev, ctx); } const f = fxn.bind(ctx); - ctx.elt.addEventListener(ev, f, false); + ctx.elt.addEventListener(ev, f, { + capture: false, + signal: ctx._pInst._removeSignal + }); ctx._events[ev] = f; } diff --git a/src/events/acceleration.js b/src/events/acceleration.js index 3b0d5d8dc7..a8228e107c 100644 --- a/src/events/acceleration.js +++ b/src/events/acceleration.js @@ -6,7 +6,21 @@ * @main Events */ -function acceleration(p5, fn){ +function acceleration(p5, fn, lifecycles){ + lifecycles.presetup = function(){ + const events = [ + 'deviceorientation', + 'devicemotion' + ]; + + for(const event of events){ + window.addEventListener(event, this[`_on${event}`].bind(this), { + passive: false, + signal: this._removeSignal + }); + } + }; + /** * The system variable deviceOrientation always contains the orientation of * the device. The value of this variable will either be set 'landscape' @@ -449,9 +463,7 @@ function acceleration(p5, fn){ * *
*/ - fn.setMoveThreshold = function (val) { - // p5._validateParameters('setMoveThreshold', arguments); move_threshold = val; }; @@ -491,9 +503,7 @@ function acceleration(p5, fn){ * * */ - fn.setShakeThreshold = function (val) { - // p5._validateParameters('setShakeThreshold', arguments); shake_threshold = val; }; @@ -614,7 +624,6 @@ function acceleration(p5, fn){ * * */ - fn._ondeviceorientation = function (e) { this._updatePRotations(); @@ -624,6 +633,7 @@ function acceleration(p5, fn){ this.rotationZ = this._fromDegrees(e.alpha); this._handleMotion(); }; + fn._ondevicemotion = function (e) { this._updatePAccelerations(); this.accelerationX = e.acceleration.x * 2; @@ -631,26 +641,33 @@ function acceleration(p5, fn){ this.accelerationZ = e.acceleration.z * 2; this._handleMotion(); }; + fn._handleMotion = function () { - if (window.orientation === 90 || window.orientation === -90) { + if ( + screen.orientation.type === 'landscape-primary' || + screen.orientation.type === 'landscape-secondary' + ) { this.deviceOrientation = 'landscape'; - } else if (window.orientation === 0) { + } else if ( + screen.orientation.type === 'portrait-primary' || + screen.orientation.type === 'portrait-secondary' + ) { this.deviceOrientation = 'portrait'; - } else if (window.orientation === undefined) { + } else { this.deviceOrientation = 'undefined'; } - const context = this._isGlobal ? window : this; - if (typeof context.deviceMoved === 'function') { + + if (this.userDefinedFunctions.deviceMoved) { if ( Math.abs(this.accelerationX - this.pAccelerationX) > move_threshold || Math.abs(this.accelerationY - this.pAccelerationY) > move_threshold || Math.abs(this.accelerationZ - this.pAccelerationZ) > move_threshold ) { - context.deviceMoved(); + this.userDefinedFunctions.deviceMoved(); } } - if (typeof context.deviceTurned === 'function') { + if (this.userDefinedFunctions.deviceTurned) { // The angles given by rotationX etc is from range [-180 to 180]. // The following will convert them to [0 to 360] for ease of calculation // of cases when the angles wrapped around. @@ -671,7 +688,7 @@ function acceleration(p5, fn){ if (Math.abs(wRX - wSAX) > 90 && Math.abs(wRX - wSAX) < 270) { wSAX = wRX; this.turnAxis = 'X'; - context.deviceTurned(); + this.userDefinedFunctions.deviceTurned(); } this.pRotateDirectionX = rotateDirectionX; startAngleX = wSAX - 180; @@ -691,7 +708,7 @@ function acceleration(p5, fn){ if (Math.abs(wRY - wSAY) > 90 && Math.abs(wRY - wSAY) < 270) { wSAY = wRY; this.turnAxis = 'Y'; - context.deviceTurned(); + this.userDefinedFunctions.deviceTurned(); } this.pRotateDirectionY = rotateDirectionY; startAngleY = wSAY - 180; @@ -720,12 +737,12 @@ function acceleration(p5, fn){ ) { startAngleZ = rotZ; this.turnAxis = 'Z'; - context.deviceTurned(); + this.userDefinedFunctions.deviceTurned(); } this.pRotateDirectionZ = rotateDirectionZ; this.turnAxis = undefined; } - if (typeof context.deviceShaken === 'function') { + if (this.userDefinedFunctions.deviceShaken) { let accelerationChangeX; let accelerationChangeY; // Add accelerationChangeZ if acceleration change on Z is needed @@ -734,7 +751,7 @@ function acceleration(p5, fn){ accelerationChangeY = Math.abs(this.accelerationY - this.pAccelerationY); } if (accelerationChangeX + accelerationChangeY > shake_threshold) { - context.deviceShaken(); + this.userDefinedFunctions.deviceShaken(); } } }; diff --git a/src/events/keyboard.js b/src/events/keyboard.js index 1e9eaaebd7..d1b69de2dd 100644 --- a/src/events/keyboard.js +++ b/src/events/keyboard.js @@ -9,7 +9,7 @@ export function isCode(input) { 'Alt', 'Shift', 'Control', - 'Meta', + 'Meta' ]; if (leftRightKeys.includes(input)) { return false; @@ -19,7 +19,24 @@ export function isCode(input) { } return input.length > 1; } -function keyboard(p5, fn){ + +function keyboard(p5, fn, lifecycles){ + lifecycles.presetup = function(){ + const events = [ + 'keydown', + 'keyup', + 'keypress', + 'blur' + ]; + + for(const event of events){ + window.addEventListener(event, this[`_on${event}`].bind(this), { + passive: false, + signal: this._removeSignal + }); + } + }; + /** * A `Boolean` system variable that's `true` if any key is currently pressed * and `false` if not. @@ -109,7 +126,6 @@ function keyboard(p5, fn){ * * */ - fn.keyIsPressed = false; fn.code = null; @@ -470,9 +486,8 @@ function keyboard(p5, fn){ this._downKeyCodes[e.code] = true; this._downKeys[e.key] = true; - const context = this._isGlobal ? window : this; - if (typeof context.keyPressed === 'function' && !e.charCode) { - const executeDefault = context.keyPressed(e); + if (this.userDefinedFunctions.keyPressed && !e.charCode) { + const executeDefault = this.userDefinedFunctions.keyPressed(e); if (executeDefault === false) { e.preventDefault(); } @@ -636,15 +651,13 @@ function keyboard(p5, fn){ * */ fn._onkeyup = function(e) { - - const context = this._isGlobal ? window : this; - if (typeof context.keyReleased === 'function') { - const executeDefault = context.keyReleased(e); + if (this.userDefinedFunctions.keyReleased) { + const executeDefault = this.userDefinedFunctions.keyReleased(e); if (executeDefault === false) { e.preventDefault(); } } - + delete this._downKeyCodes[e.code]; delete this._downKeys[e.key]; @@ -801,14 +814,14 @@ function keyboard(p5, fn){ this._lastKeyCodeTyped = e.which; // track last keyCode this.key = e.key || String.fromCharCode(e.which) || e.which; - const context = this._isGlobal ? window : this; - if (typeof context.keyTyped === 'function') { - const executeDefault = context.keyTyped(e); + if (this.userDefinedFunctions.keyTyped) { + const executeDefault = this.userDefinedFunctions.keyTyped(e); if (executeDefault === false) { e.preventDefault(); } } }; + /** * The onblur function is called when the user is no longer focused * on the p5 element. Because the keyup events will not fire if the user is @@ -936,23 +949,23 @@ function keyboard(p5, fn){ * * */ - fn.keyIsDown = function(input) { if (isCode(input)) { return this._downKeyCodes[input] || this._downKeys[input] || false; } else { return this._downKeys[input] || this._downKeyCodes[input] || false; } - } + }; + /** * The _areDownKeys function returns a boolean true if any keys pressed * and a false if no keys are currently pressed. - + * * Helps avoid instances where multiple keys are pressed simultaneously and * releasing a single key will then switch the * keyIsPressed property to true. * @private - **/ + */ fn._areDownKeys = function() { for (const key in this._downKeys) { if (this._downKeys.hasOwnProperty(key) && this._downKeys[key] === true) { diff --git a/src/events/pointer.js b/src/events/pointer.js index ce942ff0ec..c7e15573fe 100644 --- a/src/events/pointer.js +++ b/src/events/pointer.js @@ -8,7 +8,26 @@ import * as constants from '../core/constants'; -function pointer(p5, fn){ +function pointer(p5, fn, lifecycles){ + lifecycles.presetup = function(){ + const events = [ + 'pointerdown', + 'pointerup', + 'pointermove', + 'dragend', + 'dragover', + 'click', + 'dblclick', + 'wheel' + ]; + for(const event of events){ + window.addEventListener(event, this[`_on${event}`].bind(this), { + passive: false, + signal: this._removeSignal + }); + } + }; + /** * A `Number` system variable that tracks the mouse's horizontal movement. * @@ -98,6 +117,7 @@ function pointer(p5, fn){ * */ fn.movedY = 0; + /* * This is a flag which is false until the first time * we receive a mouse event. The pmouseX and pmouseY @@ -765,7 +785,7 @@ function pointer(p5, fn){ center: false }; - /** + /** * An `Array` of all the current touch points on a touchscreen device. * * The `touches` array is empty by default. When the user touches their @@ -848,8 +868,8 @@ function pointer(p5, fn){ * * */ - fn.touches = []; - fn._activePointers = new Map(); + fn.touches = []; + fn._activePointers = new Map(); /** * A `Boolean` system variable that's `true` if the mouse is pressed and @@ -916,13 +936,13 @@ function pointer(p5, fn){ const sx = canvas.scrollWidth / this.width || 1; const sy = canvas.scrollHeight / this.height || 1; - if (e.pointerType == 'touch') { - const touches = []; - for (const touch of this._activePointers.values()) { - touches.push(getTouchInfo(canvas, sx, sy, touch)); - } - this.touches = touches; - } + if (e.pointerType === 'touch') { + const touches = []; + for (const touch of this._activePointers.values()) { + touches.push(getTouchInfo(canvas, sx, sy, touch)); + } + this.touches = touches; + } const mousePos = getMouseInfo(canvas, sx, sy, e); this.movedX = e.movementX || 0; @@ -932,12 +952,12 @@ function pointer(p5, fn){ this.winMouseX = mousePos.winX; this.winMouseY = mousePos.winY; - if (!this._hasMouseInteracted) { - this._updateMouseCoords(); - this._hasMouseInteracted = true; - } + if (!this._hasMouseInteracted) { + this._updateMouseCoords(); + this._hasMouseInteracted = true; + } } - }; + }; fn._updateMouseCoords = function() { this.pmouseX = this.mouseX; @@ -950,36 +970,36 @@ function pointer(p5, fn){ function getMouseInfo(canvas, sx, sy, evt) { const rect = canvas.getBoundingClientRect(); return { - x: (evt.clientX - rect.left) / sx, - y: (evt.clientY - rect.top) / sy, - winX: evt.clientX, - winY: evt.clientY, + x: (evt.clientX - rect.left) / sx, + y: (evt.clientY - rect.top) / sy, + winX: evt.clientX, + winY: evt.clientY }; - } + } - function getTouchInfo(canvas, sx, sy, touch) { - const rect = canvas.getBoundingClientRect(); - return { - x: (touch.clientX - rect.left) / sx, - y: (touch.clientY - rect.top) / sy, - winX: touch.clientX, - winY: touch.clientY, - id: touch.pointerId, - }; -} + function getTouchInfo(canvas, sx, sy, touch) { + const rect = canvas.getBoundingClientRect(); + return { + x: (touch.clientX - rect.left) / sx, + y: (touch.clientY - rect.top) / sy, + winX: touch.clientX, + winY: touch.clientY, + id: touch.pointerId + }; + } -fn._setMouseButton = function(e) { - // Check all active touches to determine button states - this.mouseButton.left = Array.from(this._activePointers.values()).some(touch => - (touch.buttons & 1) !== 0 - ); - this.mouseButton.center = Array.from(this._activePointers.values()).some(touch => - (touch.buttons & 4) !== 0 - ); - this.mouseButton.right = Array.from(this._activePointers.values()).some(touch => - (touch.buttons & 2) !== 0 - ); -}; + fn._setMouseButton = function(e) { + // Check all active touches to determine button states + this.mouseButton.left = Array.from(this._activePointers.values()).some(touch => + (touch.buttons & 1) !== 0 + ); + this.mouseButton.center = Array.from(this._activePointers.values()).some(touch => + (touch.buttons & 4) !== 0 + ); + this.mouseButton.right = Array.from(this._activePointers.values()).some(touch => + (touch.buttons & 2) !== 0 + ); + }; /** * A function that's called when the mouse moves. @@ -1155,24 +1175,22 @@ fn._setMouseButton = function(e) { * */ fn._onpointermove = function(e) { - const context = this._isGlobal ? window : this; let executeDefault; this._updatePointerCoords(e); this._activePointers.set(e.pointerId, e); this._setMouseButton(e); - - if (!this.mouseIsPressed && typeof context.mouseMoved === 'function') { - executeDefault = context.mouseMoved(e); - if (executeDefault === false) { - e.preventDefault(); - } - } else if (this.mouseIsPressed && typeof context.mouseDragged === 'function') { - executeDefault = context.mouseDragged(e); - if (executeDefault === false) { - e.preventDefault(); - } + if (!this.mouseIsPressed && this.userDefinedFunctions.mouseMoved) { + executeDefault = this.userDefinedFunctions.mouseMoved(e); + if (executeDefault === false) { + e.preventDefault(); } + } else if (this.mouseIsPressed && this.userDefinedFunctions.mouseDragged) { + executeDefault = this.userDefinedFunctions.mouseDragged(e); + if (executeDefault === false) { + e.preventDefault(); + } + } }; /** @@ -1318,7 +1336,6 @@ fn._setMouseButton = function(e) { * */ fn._onpointerdown = function(e) { - const context = this._isGlobal ? window : this; let executeDefault; this.mouseIsPressed = true; @@ -1326,8 +1343,8 @@ fn._setMouseButton = function(e) { this._setMouseButton(e); this._updatePointerCoords(e); - if (typeof context.mousePressed === 'function') { - executeDefault = context.mousePressed(e); + if (this.userDefinedFunctions.mousePressed) { + executeDefault = this.userDefinedFunctions.mousePressed(e); if (executeDefault === false) { e.preventDefault(); } @@ -1478,7 +1495,6 @@ fn._setMouseButton = function(e) { * */ fn._onpointerup = function(e) { - const context = this._isGlobal ? window : this; let executeDefault; this.mouseIsPressed = false; @@ -1487,8 +1503,8 @@ fn._setMouseButton = function(e) { this._updatePointerCoords(e); - if (typeof context.mouseReleased === 'function') { - executeDefault = context.mouseReleased(e); + if (this.userDefinedFunctions.mouseReleased) { + executeDefault = this.userDefinedFunctions.mouseReleased(e); if (executeDefault === false) { e.preventDefault(); } @@ -1642,9 +1658,8 @@ fn._setMouseButton = function(e) { * */ fn._onclick = function(e) { - const context = this._isGlobal ? window : this; - if (typeof context.mouseClicked === 'function') { - const executeDefault = context.mouseClicked(e); + if (this.userDefinedFunctions.mouseClicked) { + const executeDefault = this.userDefinedFunctions.mouseClicked(e); if (executeDefault === false) { e.preventDefault(); } @@ -1773,9 +1788,8 @@ fn._setMouseButton = function(e) { */ fn._ondblclick = function(e) { - const context = this._isGlobal ? window : this; - if (typeof context.doubleClicked === 'function') { - const executeDefault = context.doubleClicked(e); + if (this.userDefinedFunctions.doubleClicked) { + const executeDefault = this.userDefinedFunctions.doubleClicked(e); if (executeDefault === false) { e.preventDefault(); } @@ -1921,11 +1935,10 @@ fn._setMouseButton = function(e) { * */ fn._onwheel = function(e) { - const context = this._isGlobal ? window : this; this._mouseWheelDeltaY = e.deltaY; - if (typeof context.mouseWheel === 'function') { + if (this.userDefinedFunctions.mouseWheel) { e.delta = e.deltaY; - const executeDefault = context.mouseWheel(e); + const executeDefault = this.userDefinedFunctions.mouseWheel(e); if (executeDefault === false) { e.preventDefault(); }