mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-11 16:18:11 +00:00
Merge pull request #1081 from GEJ1/instructions
Instructions plugin changes: add page_label parameter to plugin and docs, add show_page_number parameter to docs
This commit is contained in:
commit
36df451b08
@ -2,10 +2,6 @@
|
||||
|
||||
This plugin is for showing instructions to the subject. It allows subjects to navigate through multiple pages of instructions at their own pace, recording how long the subject spends on each page. Navigation can be done using the mouse or keyboard. Subjects can be allowed to navigate forwards and backwards through pages, if desired.
|
||||
|
||||
## Parameters
|
||||
|
||||
Parameters with a default value of *undefined* must be specified. Other parameters can be left unspecified if the default value is acceptable.
|
||||
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
pages | array | *undefined* | Each element of the array is the content for a single page. Each page should be an HTML-formatted string.
|
||||
@ -16,7 +12,8 @@ allow_keys | boolean | true | If true, the subject can use keyboard keys to navi
|
||||
show_clickable_nav | boolean | false | If true, then a `Previous` and `Next` button will be displayed beneath the instructions. Subjects can click the buttons to navigate.
|
||||
button_label_previous | string | 'Previous' | The text that appears on the button to go backwards.
|
||||
button_label_next | string | 'Next' | The text that appears on the button to go forwards.
|
||||
|
||||
show_page_number | boolean | false | If true, and clickable navigation is enabled, then Page x/y will be shown between the nav buttons.
|
||||
page_label | string | 'Page' | The text that appears before x/y pages displayed with show_page_number.
|
||||
|
||||
## Data Generated
|
||||
|
||||
|
43
docs/plugins/jspsych-virtual-chin-rest.md
Normal file
43
docs/plugins/jspsych-virtual-chin-rest.md
Normal file
@ -0,0 +1,43 @@
|
||||
# jspsych-virtual-chin-rest plugin
|
||||
|
||||
This plugin consist in two parts:
|
||||
|
||||
To first calculate a participant’s display, participants are asked to place a credit card-sized card on the screen and adjust the slider on the screen to fit the credit card. That allows the researchers to calculate the pixel density on the monitor.
|
||||
|
||||
To measure the user’s distance from his or her monitor, there is also a blind spot task. Testers are asked to focus on a black square on the screen with their right eye closed, while a red dot repeatedly sweeps from right to left. They must hit the spacebar on their keyboards whenever it appears that the red dot has disappeared. That allows researchers to determine the distance between the center of the black square and the center of the red dot when it disappears from eyesight and understand how far the participant is from the monitor.
|
||||
|
||||
|
||||
We would appreciate it if you cited this paper when you use the virtual-chin-rest plugin:
|
||||
|
||||
**Li, Q., Joo, S. J., Yeatman, J. D., & Reinecke, K. (2020). Controlling for Participants’ Viewing Distance in Large-Scale, Psychophysical Online Experiments Using a Virtual Chinrest. Scientific Reports, 10(1), 1-11. DOI: [10.1038/s41598-019-57204-1]**
|
||||
|
||||
|
||||
|
||||
## Parameters
|
||||
|
||||
Parameters can be left unspecified if the default value is acceptable.
|
||||
|
||||
|Parameter|Type|Default Value| Descripton|
|
||||
|---------|----|-------------|-----------|
|
||||
|
||||
|
||||
## Data Generated
|
||||
|
||||
In addition to the default data collected by all plugins, this plugin collects all parameter data described above and the following data for each trial.
|
||||
|
||||
|
||||
|Name|Type|Value|
|
||||
|----|----|-----|
|
||||
viewing_distance_cm|numeric|
|
||||
cardWidth_px| numeric
|
||||
screen_size_px| char
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
```javascript
|
||||
var chin = {
|
||||
|
||||
type: 'virtual-chin'
|
||||
|
||||
}
|
@ -63,6 +63,12 @@ jsPsych.plugins.instructions = (function() {
|
||||
default: false,
|
||||
description: 'If true, and clickable navigation is enabled, then Page x/y will be shown between the nav buttons.'
|
||||
},
|
||||
page_label: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Page label',
|
||||
default: 'Page',
|
||||
description: 'The text that appears before x/y (current/total) pages displayed with show_page_number'
|
||||
},
|
||||
button_label_previous: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Button label previous',
|
||||
@ -104,7 +110,7 @@ jsPsych.plugins.instructions = (function() {
|
||||
var pagenum_display = "";
|
||||
if(trial.show_page_number) {
|
||||
pagenum_display = "<span style='margin: 0 1em;' class='"+
|
||||
"jspsych-instructions-pagenum'>Page "+(current_page+1)+"/"+trial.pages.length+"</span>";
|
||||
"jspsych-instructions-pagenum'>"+ trial.page_label + ' ' +(current_page+1)+"/"+trial.pages.length+"</span>";
|
||||
}
|
||||
|
||||
if (trial.show_clickable_nav) {
|
||||
|
296
plugins/jspsych-virtual-chin-rest.js
Normal file
296
plugins/jspsych-virtual-chin-rest.js
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* plugin for jsPsych based in Qisheng Li 11/2019. /// https://github.com/QishengLi/virtual_chinrest
|
||||
|
||||
Modified by Gustavo Juantorena 08/2020 // https://github.com/GEJ1
|
||||
|
||||
Work in progress in jsPsych discussion: https://github.com/jspsych/jsPsych/discussions/975
|
||||
*/
|
||||
|
||||
jsPsych.plugins['virtual-chin'] = (function() {
|
||||
|
||||
var plugin = {};
|
||||
|
||||
plugin.info = {
|
||||
name: "virtual-chin",
|
||||
parameters: {
|
||||
viewing_distance_cm: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
default: 0
|
||||
},
|
||||
cardWidth_px: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
default: 0
|
||||
},
|
||||
pixels_per_unit: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Pixels per unit',
|
||||
default: 100,
|
||||
description: 'After the scaling factor is applied, this many pixels will equal one unit of measurement.'
|
||||
}
|
||||
// prompt_card: {
|
||||
// type: jsPsych.plugins.parameterType.STRING,
|
||||
// default: null,
|
||||
// description: 'Any content here will be displayed above card image.'
|
||||
// },
|
||||
// prompt_blindspot: {
|
||||
// type: jsPsych.plugins.parameterType.STRING,
|
||||
// default: null,
|
||||
// description: 'Any content here will be displayed below the stimulus.'
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
plugin.trial = function(display_element, trial) {
|
||||
|
||||
// Get screen size
|
||||
var w = window.innerWidth;
|
||||
var h = window.innerHeight;
|
||||
|
||||
const screen_size_px = []
|
||||
screen_size_px.push(w)
|
||||
screen_size_px.push('x')
|
||||
screen_size_px.push(h)
|
||||
|
||||
|
||||
// data saving
|
||||
var trial_data = { //I need to modify this in order to save important data
|
||||
'viewing_distance_cm': trial.viewing_distance_cm,
|
||||
'cardWidth_px': trial.cardWidth_px,
|
||||
'screen_size_px': trial.screen_size_px
|
||||
};
|
||||
|
||||
trial_data.screen_size_px = screen_size_px
|
||||
|
||||
|
||||
//Store all the configuration data in variable 'data'
|
||||
var data = {"dataType":"configurationData"};
|
||||
|
||||
data["ballPosition"] = [];
|
||||
|
||||
data["sliderClicked"] = false;
|
||||
|
||||
(function ( distanceSetup, $ ) { // jQuery short-hand for $(document).ready(function() { ... });
|
||||
|
||||
distanceSetup.round = function(value, decimals) {
|
||||
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
|
||||
};
|
||||
|
||||
distanceSetup.px2mm = function(cardImageWidth) {
|
||||
const cardWidth = 85.6; //card dimension: 85.60 × 53.98 mm (3.370 × 2.125 in)
|
||||
var px2mm = cardImageWidth/cardWidth;
|
||||
data["px2mm"] = distanceSetup.round(px2mm, 2);
|
||||
return px2mm;
|
||||
};
|
||||
|
||||
}( window.distanceSetup = window.distanceSetup || {}, jQuery));
|
||||
|
||||
|
||||
function getCardWidth() {
|
||||
var cardWidthPx = $('#card').width();
|
||||
data["cardWidthPx"] = distanceSetup.round(cardWidthPx,2);
|
||||
|
||||
trial_data.cardWidth_px = cardWidthPx // add to trial_data
|
||||
|
||||
return cardWidthPx
|
||||
}
|
||||
|
||||
|
||||
function configureBlindSpot() {
|
||||
|
||||
drawBall();
|
||||
$('#page-size').remove();
|
||||
$('#blind-spot').css({'visibility':'visible'});
|
||||
// $(document).on('keydown', recordPosition);
|
||||
$(document).on('keydown', recordPosition);
|
||||
|
||||
};
|
||||
|
||||
$( function() {
|
||||
$( "#slider" ).slider({value:"50"});
|
||||
} );
|
||||
|
||||
$(document).ready(function() {
|
||||
$( "#slider" ).on("slide", function (event, ui) {
|
||||
var cardWidth = ui.value + "%";
|
||||
$("#card").css({"width":cardWidth});
|
||||
});
|
||||
|
||||
$('#slider').on('slidechange', function(event, ui){
|
||||
data["sliderClicked"] = true;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
//=============================
|
||||
//Ball Animation
|
||||
|
||||
function drawBall(pos=180){
|
||||
// pos: define where the fixation square should be.
|
||||
var mySVG = SVG("svgDiv");
|
||||
const cardWidthPx = getCardWidth()
|
||||
const rectX = distanceSetup.px2mm(cardWidthPx)*pos;
|
||||
|
||||
const ballX = rectX*0.6 // define where the ball is
|
||||
var ball = mySVG.circle(30).move(ballX, 50).fill("#f00");
|
||||
window.ball = ball;
|
||||
var square = mySVG.rect(30, 30).move(Math.min(rectX - 50, 950), 50); //square position
|
||||
data["squarePosition"] = distanceSetup.round(square.cx(),2);
|
||||
data['rectX'] = rectX
|
||||
data['ballX'] = ballX
|
||||
};
|
||||
|
||||
|
||||
function animateBall(){
|
||||
ball.animate(7000).during(
|
||||
function(pos){
|
||||
moveX = - pos*data['ballX'];
|
||||
window.moveX = moveX;
|
||||
moveY = 0;
|
||||
ball.attr({transform:"translate("+moveX+","+moveY+")"});
|
||||
|
||||
}
|
||||
).loop(true, false).
|
||||
after(function(){
|
||||
animateBall();
|
||||
});
|
||||
|
||||
//disbale the button after clicked once.
|
||||
$("#start").attr("disabled", true);
|
||||
};
|
||||
|
||||
function recordPosition(event, angle=13.5) {
|
||||
// angle: define horizontal blind spot entry point position in degrees.
|
||||
if (event.keyCode == '32') { //Press "Space"
|
||||
|
||||
|
||||
data["ballPosition"].push(distanceSetup.round((ball.cx() + moveX),2));
|
||||
var sum = data["ballPosition"].reduce((a, b) => a + b, 0);
|
||||
var ballPosLen = data["ballPosition"].length;
|
||||
data["avgBallPos"] = distanceSetup.round(sum/ballPosLen, 2);
|
||||
var ball_sqr_distance = (data["squarePosition"]-data["avgBallPos"])/data["px2mm"];
|
||||
var viewDistance = ball_sqr_distance/Math.radians(angle)
|
||||
data["viewDistance_mm"] = distanceSetup.round(viewDistance, 2);
|
||||
|
||||
//counter and stop
|
||||
var counter = Number($('#click').text());
|
||||
counter = counter - 1;
|
||||
$('#click').text(Math.max(counter, 0));
|
||||
if (counter <= 0) {
|
||||
ball.stop();
|
||||
|
||||
// Disable space key
|
||||
$('html').bind('keydown', function(e)
|
||||
{
|
||||
if (e.keyCode == 32) {return false;} //32 is spacebar I CHANGE THAT
|
||||
});
|
||||
|
||||
// Display data
|
||||
$('#info').css("visibility", "visible");
|
||||
$('#info-h').append(data["viewDistance_mm"]/10)
|
||||
|
||||
|
||||
//Estimated viewing distance in centimeters
|
||||
trial_data.viewing_distance_cm = (data["viewDistance_mm"]/10); // add to trial_data
|
||||
|
||||
|
||||
dist = Math.round(data["viewDistance_mm"]/10)
|
||||
|
||||
// The trial must end
|
||||
end_trial()
|
||||
|
||||
|
||||
return trial_data.viewing_distance_cm;
|
||||
}
|
||||
|
||||
ball.stop();
|
||||
animateBall();
|
||||
}
|
||||
}
|
||||
|
||||
//helper function for radians
|
||||
// Converts from degrees to radians.
|
||||
Math.radians = function(degrees) {
|
||||
return degrees * Math.PI / 180;
|
||||
};
|
||||
|
||||
|
||||
// You can write functions here that live only in the scope of plugin.trial
|
||||
function show_stimulus(){
|
||||
|
||||
// create html for display
|
||||
|
||||
var html = "<body><div id='content'><div id='page-size'><br><br><br><br><br><br>";
|
||||
// html += "<h3> Let’s find out what your monitor size is (click to go into <div onclick='fullScreen(); registerClick();' style='display:inline; cursor:pointer; color: red'><em><u>full screen mode</u></em></div>).</h2>";
|
||||
|
||||
html += "<p>Please use any credit card that you have available (it can also be a grocery store membership card, your drivers license, or anything that is of the same format), hold it onto the screen, and adjust the slider below to its size.</p>";
|
||||
html += "<p>(If you don't have access to a real card, you can use a ruler to measure image width to 3.37inch or 85.6mm, or make your best guess!)</p>";
|
||||
html += "<b style='font-style: italic'>Make sure you put the card onto your screen.</b>";
|
||||
html += '<br><div id="container">';
|
||||
html += "<div id='slider'></div>";
|
||||
html += '<br> <img id="card" src="card.png" style="width: 50%"><br><br>';
|
||||
html +='<button id="btnBlindSpot" class="btn btn-primary">Click here when you are done!</button></div></div>';
|
||||
|
||||
|
||||
html += '<div id="blind-spot" style="visibility: hidden">';
|
||||
html += '<!-- <h2 class="bolded-blue">Task 2: Where’s your blind spot?</h2> -->';
|
||||
html += "<h3>Now, let's quickly test how far away you are sitting.</h3>";
|
||||
html += "<p>You might know that vision tests at a doctor's practice often involve chinrests; the doctor basically asks you to sit away from a screen in a specific distance. We do this here with a 'virtual chinrest'.</p>";
|
||||
|
||||
html += '<h3>Instructions</h3>';
|
||||
html += '<p>1. Put your finger on <b>space bar</b> on the keyboard.</p>';
|
||||
html += '<p>2. Close your right eye. <em>(Tips: it might be easier to cover your right eye by hand!)</em></p>';
|
||||
html += '<p>3. Using your left eye, focus on the black square.</p>';
|
||||
html += '<p>4. Click the button below to start the animation of the red ball. The <b style="color: red">red ball</b> will disappear as it moves from right to left. Press the Space key as soon as the ball disappears from your eye sight.</p><br>';
|
||||
html += '<p>Please do it <b>five</b> times. Keep your right eye closed and hit the Space key fast!</p><br>';
|
||||
html += '<button class="btn btn-primary" id="start" ">Start</button>';
|
||||
|
||||
html += '<div id="svgDiv" style="width:1000px;height:200px;"></div>';
|
||||
html += "Hit 'space' <div id='click' style='display:inline; color: red; font-weight: bold'>5</div> more times!</div>";
|
||||
|
||||
|
||||
// render
|
||||
display_element.innerHTML = html;
|
||||
|
||||
|
||||
//Event listeners for buttons
|
||||
|
||||
document.getElementById("btnBlindSpot").addEventListener('click', function() {
|
||||
configureBlindSpot();
|
||||
});
|
||||
|
||||
document.getElementById("start").addEventListener('click', function() {
|
||||
animateBall();
|
||||
});
|
||||
|
||||
|
||||
jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response, // we need to create after_response
|
||||
valid_responses: [trial.key], // valid_responses expects an array
|
||||
rt_method: 'performance', // This is only relevant for RT in audio stimuli
|
||||
persist: false, // true if you want to listen to more than one key
|
||||
allow_held_key: true // false for a new key pressing in order to get a new response
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function after_response(response_info){
|
||||
// rt.push(response_info.rt); // response time of the key
|
||||
end_trial();
|
||||
}
|
||||
|
||||
function end_trial(){
|
||||
jsPsych.finishTrial(trial_data); // ends trial and save the data
|
||||
// display_element.innerHTML = ' '; // clear the display
|
||||
|
||||
jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
||||
|
||||
|
||||
}
|
||||
show_stimulus();
|
||||
|
||||
};
|
||||
|
||||
return plugin;
|
||||
})();
|
Loading…
Reference in New Issue
Block a user