mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-10 10:40:54 +00:00
commit
2de53fcf44
@ -49,6 +49,12 @@ export class EventManager
|
||||
// clock reset when mouse is moved:
|
||||
moveClock: new Clock(),
|
||||
};
|
||||
|
||||
// storing touches in both map and array for fast search and fast access if touchID is known
|
||||
this._touchInfo = {
|
||||
touchesArray: [],
|
||||
touchesMap: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,6 +146,19 @@ export class EventManager
|
||||
return this._mouseInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the data gathered about touches.
|
||||
*
|
||||
* @name module:core.EventManager#getTouchInfo
|
||||
* @function
|
||||
* @public
|
||||
* @return {object} the touch info.
|
||||
*/
|
||||
getTouchInfo ()
|
||||
{
|
||||
return this._touchInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all events from the event buffer.
|
||||
*
|
||||
@ -200,7 +219,6 @@ export class EventManager
|
||||
|
||||
self._mouseInfo.buttons.pressed[event.button] = 1;
|
||||
self._mouseInfo.buttons.times[event.button] = self._psychoJS._monotonicClock.getTime() - self._mouseInfo.buttons.clocks[event.button].getLastResetTime();
|
||||
|
||||
self._mouseInfo.pos = [event.offsetX, event.offsetY];
|
||||
|
||||
this._psychoJS.experimentLogger.data("Mouse: " + event.button + " button down, pos=(" + self._mouseInfo.pos[0] + "," + self._mouseInfo.pos[1] + ")");
|
||||
@ -212,10 +230,21 @@ export class EventManager
|
||||
|
||||
self._mouseInfo.buttons.pressed[0] = 1;
|
||||
self._mouseInfo.buttons.times[0] = self._psychoJS._monotonicClock.getTime() - self._mouseInfo.buttons.clocks[0].getLastResetTime();
|
||||
self._mouseInfo.pos = [event.changedTouches[0].pageX, event.changedTouches[0].pageY];
|
||||
|
||||
// we use the first touch, discarding all others:
|
||||
const touches = event.changedTouches;
|
||||
self._mouseInfo.pos = [touches[0].pageX, touches[0].pageY];
|
||||
this._touchInfo.touchesArray = new Array(event.touches.length);
|
||||
this._touchInfo.touchesMap = {};
|
||||
let i;
|
||||
for (i = 0; i < event.touches.length; i++)
|
||||
{
|
||||
this._touchInfo.touchesArray[i] = {
|
||||
id: event.touches[i].identifier,
|
||||
force: event.touches[i].force,
|
||||
pos: [event.touches[i].pageX, event.touches[i].pageY],
|
||||
busy: false
|
||||
};
|
||||
this._touchInfo.touchesMap[event.touches[i].identifier] = this._touchInfo.touchesArray[i];
|
||||
}
|
||||
|
||||
this._psychoJS.experimentLogger.data("Mouse: " + event.button + " button down, pos=(" + self._mouseInfo.pos[0] + "," + self._mouseInfo.pos[1] + ")");
|
||||
}, false);
|
||||
@ -249,10 +278,20 @@ export class EventManager
|
||||
|
||||
self._mouseInfo.buttons.pressed[0] = 0;
|
||||
self._mouseInfo.buttons.times[0] = self._psychoJS._monotonicClock.getTime() - self._mouseInfo.buttons.clocks[0].getLastResetTime();
|
||||
self._mouseInfo.pos = [event.changedTouches[0].pageX, event.changedTouches[0].pageY];
|
||||
|
||||
// we use the first touch, discarding all others:
|
||||
const touches = event.changedTouches;
|
||||
self._mouseInfo.pos = [touches[0].pageX, touches[0].pageY];
|
||||
this._touchInfo.touchesArray = new Array(event.touches.length);
|
||||
this._touchInfo.touchesMap = {};
|
||||
let i;
|
||||
for (i = 0; i < event.touches.length; i++)
|
||||
{
|
||||
this._touchInfo.touchesArray[i] = {
|
||||
id: event.touches[i].identifier,
|
||||
force: event.touches[i].force,
|
||||
pos: [event.touches[i].pageX, event.touches[i].pageY]
|
||||
};
|
||||
this._touchInfo.touchesMap[event.touches[i].identifier] = this._touchInfo.touchesArray[i];
|
||||
}
|
||||
|
||||
this._psychoJS.experimentLogger.data("Mouse: " + event.button + " button up, pos=(" + self._mouseInfo.pos[0] + "," + self._mouseInfo.pos[1] + ")");
|
||||
}, false);
|
||||
@ -270,10 +309,20 @@ export class EventManager
|
||||
event.preventDefault();
|
||||
|
||||
self._mouseInfo.moveClock.reset();
|
||||
self._mouseInfo.pos = [event.changedTouches[0].pageX, event.changedTouches[0].pageY];
|
||||
|
||||
// we use the first touch, discarding all others:
|
||||
const touches = event.changedTouches;
|
||||
self._mouseInfo.pos = [touches[0].pageX, touches[0].pageY];
|
||||
this._touchInfo.touchesArray = new Array(event.touches.length);
|
||||
this._touchInfo.touchesMap = {};
|
||||
let i;
|
||||
for (i = 0; i < event.touches.length; i++)
|
||||
{
|
||||
this._touchInfo.touchesArray[i] = {
|
||||
id: event.touches[i].identifier,
|
||||
force: event.touches[i].force,
|
||||
pos: [event.touches[i].pageX, event.touches[i].pageY]
|
||||
};
|
||||
this._touchInfo.touchesMap[event.touches[i].identifier] = this._touchInfo.touchesArray[i];
|
||||
}
|
||||
}, false);
|
||||
|
||||
// (*) wheel
|
||||
|
@ -152,7 +152,7 @@ export class Window extends PsychObject
|
||||
}
|
||||
|
||||
this._rootContainer.destroy();
|
||||
|
||||
|
||||
if (document.body.contains(this._renderer.view))
|
||||
{
|
||||
document.body.removeChild(this._renderer.view);
|
||||
@ -166,7 +166,6 @@ export class Window extends PsychObject
|
||||
}
|
||||
|
||||
this._renderer.destroy();
|
||||
|
||||
window.removeEventListener("resize", this._resizeCallback);
|
||||
window.removeEventListener("orientationchange", this._resizeCallback);
|
||||
|
||||
@ -316,7 +315,7 @@ export class Window extends PsychObject
|
||||
*/
|
||||
removePixiObject(pixiObject)
|
||||
{
|
||||
this._stimsContainer.removeChild(pixiObject);
|
||||
this._stimsContainer.removeChild(pixiObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -477,11 +476,11 @@ export class Window extends PsychObject
|
||||
// create a top-level PIXI container:
|
||||
this._rootContainer = new PIXI.Container();
|
||||
this._rootContainer.addChild(this._backgroundSprite, this._stimsContainer);
|
||||
|
||||
|
||||
// sorts children according to their zIndex value. Higher zIndex means it will be moved towards the end of the array,
|
||||
// and thus rendered on top of previous one.
|
||||
this._rootContainer.sortableChildren = true;
|
||||
|
||||
|
||||
this._rootContainer.interactive = true;
|
||||
this._rootContainer.filters = [this._adjustmentFilter];
|
||||
|
||||
@ -490,28 +489,7 @@ export class Window extends PsychObject
|
||||
|
||||
// touch/mouse events are treated by PsychoJS' event manager:
|
||||
this.psychoJS.eventManager.addMouseListeners(this._renderer);
|
||||
|
||||
// update the renderer size and the Window's stimuli whenever the browser's size or orientation change:
|
||||
this._resizeCallback = (e) =>
|
||||
{
|
||||
// if the user device is a mobile phone or tablet (we use the presence of a touch screen as a
|
||||
// proxy), we need to detect whether the change in size is due to the appearance of a virtual keyboard
|
||||
// in which case we do not want to resize the canvas. This is rather tricky and so we resort to
|
||||
// the below trick. It would be better to use the VirtualKeyboard API, but it is not widely
|
||||
// available just yet, as of 2023-06.
|
||||
const keyboardHeight = 300;
|
||||
if (hasTouchScreen() && (window.screen.height - window.visualViewport.height) > keyboardHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Window._resizePixiRenderer(this, e);
|
||||
this._backgroundSprite.width = this._size[0];
|
||||
this._backgroundSprite.height = this._size[1];
|
||||
this._fullRefresh();
|
||||
};
|
||||
window.addEventListener("resize", this._resizeCallback);
|
||||
window.addEventListener("orientationchange", this._resizeCallback);
|
||||
this._addEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -544,6 +522,80 @@ export class Window extends PsychObject
|
||||
pjsWindow._rootContainer.scale.y = -1;
|
||||
}
|
||||
|
||||
_handlePointerDown (e)
|
||||
{
|
||||
let i;
|
||||
let pickedPixi;
|
||||
let tmpPoint = new PIXI.Point();
|
||||
const cursorPos = new PIXI.Point(e.pageX, e.pageY);
|
||||
for (i = this._stimsContainer.children.length - 1; i >= 0; i--)
|
||||
{
|
||||
if (typeof this._stimsContainer.children[i].containsPoint === "function" &&
|
||||
this._stimsContainer.children[i].containsPoint(cursorPos))
|
||||
{
|
||||
pickedPixi = this._stimsContainer.children[i];
|
||||
break;
|
||||
}
|
||||
else if (this._stimsContainer.children[i].containsPoint === undefined &&
|
||||
this._stimsContainer.children[i] instanceof PIXI.DisplayObject)
|
||||
{
|
||||
this._stimsContainer.children[i].worldTransform.applyInverse(cursorPos, tmpPoint);
|
||||
if (this._stimsContainer.children[i].getLocalBounds().contains(tmpPoint.x, tmpPoint.y))
|
||||
{
|
||||
pickedPixi = this._stimsContainer.children[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.emit("pointerdown", {
|
||||
pixi: pickedPixi,
|
||||
originalEvent: e
|
||||
});
|
||||
}
|
||||
|
||||
_handlePointerUp (e)
|
||||
{
|
||||
this.emit("pointerup", {
|
||||
originalEvent: e
|
||||
});
|
||||
}
|
||||
|
||||
_handlePointerMove (e)
|
||||
{
|
||||
this.emit("pointermove", {
|
||||
originalEvent: e
|
||||
});
|
||||
}
|
||||
|
||||
_addEventListeners ()
|
||||
{
|
||||
this._renderer.view.addEventListener("pointerdown", this._handlePointerDown.bind(this));
|
||||
this._renderer.view.addEventListener("pointerup", this._handlePointerUp.bind(this));
|
||||
this._renderer.view.addEventListener("pointermove", this._handlePointerMove.bind(this));
|
||||
|
||||
// update the renderer size and the Window's stimuli whenever the browser's size or orientation change:
|
||||
this._resizeCallback = (e) =>
|
||||
{
|
||||
// if the user device is a mobile phone or tablet (we use the presence of a touch screen as a
|
||||
// proxy), we need to detect whether the change in size is due to the appearance of a virtual keyboard
|
||||
// in which case we do not want to resize the canvas. This is rather tricky and so we resort to
|
||||
// the below trick. It would be better to use the VirtualKeyboard API, but it is not widely
|
||||
// available just yet, as of 2023-06.
|
||||
const keyboardHeight = 300;
|
||||
if (hasTouchScreen() && (window.screen.height - window.visualViewport.height) > keyboardHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Window._resizePixiRenderer(this, e);
|
||||
this._backgroundSprite.width = this._size[0];
|
||||
this._backgroundSprite.height = this._size[1];
|
||||
this._fullRefresh();
|
||||
};
|
||||
window.addEventListener("resize", this._resizeCallback);
|
||||
window.addEventListener("orientationchange", this._resizeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all logged messages to the {@link Logger}.
|
||||
*
|
||||
|
@ -39,6 +39,7 @@ export class ButtonStim extends TextBox
|
||||
* @param {boolean} [options.italic= false] - whether or not the text is italic
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor(
|
||||
{
|
||||
@ -62,6 +63,7 @@ export class ButtonStim extends TextBox
|
||||
italic,
|
||||
autoDraw,
|
||||
autoLog,
|
||||
draggable,
|
||||
boxFn,
|
||||
multiline
|
||||
} = {},
|
||||
@ -90,6 +92,7 @@ export class ButtonStim extends TextBox
|
||||
alignment: "center",
|
||||
autoDraw,
|
||||
autoLog,
|
||||
draggable,
|
||||
boxFn
|
||||
});
|
||||
|
||||
|
@ -42,10 +42,11 @@ export class FaceDetector extends VisualStim
|
||||
* @param {number} [options.opacity= 1.0] - the opacity
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor({name, win, input, modelDir, faceApiUrl, units, ori, opacity, pos, size, autoDraw, autoLog} = {})
|
||||
constructor({name, win, input, modelDir, faceApiUrl, units, ori, opacity, pos, size, autoDraw, autoLog, draggable} = {})
|
||||
{
|
||||
super({name, win, units, ori, opacity, pos, size, autoDraw, autoLog});
|
||||
super({name, win, units, ori, opacity, pos, size, autoDraw, autoLog, draggable});
|
||||
|
||||
// TODO deal with onChange (see MovieStim and Camera)
|
||||
this._addAttribute("input", input, undefined);
|
||||
|
@ -54,6 +54,7 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every
|
||||
* frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor(
|
||||
{
|
||||
@ -82,10 +83,11 @@ export class Form extends util.mix(VisualStim).with(ColorMixin)
|
||||
clipMask,
|
||||
autoDraw,
|
||||
autoLog,
|
||||
draggable
|
||||
} = {},
|
||||
)
|
||||
{
|
||||
super({ name, win, units, opacity, depth, pos, size, clipMask, autoDraw, autoLog });
|
||||
super({ name, win, units, opacity, depth, pos, size, clipMask, autoDraw, autoLog, draggable });
|
||||
|
||||
this._addAttribute(
|
||||
"itemPadding",
|
||||
|
@ -426,6 +426,7 @@ export class GratingStim extends VisualStim
|
||||
* @param {String} [options.blendmode= "avg"] - blend mode of the stimulus, determines how the stimulus is blended with the background. Supported values: "avg", "add", "mul", "screen".
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor({
|
||||
name,
|
||||
@ -448,10 +449,11 @@ export class GratingStim extends VisualStim
|
||||
blendmode,
|
||||
autoDraw,
|
||||
autoLog,
|
||||
maskParams
|
||||
maskParams,
|
||||
draggable
|
||||
} = {})
|
||||
{
|
||||
super({ name, win, units, ori, opacity, depth, pos, anchor, size, autoDraw, autoLog });
|
||||
super({ name, win, units, ori, opacity, depth, pos, anchor, size, autoDraw, autoLog, draggable });
|
||||
|
||||
this._adjustmentFilter = new AdjustmentFilter({
|
||||
contrast
|
||||
|
@ -46,6 +46,7 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
|
||||
* @param {boolean} [options.flipVert= false] - whether or not to flip vertically
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
* @param {ImageStim.AspectRatioStrategy} [options.aspectRatio= ImageStim.AspectRatioStrategy.VARIABLE] - the aspect ratio handling strategy
|
||||
* @param {number} [options.blurVal= 0] - the blur value. Goes 0 to as hish as you like. 0 is no blur.
|
||||
*/
|
||||
@ -70,10 +71,11 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
|
||||
autoDraw,
|
||||
autoLog,
|
||||
aspectRatio,
|
||||
draggable,
|
||||
blurVal
|
||||
} = {})
|
||||
{
|
||||
super({ name, win, units, ori, opacity, depth, pos, anchor, size, autoDraw, autoLog });
|
||||
super({ name, win, units, ori, opacity, depth, pos, anchor, size, autoDraw, autoLog, draggable });
|
||||
|
||||
// Holds an instance of PIXI blur filter. Used if blur value is passed.
|
||||
this._blurFilter = undefined;
|
||||
|
@ -50,10 +50,11 @@ export class MovieStim extends VisualStim
|
||||
* @param {boolean} [options.autoPlay= true] - whether or not to autoplay the video
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor({ name, win, movie, pos, anchor, units, ori, size, color, opacity, contrast, interpolate, flipHoriz, flipVert, loop, volume, noAudio, autoPlay, autoDraw, autoLog } = {})
|
||||
constructor({ name, win, movie, pos, anchor, units, ori, size, color, opacity, contrast, interpolate, flipHoriz, flipVert, loop, volume, noAudio, autoPlay, autoDraw, autoLog, draggable } = {})
|
||||
{
|
||||
super({ name, win, units, ori, opacity, pos, anchor, size, autoDraw, autoLog });
|
||||
super({ name, win, units, ori, opacity, pos, anchor, size, autoDraw, autoLog, draggable });
|
||||
|
||||
this.psychoJS.logger.debug("create a new MovieStim with name: ", name);
|
||||
|
||||
|
@ -39,8 +39,9 @@ export class Polygon extends ShapeStim
|
||||
* @param {boolean} [options.interpolate= true] - whether or not the shape is interpolated
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor({ name, win, lineWidth, lineColor, fillColor, opacity, edges, radius, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog } = {})
|
||||
constructor({ name, win, lineWidth, lineColor, fillColor, opacity, edges, radius, pos, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog, draggable } = {})
|
||||
{
|
||||
super({
|
||||
name,
|
||||
@ -58,9 +59,11 @@ export class Polygon extends ShapeStim
|
||||
interpolate,
|
||||
autoDraw,
|
||||
autoLog,
|
||||
draggable
|
||||
});
|
||||
|
||||
this._psychoJS.logger.debug("create a new Polygon with name: ", name);
|
||||
this._psychoJS.logger.debug("create a new Polygon with name: ",
|
||||
name);
|
||||
|
||||
this._addAttribute(
|
||||
"edges",
|
||||
|
@ -38,8 +38,9 @@ export class Rect extends ShapeStim
|
||||
* @param {boolean} [options.interpolate= true] - whether or not the shape is interpolated
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor({ name, win, lineWidth, lineColor, fillColor, opacity, width, height, pos, anchor, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog } = {})
|
||||
constructor({ name, win, lineWidth, lineColor, fillColor, opacity, width, height, pos, anchor, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog, draggable } = {})
|
||||
{
|
||||
super({
|
||||
name,
|
||||
@ -58,6 +59,7 @@ export class Rect extends ShapeStim
|
||||
interpolate,
|
||||
autoDraw,
|
||||
autoLog,
|
||||
draggable
|
||||
});
|
||||
|
||||
this._psychoJS.logger.debug("create a new Rect with name: ", name);
|
||||
|
@ -44,10 +44,11 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
|
||||
* @param {boolean} [options.interpolate= true] - whether or not the shape is interpolated
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor({ name, win, lineWidth, lineColor, fillColor, opacity, vertices, closeShape, pos, anchor, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog } = {})
|
||||
constructor({ name, win, lineWidth, lineColor, fillColor, opacity, vertices, closeShape, pos, anchor, size, ori, units, contrast, depth, interpolate, autoDraw, autoLog, draggable } = {})
|
||||
{
|
||||
super({ name, win, units, ori, opacity, pos, anchor, depth, size, autoDraw, autoLog });
|
||||
super({ name, win, units, ori, opacity, pos, anchor, depth, size, autoDraw, autoLog, draggable });
|
||||
|
||||
// the PIXI polygon corresponding to the vertices, in pixel units:
|
||||
this._pixiPolygon_px = undefined;
|
||||
@ -163,8 +164,8 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
|
||||
if (typeof objectPos_px === "undefined")
|
||||
{
|
||||
throw {
|
||||
origin: "VisualStim.contains",
|
||||
context: "when determining whether VisualStim: " + this._name + " contains object: " + util.toString(object),
|
||||
origin: "ShapeStim.contains",
|
||||
context: "when determining whether ShapeStim: " + this._name + " contains object: " + util.toString(object),
|
||||
error: "unable to determine the position of the object",
|
||||
};
|
||||
}
|
||||
@ -176,6 +177,22 @@ export class ShapeStim extends util.mix(VisualStim).with(ColorMixin, WindowMixin
|
||||
return util.IsPointInsidePolygon(objectPos_px, polygon_px);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a point that is nown to have pixel dimensions is inside the bounding box of the stimulus.
|
||||
*
|
||||
* @name module:visual.ShapeStim#containsPointPx
|
||||
* @public
|
||||
* @param {number[]} point_px - the point in pixels
|
||||
* @return {boolean} whether or not the object is inside the bounding box of the stimulus
|
||||
*/
|
||||
containsPointPx (point_px)
|
||||
{
|
||||
const pos_px = util.to_px(this.pos, this.units, this.win);
|
||||
this._getVertices_px();
|
||||
const polygon_px = this._vertices_px.map((v) => [v[0] + pos_px[0], v[1] + pos_px[1]]);
|
||||
return util.IsPointInsidePolygon(point_px, polygon_px);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the anchor attribute.
|
||||
*
|
||||
|
@ -65,6 +65,7 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every
|
||||
* frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*
|
||||
* @param {core.MinimalStim[]} [options.dependentStims = [] ] - the list of dependent stimuli,
|
||||
* which must be updated when this Slider is updated, e.g. a Form.
|
||||
@ -99,10 +100,11 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin, WindowMixin)
|
||||
autoDraw,
|
||||
autoLog,
|
||||
dependentStims,
|
||||
draggable
|
||||
} = {},
|
||||
)
|
||||
{
|
||||
super({ name, win, units, ori, opacity, depth, pos, size, clipMask, autoDraw, autoLog });
|
||||
super({ name, win, units, ori, opacity, depth, pos, size, clipMask, autoDraw, autoLog, draggable });
|
||||
|
||||
this._needMarkerUpdate = false;
|
||||
|
||||
|
@ -52,6 +52,7 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.fitToContent = false] - whether or not to resize itself automaitcally to fit to the text content
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor(
|
||||
{
|
||||
@ -87,11 +88,12 @@ export class TextBox extends util.mix(VisualStim).with(ColorMixin)
|
||||
autoDraw,
|
||||
autoLog,
|
||||
fitToContent,
|
||||
draggable,
|
||||
boxFn
|
||||
} = {},
|
||||
)
|
||||
{
|
||||
super({ name, win, pos, anchor, size, units, ori, opacity, depth, clipMask, autoDraw, autoLog });
|
||||
super({ name, win, pos, anchor, size, units, ori, opacity, depth, clipMask, autoDraw, autoLog, draggable });
|
||||
|
||||
this._addAttribute(
|
||||
"text",
|
||||
|
@ -49,6 +49,7 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
|
||||
* @param {PIXI.Graphics} [options.clipMask= null] - the clip mask
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor(
|
||||
{
|
||||
@ -75,10 +76,11 @@ export class TextStim extends util.mix(VisualStim).with(ColorMixin)
|
||||
clipMask,
|
||||
autoDraw,
|
||||
autoLog,
|
||||
draggable
|
||||
} = {},
|
||||
)
|
||||
{
|
||||
super({ name, win, units, ori, opacity, depth, pos, anchor, clipMask, autoDraw, autoLog });
|
||||
super({ name, win, units, ori, opacity, depth, pos, anchor, clipMask, autoDraw, autoLog, draggable });
|
||||
|
||||
// callback to deal with text metrics invalidation:
|
||||
const onChange = (withPixi = false, withBoundingBox = false, withMetrics = false) =>
|
||||
|
@ -35,8 +35,9 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
|
||||
* @param {PIXI.Graphics} [options.clipMask= null] - the clip mask
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
* @param {boolean} [options.draggable= false] - whether or not to make stim draggable with mouse/touch/other pointer device
|
||||
*/
|
||||
constructor({ name, win, units, ori, opacity, depth, pos, anchor, size, clipMask, autoDraw, autoLog } = {})
|
||||
constructor({ name, win, units, ori, opacity, depth, pos, anchor, size, clipMask, autoDraw, autoLog, draggable } = {})
|
||||
{
|
||||
super({ win, name, autoDraw, autoLog });
|
||||
|
||||
@ -84,6 +85,12 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
|
||||
null,
|
||||
this._onChange(false, false),
|
||||
);
|
||||
this._addAttribute("draggable", draggable, false);
|
||||
|
||||
// data needed to properly support drag and drop functionality
|
||||
this._associatedPointerId = undefined;
|
||||
this._initialPointerOffset = [0, 0];
|
||||
this._pointerEventHandlersUuids = {};
|
||||
|
||||
// bounding box of the stimulus, in stimulus units
|
||||
// note: boundingBox does not take the orientation into account
|
||||
@ -96,6 +103,14 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
|
||||
this._needPixiUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not stimuli is being dragged by pointer. Works in conjunction with draggable attribute.
|
||||
*/
|
||||
get isDragging()
|
||||
{
|
||||
return this._associatedPointerId !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a refresh of the stimulus.
|
||||
*
|
||||
@ -179,15 +194,45 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the draggable attribute.
|
||||
*
|
||||
* @name module:visual.VisualStim#setDraggable
|
||||
* @public
|
||||
* @param {boolean} [draggable=false] - whether or not to make stim draggable using mouse/touch/other pointer device
|
||||
* @param {boolean} [log= false] - whether of not to log
|
||||
*/
|
||||
setDraggable(draggable = false, log = false)
|
||||
{
|
||||
const hasChanged = this._setAttribute("draggable", draggable, log);
|
||||
if (hasChanged)
|
||||
{
|
||||
if (draggable)
|
||||
{
|
||||
this._pointerEventHandlersUuids[ "pointerdown" ] = this._win.on("pointerdown", this._handlePointerDown.bind(this));
|
||||
this._pointerEventHandlersUuids[ "pointerup" ] = this._win.on("pointerup", this._handlePointerUp.bind(this));
|
||||
this._pointerEventHandlersUuids[ "pointermove" ] = this._win.on("pointermove", this._handlePointerMove.bind(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
this._win.off("pointerdown", this._pointerEventHandlersUuids[ "pointerdown" ]);
|
||||
this._win.off("pointerup", this._pointerEventHandlersUuids[ "pointerup" ]);
|
||||
this._win.off("pointermove", this._pointerEventHandlersUuids[ "pointermove" ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the depth attribute.
|
||||
*
|
||||
* @param {Array.<number>} depth - order in which stimuli is rendered, kind of css's z-index with a negative sign.
|
||||
* @param {boolean} [log= false] - whether of not to log
|
||||
*/
|
||||
setDepth (depth = 0, log = false) {
|
||||
setDepth(depth = 0, log = false)
|
||||
{
|
||||
this._setAttribute("depth", depth, log);
|
||||
if (this._pixi) {
|
||||
if (this._pixi)
|
||||
{
|
||||
this._pixi.zIndex = -this._depth;
|
||||
}
|
||||
}
|
||||
@ -217,6 +262,93 @@ export class VisualStim extends util.mix(MinimalStim).with(WindowMixin)
|
||||
return this._getBoundingBox_px().contains(objectPos_px[0], objectPos_px[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a point that is nown to have pixel dimensions is inside the bounding box of the stimulus.
|
||||
*
|
||||
* @name module:visual.VisualStim#containsPointPx
|
||||
* @public
|
||||
* @param {number[]} point_px - the point in pixels
|
||||
* @return {boolean} whether or not the object is inside the bounding box of the stimulus
|
||||
*/
|
||||
containsPointPx (point_px)
|
||||
{
|
||||
return this._getBoundingBox_px().contains(point_px[0], point_px[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the PIXI representation, if there is one.
|
||||
*
|
||||
* @name module:core.VisualStim#release
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {boolean} [log= false] - whether or not to log
|
||||
*/
|
||||
release(log = false)
|
||||
{
|
||||
this.draggable = false;
|
||||
super.release(log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler of pointerdown event.
|
||||
*
|
||||
* @name module:visual.VisualStim#_handlePointerDown
|
||||
* @private
|
||||
* @param {Object} e - pointerdown event data.
|
||||
*/
|
||||
_handlePointerDown (e)
|
||||
{
|
||||
if (e.pixi === undefined || e.pixi !== this._pixi)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let relativePos = [];
|
||||
let pixPos = util.to_unit(this._pos, this._units, this._win, "pix");
|
||||
relativePos[0] = e.originalEvent.pageX - this._win.size[0] * 0.5 - this._pixi.parent.position.x;
|
||||
relativePos[1] = -(e.originalEvent.pageY - this._win.size[1] * 0.5) - this._pixi.parent.position.y;
|
||||
this._associatedPointerId = e.originalEvent.pointerId;
|
||||
this._initialPointerOffset[0] = relativePos[0] - pixPos[0];
|
||||
this._initialPointerOffset[1] = relativePos[1] - pixPos[1];
|
||||
this.emit("pointerdown", e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler of pointerup event.
|
||||
*
|
||||
* @name module:visual.VisualStim#_handlePointerUp
|
||||
* @private
|
||||
* @param {Object} e - pointerup event data.
|
||||
*/
|
||||
_handlePointerUp (e)
|
||||
{
|
||||
if (e.originalEvent.pointerId === this._associatedPointerId)
|
||||
{
|
||||
this._associatedPointerId = undefined;
|
||||
this._initialPointerOffset.fill(0);
|
||||
this.emit("pointerup", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler of pointermove event.
|
||||
*
|
||||
* @name module:visual.VisualStim#_handlePointerMove
|
||||
* @private
|
||||
* @param {Object} e - pointermove event data.
|
||||
*/
|
||||
_handlePointerMove (e)
|
||||
{
|
||||
if (e.originalEvent.pointerId === this._associatedPointerId)
|
||||
{
|
||||
let newPos = [];
|
||||
newPos[ 0 ] = e.originalEvent.pageX - this._win.size[ 0 ] * 0.5 - this._pixi.parent.position.x - this._initialPointerOffset[ 0 ];
|
||||
newPos[ 1 ] = -(e.originalEvent.pageY - this._win.size[ 1 ] * 0.5) - this._pixi.parent.position.y - this._initialPointerOffset[ 1 ];
|
||||
this.setPos(util.to_unit(newPos, "pix", this._win, this._units));
|
||||
this.emit("pointermove", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the anchor attribute.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user