mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-12 08:38:11 +00:00
commit
e2d66c4de1
@ -4,11 +4,7 @@
|
|||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta content="utf-8" http-equiv="encoding">
|
<meta content="utf-8" http-equiv="encoding">
|
||||||
|
|
||||||
<!-- css -->
|
<!-- SVG.js -->
|
||||||
<!--<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
|
||||||
<link href="css/jquery-ui.css" rel="stylesheet" type="text/css">-->
|
|
||||||
|
|
||||||
<!-- SVG.js -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.3/svg.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.3/svg.min.js"></script>
|
||||||
|
|
||||||
<!-- JsPsych -->
|
<!-- JsPsych -->
|
||||||
|
@ -97,7 +97,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
default: "Start",
|
default: "Start",
|
||||||
description: "Content of the start button for the blindspot tasks.",
|
description: "Content of the start button for the blindspot tasks.",
|
||||||
},
|
},
|
||||||
|
|
||||||
blindspot_measurements_prompt: {
|
blindspot_measurements_prompt: {
|
||||||
type: jsPsych.plugins.parameterType.STRING,
|
type: jsPsych.plugins.parameterType.STRING,
|
||||||
default: "Remaining measurements: ",
|
default: "Remaining measurements: ",
|
||||||
@ -105,11 +105,11 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
},
|
},
|
||||||
viewing_distance_report: {
|
viewing_distance_report: {
|
||||||
type: jsPsych.plugins.parameterType.STRING,
|
type: jsPsych.plugins.parameterType.STRING,
|
||||||
default: "<p>Based on your responses, you are sitting about <span id='distance-estimate' style='font-weight: bold;'></span> from the screen</p><p>Does that seem about right?</p>",
|
default: "<p>Based on your responses, you are sitting about <span id='distance-estimate' style='font-weight: bold;'></span> from the screen.</p><p>Does that seem about right?</p>",
|
||||||
description:
|
description:
|
||||||
'If "none" is given, viewing distance will not be reported to the participant',
|
'If "none" is given, viewing distance will not be reported to the participant',
|
||||||
},
|
},
|
||||||
redo_measurement_button_label: {
|
redo_measurement_button_label: {
|
||||||
type: jsPsych.plugins.parameterType.STRING,
|
type: jsPsych.plugins.parameterType.STRING,
|
||||||
default: 'No, that is not close. Try again.'
|
default: 'No, that is not close. Try again.'
|
||||||
},
|
},
|
||||||
@ -127,25 +127,19 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
console.error("Blindspot repetitions set to 0, so resizing to degrees of visual angle is not possible!");
|
console.error("Blindspot repetitions set to 0, so resizing to degrees of visual angle is not possible!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* some additional parameter configuration */
|
/* some additional parameter configuration */
|
||||||
var w = window.innerWidth;
|
|
||||||
var h = window.innerHeight;
|
|
||||||
|
|
||||||
const screen_size_px = [w, "x", h];
|
|
||||||
|
|
||||||
let trial_data = {
|
let trial_data = {
|
||||||
item_width_mm: trial.item_width_mm,
|
item_width_mm: trial.item_width_mm,
|
||||||
item_height_mm: trial.item_height_mm, //card dimension: 85.60 × 53.98 mm (3.370 × 2.125 in)
|
item_height_mm: trial.item_height_mm, //card dimension: 85.60 × 53.98 mm (3.370 × 2.125 in)
|
||||||
};
|
};
|
||||||
|
|
||||||
let config_data = {
|
let blindspot_config_data = {
|
||||||
ball_pos: [],
|
ball_pos: [],
|
||||||
slider_clck: false,
|
slider_clck: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let aspect_ratio = 1;
|
let aspect_ratio = trial.item_width_mm / trial.item_height_mm;
|
||||||
|
|
||||||
aspect_ratio = trial.item_width_mm / trial.item_height_mm;
|
|
||||||
const start_div_height =
|
const start_div_height =
|
||||||
aspect_ratio < 1
|
aspect_ratio < 1
|
||||||
? trial.item_init_size
|
? trial.item_init_size
|
||||||
@ -160,7 +154,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
let pagesize_content = `
|
let pagesize_content = `
|
||||||
<div id="page-size">
|
<div id="page-size">
|
||||||
<div id="item" style="border: none; height: ${start_div_height}px; width: ${start_div_width}px; margin: 5px auto; background-color: none; position: relative; background-image: url(${trial.item_path}); background-size: 100% auto; background-repeat: no-repeat;">
|
<div id="item" style="border: none; height: ${start_div_height}px; width: ${start_div_width}px; margin: 5px auto; background-color: none; position: relative; background-image: url(${trial.item_path}); background-size: 100% auto; background-repeat: no-repeat;">
|
||||||
<div id="jspsych-resize-handle" style="cursor: nwse-resize; background-color: none; width: ${adjust_size}px; height: ${adjust_size}px; border: 5px solid red; border-left: 0; border-top: 0; position: absolute; bottom: 0; right: 0;">' +
|
<div id="jspsych-resize-handle" style="cursor: nwse-resize; background-color: none; width: ${adjust_size}px; height: ${adjust_size}px; border: 5px solid red; border-left: 0; border-top: 0; position: absolute; bottom: 0; right: 0;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${trial.adjustment_prompt}
|
${trial.adjustment_prompt}
|
||||||
@ -186,7 +180,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
${trial.viewing_distance_report}
|
${trial.viewing_distance_report}
|
||||||
</b>
|
</b>
|
||||||
</div>` : ''
|
</div>` : ''
|
||||||
}
|
}
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
/* create content for final report screen */
|
/* create content for final report screen */
|
||||||
@ -200,73 +194,84 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|
||||||
|
display_element.innerHTML = `<div id="content" style="width: 900px; margin: 0 auto;"></div>`
|
||||||
/* show first screen */
|
|
||||||
display_element.innerHTML = `
|
|
||||||
<div id="content" style="width: 900px; margin: 0 auto;">
|
|
||||||
${pagesize_content}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
|
|
||||||
const start_time = performance.now();
|
const start_time = performance.now();
|
||||||
|
startResizePhase();
|
||||||
|
|
||||||
// Event listeners for mouse-based resize
|
function startResizePhase() {
|
||||||
let dragging = false;
|
display_element.querySelector('#content').innerHTML = pagesize_content;
|
||||||
let origin_x, origin_y;
|
|
||||||
let cx, cy;
|
|
||||||
const scale_div = display_element.querySelector("#item");
|
|
||||||
|
|
||||||
function mouseupevent() {
|
// Event listeners for mouse-based resize
|
||||||
dragging = false;
|
let dragging = false;
|
||||||
};
|
let origin_x, origin_y;
|
||||||
document.addEventListener("mouseup", mouseupevent);
|
let cx, cy;
|
||||||
|
const scale_div = display_element.querySelector("#item");
|
||||||
|
|
||||||
function mousedownevent(e) {
|
function mouseupevent() {
|
||||||
e.preventDefault();
|
dragging = false;
|
||||||
dragging = true;
|
};
|
||||||
origin_x = e.pageX;
|
document.addEventListener("mouseup", mouseupevent);
|
||||||
origin_y = e.pageY;
|
|
||||||
cx = parseInt(scale_div.style.width);
|
|
||||||
cy = parseInt(scale_div.style.height);
|
|
||||||
};
|
|
||||||
display_element.querySelector("#jspsych-resize-handle").addEventListener("mousedown", mousedownevent);
|
|
||||||
|
|
||||||
function resizeevent(e) {
|
function mousedownevent(e) {
|
||||||
if (dragging) {
|
e.preventDefault();
|
||||||
let dx = e.pageX - origin_x;
|
dragging = true;
|
||||||
let dy = e.pageY - origin_y;
|
origin_x = e.pageX;
|
||||||
|
origin_y = e.pageY;
|
||||||
|
cx = parseInt(scale_div.style.width);
|
||||||
|
cy = parseInt(scale_div.style.height);
|
||||||
|
};
|
||||||
|
display_element.querySelector("#jspsych-resize-handle").addEventListener("mousedown", mousedownevent);
|
||||||
|
|
||||||
if (Math.abs(dx) >= Math.abs(dy)) {
|
function resizeevent(e) {
|
||||||
scale_div.style.width =
|
if (dragging) {
|
||||||
Math.round(Math.max(20, cx + dx * 2)) + "px";
|
let dx = e.pageX - origin_x;
|
||||||
scale_div.style.height =
|
let dy = e.pageY - origin_y;
|
||||||
Math.round(Math.max(20, cx + dx * 2) / aspect_ratio) + "px";
|
|
||||||
} else {
|
if (Math.abs(dx) >= Math.abs(dy)) {
|
||||||
scale_div.style.height =
|
scale_div.style.width =
|
||||||
Math.round(Math.max(20, cy + dy * 2)) + "px";
|
Math.round(Math.max(20, cx + dx * 2)) + "px";
|
||||||
scale_div.style.width =
|
scale_div.style.height =
|
||||||
Math.round(aspect_ratio * Math.max(20, cy + dy * 2)) + "px";
|
Math.round(Math.max(20, cx + dx * 2) / aspect_ratio) + "px";
|
||||||
|
} else {
|
||||||
|
scale_div.style.height =
|
||||||
|
Math.round(Math.max(20, cy + dy * 2)) + "px";
|
||||||
|
scale_div.style.width =
|
||||||
|
Math.round(aspect_ratio * Math.max(20, cy + dy * 2)) + "px";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
display_element.addEventListener("mousemove", resizeevent);
|
||||||
|
|
||||||
|
display_element.querySelector("#end_resize_phase").addEventListener("click", finishResizePhase);
|
||||||
|
|
||||||
}
|
}
|
||||||
display_element.addEventListener("mousemove", resizeevent);
|
|
||||||
|
|
||||||
display_element.querySelector("#end_resize_phase").addEventListener("click", finishResizePhase);
|
function finishResizePhase() {
|
||||||
|
// add item width info to data
|
||||||
function finishResizePhase(){
|
const item_width_px = getScaledItemWidth();
|
||||||
|
trial_data["item_width_px"] = Math.round(item_width_px);
|
||||||
|
const px2mm = convertPixelsToMM(item_width_px);
|
||||||
|
trial_data["px2mm"] = accurateRound(px2mm, 2);
|
||||||
// check what to do next
|
// check what to do next
|
||||||
if (trial.blindspot_reps > 0) {
|
if (trial.blindspot_reps > 0) {
|
||||||
get_item_width(); // modifies trial data
|
startBlindSpotPhase();
|
||||||
configureBlindSpot();
|
|
||||||
} else {
|
} else {
|
||||||
distanceSetup.px2mm(get_item_width());
|
|
||||||
endTrial();
|
endTrial();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function configureBlindSpot() {
|
function startBlindSpotPhase() {
|
||||||
|
// reset the config data in case we are redoing the measurement
|
||||||
|
blindspot_config_data = {
|
||||||
|
ball_pos: [],
|
||||||
|
slider_clck: false,
|
||||||
|
};
|
||||||
|
// add the content to the page
|
||||||
document.querySelector("#content").innerHTML = blindspot_content;
|
document.querySelector("#content").innerHTML = blindspot_content;
|
||||||
|
// draw the ball and fixation square
|
||||||
drawBall();
|
drawBall();
|
||||||
|
// wait for a spacebar to begin the animations
|
||||||
jsPsych.pluginAPI.getKeyboardResponse({
|
jsPsych.pluginAPI.getKeyboardResponse({
|
||||||
callback_function: startBall,
|
callback_function: startBall,
|
||||||
valid_responses: ['space'],
|
valid_responses: ['space'],
|
||||||
@ -276,8 +281,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var ball_position_listener = null;
|
function startBall() {
|
||||||
function startBall(){
|
|
||||||
ball_position_listener = jsPsych.pluginAPI.getKeyboardResponse({
|
ball_position_listener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||||
callback_function: recordPosition,
|
callback_function: recordPosition,
|
||||||
valid_responses: ['space'],
|
valid_responses: ['space'],
|
||||||
@ -288,7 +292,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
animateBall();
|
animateBall();
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishBlindSpotPhase(){
|
function finishBlindSpotPhase() {
|
||||||
ball.stop();
|
ball.stop();
|
||||||
|
|
||||||
jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
||||||
@ -296,20 +300,18 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
showReport();
|
showReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showReport(){
|
function showReport() {
|
||||||
// Display data
|
// Display data
|
||||||
display_element.querySelector("#content").innerHTML = report_content;
|
display_element.querySelector("#content").innerHTML = report_content;
|
||||||
display_element.querySelector('#distance-estimate').innerHTML = `${Math.round(trial_data["view_dist_mm"] / 10)} cm`
|
display_element.querySelector('#distance-estimate').innerHTML = `
|
||||||
|
${Math.round(trial_data["view_dist_mm"] / 10)} cm (${Math.round(trial_data["view_dist_mm"]*0.0393701)} inches)
|
||||||
|
`
|
||||||
|
|
||||||
display_element.querySelector("#redo_blindspot").addEventListener('click', configureBlindSpot)
|
display_element.querySelector("#redo_blindspot").addEventListener('click', startBlindSpotPhase)
|
||||||
display_element.querySelector("#proceed").addEventListener('click', endTrial);
|
display_element.querySelector("#proceed").addEventListener('click', endTrial);
|
||||||
}
|
}
|
||||||
|
|
||||||
function endTrial() {
|
function computeTransformation() {
|
||||||
// finish trial
|
|
||||||
trial_data.rt = performance.now() - start_time;
|
|
||||||
display_element.innerHTML = "";
|
|
||||||
|
|
||||||
trial_data.item_width_deg =
|
trial_data.item_width_deg =
|
||||||
(2 *
|
(2 *
|
||||||
Math.atan(
|
Math.atan(
|
||||||
@ -357,43 +359,49 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
delete trial_data.px2deg;
|
delete trial_data.px2deg;
|
||||||
delete trial_data.item_width_deg;
|
delete trial_data.item_width_deg;
|
||||||
}
|
}
|
||||||
// NEED TO REMOVE EVENT LISTENERS
|
|
||||||
|
|
||||||
jsPsych.finishTrial(trial_data);
|
|
||||||
jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_item_width() {
|
function endTrial() {
|
||||||
const item_width_px = parseFloat(
|
|
||||||
getComputedStyle(document.querySelector("#item"), null).width.replace(
|
// finish trial
|
||||||
"px",
|
trial_data.rt = performance.now() - start_time;
|
||||||
""
|
|
||||||
)
|
// remove lingering event listeners, just in case
|
||||||
);
|
jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
||||||
|
|
||||||
trial_data["item_width_px"] = distanceSetup.round(item_width_px, 2);
|
// compute final data
|
||||||
return item_width_px;
|
computeTransformation();
|
||||||
|
|
||||||
|
// clear the display
|
||||||
|
display_element.innerHTML = "";
|
||||||
|
|
||||||
|
// finish the trial
|
||||||
|
jsPsych.finishTrial(trial_data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScaledItemWidth() {
|
||||||
|
return document.querySelector('#item').getBoundingClientRect().width;
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawBall(pos = 180) {
|
function drawBall(pos = 180) {
|
||||||
// pos: define where the fixation square should be.
|
// pos: define where the fixation square should be.
|
||||||
var mySVG = SVG("svgDiv");
|
var mySVG = SVG("svgDiv");
|
||||||
const item_width_px = trial_data["item_width_px"];
|
const rectX = trial_data["px2mm"] * pos;
|
||||||
const rectX = distanceSetup.px2mm(item_width_px) * pos;
|
|
||||||
const ballX = rectX * 0.6; // define where the ball is
|
const ballX = rectX * 0.6; // define where the ball is
|
||||||
var ball = mySVG.circle(30).move(ballX, 50).fill("#f00");
|
var ball = mySVG.circle(30).move(ballX, 50).fill("#f00");
|
||||||
window.ball = ball;
|
window.ball = ball;
|
||||||
var square = mySVG.rect(30, 30).move(Math.min(rectX - 50, 950), 50); //square position
|
var square = mySVG.rect(30, 30).move(Math.min(rectX - 50, 950), 50); //square position
|
||||||
config_data["square_pos"] = distanceSetup.round(square.cx(), 2);
|
blindspot_config_data["square_pos"] = accurateRound(square.cx(), 2);
|
||||||
config_data["rectX"] = rectX;
|
blindspot_config_data["rectX"] = rectX;
|
||||||
config_data["ballX"] = ballX;
|
blindspot_config_data["ballX"] = ballX;
|
||||||
}
|
}
|
||||||
|
|
||||||
function animateBall() {
|
function animateBall() {
|
||||||
ball
|
ball
|
||||||
.animate(7000)
|
.animate(7000)
|
||||||
.during(function (pos) {
|
.during(function (pos) {
|
||||||
moveX = -pos * config_data["ballX"];
|
moveX = -pos * blindspot_config_data["ballX"];
|
||||||
window.moveX = moveX;
|
window.moveX = moveX;
|
||||||
moveY = 0;
|
moveY = 0;
|
||||||
ball.attr({ transform: "translate(" + moveX + "," + moveY + ")" }); //jqueryToVanilla: el.getAttribute('');
|
ball.attr({ transform: "translate(" + moveX + "," + moveY + ")" }); //jqueryToVanilla: el.getAttribute('');
|
||||||
@ -401,22 +409,22 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
.loop(true, false)
|
.loop(true, false)
|
||||||
.after(function () {
|
.after(function () {
|
||||||
animateBall();
|
animateBall();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function recordPosition(info) {
|
function recordPosition() {
|
||||||
// angle: define horizontal blind spot entry point position in degrees.
|
// angle: define horizontal blind spot entry point position in degrees.
|
||||||
const angle = 13.5;
|
const angle = 13.5;
|
||||||
|
|
||||||
config_data["ball_pos"].push(distanceSetup.round(ball.cx() + moveX, 2));
|
blindspot_config_data["ball_pos"].push(accurateRound(ball.cx() + moveX, 2));
|
||||||
var sum = config_data["ball_pos"].reduce((a, b) => a + b, 0);
|
var sum = blindspot_config_data["ball_pos"].reduce((a, b) => a + b, 0);
|
||||||
var ballPosLen = config_data["ball_pos"].length;
|
var ballPosLen = blindspot_config_data["ball_pos"].length;
|
||||||
config_data["avg_ball_pos"] = distanceSetup.round(sum / ballPosLen, 2);
|
blindspot_config_data["avg_ball_pos"] = accurateRound(sum / ballPosLen, 2);
|
||||||
var ball_sqr_distance =
|
var ball_sqr_distance =
|
||||||
(config_data["square_pos"] - config_data["avg_ball_pos"]) /
|
(blindspot_config_data["square_pos"] - blindspot_config_data["avg_ball_pos"]) /
|
||||||
trial_data["px2mm"];
|
trial_data["px2mm"];
|
||||||
var viewDistance = ball_sqr_distance / Math.radians(angle);
|
var viewDistance = ball_sqr_distance / Math.radians(angle);
|
||||||
trial_data["view_dist_mm"] = distanceSetup.round(viewDistance, 2);
|
trial_data["view_dist_mm"] = accurateRound(viewDistance, 2);
|
||||||
|
|
||||||
//counter and stop
|
//counter and stop
|
||||||
var counter = Number(document.querySelector("#click").textContent);
|
var counter = Number(document.querySelector("#click").textContent);
|
||||||
@ -429,20 +437,17 @@ jsPsych.plugins["virtual-chinrest"] = (function () {
|
|||||||
ball.stop();
|
ball.stop();
|
||||||
animateBall();
|
animateBall();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var distanceSetup = {
|
function convertPixelsToMM(item_width_px){
|
||||||
round: function (value, decimals) {
|
const px2mm = item_width_px / trial_data["item_width_mm"];
|
||||||
return Number(Math.round(value + "e" + decimals) + "e-" + decimals);
|
return px2mm;
|
||||||
},
|
|
||||||
px2mm: function (item_width_px) {
|
|
||||||
const px2mm = item_width_px / trial_data["item_width_mm"];
|
|
||||||
trial_data["px2mm"] = distanceSetup.round(px2mm, 2);
|
|
||||||
return px2mm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function accurateRound(value, decimals){
|
||||||
|
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user