create targets parameter for webgazer ext

This commit is contained in:
Josh de Leeuw 2021-02-05 14:16:03 -05:00
parent 18014cbe5f
commit af95ebe7c1
4 changed files with 72 additions and 11 deletions

View File

@ -4,7 +4,9 @@ This extension supports eye tracking through the [WebGazer](https://webgazer.cs.
## Parameters ## Parameters
Parameters can be set when calling `jsPsych.init()` ### Initialization Parameters
Initialization parameters can be set when calling `jsPsych.init()`
```js ```js
jsPsych.init({ jsPsych.init({
@ -19,11 +21,29 @@ Parameter | Type | Default Value | Description
webgazer | object | `undefined` | You can explicitly pass a reference to a loaded instance of the webgazer.js library. If no explicit reference is passed then the extension will look for a global `webgazer` object. If you are loading webgazer.js via a `<script>` tag you do not need to set this parameter in most circumstances. webgazer | object | `undefined` | You can explicitly pass a reference to a loaded instance of the webgazer.js library. If no explicit reference is passed then the extension will look for a global `webgazer` object. If you are loading webgazer.js via a `<script>` tag you do not need to set this parameter in most circumstances.
round_predictions | bool | true | Whether to round the `x`,`y` coordinates predicted by WebGazer to the nearest whole number. This *greatly* reduces the size of the data, as WebGazer records data to 15 decimal places by default. Given the noise of the system, there's really no need to record data to this level of precision. round_predictions | bool | true | Whether to round the `x`,`y` coordinates predicted by WebGazer to the nearest whole number. This *greatly* reduces the size of the data, as WebGazer records data to 15 decimal places by default. Given the noise of the system, there's really no need to record data to this level of precision.
### Trial Parameters
Trial parameters can be set when adding the extension to a trial object.
```js
var trial = {
type: '...',
extensions: [
{type: 'webgazer', params: {...}}
]
}
```
Parameter | Type | Default Value | Description
----------|------|---------------|------------
targets | array | [] | A list of elements on the page that you would like to record the coordinates of for comparison with the webgazer data. Each entry in the array should be a valid [CSS selector string](https://www.w3schools.com/cssref/css_selectors.asp) that identifies the element. The selector string should be valid for exactly one element on the page. If the selector is valid for more than one element then only the first matching element will be recorded.
## Data Generated ## Data Generated
Name | Type | Value Name | Type | Value
-----|------|------ -----|------|------
webgazer_data | array | An array of objects containing gaze data for the trial. Each object has an `x`, a `y`, and a `t` property. The `x` and `y` properties specify the gaze location in pixels and `t` specifies the time in milliseconds since the start of the trial. webgazer_data | array | An array of objects containing gaze data for the trial. Each object has an `x`, a `y`, and a `t` property. The `x` and `y` properties specify the gaze location in pixels and `t` specifies the time in milliseconds since the start of the trial.
webgazer_targets | array | An array of objects contain the pixel coordinates of elements on the screen specified by the `.targets` parameter. Each object contains a `selector` property, containing the CSS selector string used to find the element, plus `top`, `bottom`, `left`, and `right` parameters which specify the [bounding rectangle](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) of the element.
## Functions ## Functions

View File

@ -75,13 +75,31 @@ To enable eye tracking for a trial in your experiment, you can simply add the We
```js ```js
var trial = { var trial = {
type: 'image-keyboard-response', type: 'html-keyboard-response',
stimulus: 'my-scene.png', stimulus: '<img id="scene" src="my-scene.png"></img>',
extensions: ['webgazer'] extensions: [
{
type: 'webgazer',
params: {
targets: ['#scene']
}
}
]
} }
``` ```
This will turn on WebGazer at the start of the trial. Gaze data will be added to the trial's data under the property `webgazer_data`. The gaze data is an array of objects. Each object has an `x`, a `y`, and a `t` property. The `x` and `y` properties specify the gaze location in pixels and `t` specifies the time in milliseconds since the start of the trial. Note that establishing the precision and accuracy of these measurements across the variety of web browsers and systems that your experiment participants might be using is quite difficult. For example, different browsers may cause small systematic shifts in the accuracy of `t` values. This will turn on WebGazer at the start of the trial.
The `params` property in the `extensions` declaration allows you to pass in a list of [CSS selector strings](https://www.w3schools.com/cssref/css_selectors.asp). The [bounding rectangle](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) of the DOM element that matches each selector will be recorded in the data for that trial. This allows for easy alignment of the gaze data and objects on the screen.
```js
webgazer_targets : [
{selector: ..., top: ..., left: ..., right: ..., bottom:...},
{selector: ..., top: ..., left: ..., right: ..., bottom:...},
]
```
Gaze data will be added to the trial's data under the property `webgazer_data`. The gaze data is an array of objects. Each object has an `x`, a `y`, and a `t` property. The `x` and `y` properties specify the gaze location in pixels and `t` specifies the time in milliseconds since the start of the trial. Note that establishing the precision and accuracy of these measurements across the variety of web browsers and systems that your experiment participants might be using is quite difficult. For example, different browsers may cause small systematic shifts in the accuracy of `t` values.
```js ```js
webgazer_data: [ webgazer_data: [

View File

@ -93,7 +93,7 @@
return( return(
`<div style="position: relative; width: 400px; height: 400px;"> `<div style="position: relative; width: 400px; height: 400px;">
<div style="position: absolute; top:${jsPsych.timelineVariable('top', true)}%; left: ${jsPsych.timelineVariable('left', true)}%"> <div style="position: absolute; top:${jsPsych.timelineVariable('top', true)}%; left: ${jsPsych.timelineVariable('left', true)}%">
<span style="font-size: 40px; transform: translate(-50%, -50%);">${jsPsych.timelineVariable('direction', true) == 'left' ? '⬅' : '➡'}</span> <span id="arrow-target" style="font-size: 40px; transform: translate(-50%, -50%);">${jsPsych.timelineVariable('direction', true) == 'left' ? '⬅' : '➡'}</span>
</div> </div>
</div>` </div>`
) )
@ -104,7 +104,9 @@
top: jsPsych.timelineVariable('top'), top: jsPsych.timelineVariable('top'),
left: jsPsych.timelineVariable('left') left: jsPsych.timelineVariable('left')
}, },
extensions: ['webgazer'] extensions: [
{type: 'webgazer', params: {targets: ['#arrow-target']}}
]
} }
var params = [ var params = [

View File

@ -48,12 +48,13 @@ jsPsych.extensions['webgazer'] = (function () {
} }
// required, will be called when the trial starts (before trial loads) // required, will be called when the trial starts (before trial loads)
extension.on_start = function () { extension.on_start = function (params) {
state.currentTrialData = []; state.currentTrialData = [];
state.currentTrialTargets = [];
} }
// required will be called when the trial loads // required will be called when the trial loads
extension.on_load = function () { extension.on_load = function (params) {
// set current trial start time // set current trial start time
state.currentTrialStart = performance.now(); state.currentTrialStart = performance.now();
@ -63,11 +64,30 @@ jsPsych.extensions['webgazer'] = (function () {
// set internal flag // set internal flag
state.activeTrial = true; state.activeTrial = true;
// record bounding box of any elements in params.targets
if(typeof params !== 'undefined'){
if(typeof params.targets !== 'undefined'){
for(var i=0; i<params.targets.length; i++){
var target = document.querySelector(params.targets[i]);
if(target !== null){
var bounding_rect = target.getBoundingClientRect();
state.currentTrialTargets.push({
selector: params.targets[i],
top: bounding_rect.top,
bottom: bounding_rect.bottom,
left: bounding_rect.left,
right: bounding_rect.right
})
}
}
}
}
} }
// required, will be called when jsPsych.finishTrial() is called // required, will be called when jsPsych.finishTrial() is called
// must return data object to be merged into data. // must return data object to be merged into data.
extension.on_finish = function () { extension.on_finish = function (params) {
// pause the eye tracker // pause the eye tracker
state.webgazer.pause(); state.webgazer.pause();
@ -76,7 +96,8 @@ jsPsych.extensions['webgazer'] = (function () {
// send back the gazeData // send back the gazeData
return { return {
webgazer_data: state.currentTrialData webgazer_data: state.currentTrialData,
webgazer_targets: state.currentTrialTargets
} }
} }