diff --git a/examples/jspsych-virtual-chinrest.html b/examples/jspsych-virtual-chinrest.html index 46fe05df..3598b969 100644 --- a/examples/jspsych-virtual-chinrest.html +++ b/examples/jspsych-virtual-chinrest.html @@ -4,11 +4,7 @@ - - - - + diff --git a/plugins/jspsych-virtual-chinrest.js b/plugins/jspsych-virtual-chinrest.js index 3b0159ce..7ba7cf00 100644 --- a/plugins/jspsych-virtual-chinrest.js +++ b/plugins/jspsych-virtual-chinrest.js @@ -97,7 +97,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () { default: "Start", description: "Content of the start button for the blindspot tasks.", }, - + blindspot_measurements_prompt: { type: jsPsych.plugins.parameterType.STRING, default: "Remaining measurements: ", @@ -105,11 +105,11 @@ jsPsych.plugins["virtual-chinrest"] = (function () { }, viewing_distance_report: { type: jsPsych.plugins.parameterType.STRING, - default: "

Based on your responses, you are sitting about from the screen

Does that seem about right?

", + default: "

Based on your responses, you are sitting about from the screen.

Does that seem about right?

", description: '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, 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!"); } - /* some additional parameter configuration */ - var w = window.innerWidth; - var h = window.innerHeight; - - const screen_size_px = [w, "x", h]; - + /* some additional parameter configuration */ let trial_data = { 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) }; - let config_data = { + let blindspot_config_data = { ball_pos: [], 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 = aspect_ratio < 1 ? trial.item_init_size @@ -160,7 +154,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () { let pagesize_content = `
-
' + +
${trial.adjustment_prompt} @@ -186,7 +180,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () { ${trial.viewing_distance_report}
` : '' - } + }
` /* create content for final report screen */ @@ -200,73 +194,84 @@ jsPsych.plugins["virtual-chinrest"] = (function () { ` - - /* show first screen */ - display_element.innerHTML = ` -
- ${pagesize_content} -
- ` + display_element.innerHTML = `
` const start_time = performance.now(); + startResizePhase(); - // Event listeners for mouse-based resize - let dragging = false; - let origin_x, origin_y; - let cx, cy; - const scale_div = display_element.querySelector("#item"); + function startResizePhase() { + display_element.querySelector('#content').innerHTML = pagesize_content; - function mouseupevent() { - dragging = false; - }; - document.addEventListener("mouseup", mouseupevent); + // Event listeners for mouse-based resize + let dragging = false; + let origin_x, origin_y; + let cx, cy; + const scale_div = display_element.querySelector("#item"); - function mousedownevent(e) { - e.preventDefault(); - dragging = true; - 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); + function mouseupevent() { + dragging = false; + }; + document.addEventListener("mouseup", mouseupevent); - function resizeevent(e) { - if (dragging) { - let dx = e.pageX - origin_x; - let dy = e.pageY - origin_y; + function mousedownevent(e) { + e.preventDefault(); + dragging = true; + 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)) { - scale_div.style.width = - Math.round(Math.max(20, cx + dx * 2)) + "px"; - scale_div.style.height = - 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"; + function resizeevent(e) { + if (dragging) { + let dx = e.pageX - origin_x; + let dy = e.pageY - origin_y; + + if (Math.abs(dx) >= Math.abs(dy)) { + scale_div.style.width = + Math.round(Math.max(20, cx + dx * 2)) + "px"; + scale_div.style.height = + 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(){ + function finishResizePhase() { + // add item width info to data + 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 if (trial.blindspot_reps > 0) { - get_item_width(); // modifies trial data - configureBlindSpot(); + startBlindSpotPhase(); } else { - distanceSetup.px2mm(get_item_width()); 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; + // draw the ball and fixation square drawBall(); + // wait for a spacebar to begin the animations jsPsych.pluginAPI.getKeyboardResponse({ callback_function: startBall, 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({ callback_function: recordPosition, valid_responses: ['space'], @@ -288,7 +292,7 @@ jsPsych.plugins["virtual-chinrest"] = (function () { animateBall(); } - function finishBlindSpotPhase(){ + function finishBlindSpotPhase() { ball.stop(); jsPsych.pluginAPI.cancelAllKeyboardResponses(); @@ -296,20 +300,18 @@ jsPsych.plugins["virtual-chinrest"] = (function () { showReport(); } - function showReport(){ + function showReport() { // Display data 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); } - function endTrial() { - // finish trial - trial_data.rt = performance.now() - start_time; - display_element.innerHTML = ""; - + function computeTransformation() { trial_data.item_width_deg = (2 * Math.atan( @@ -357,43 +359,49 @@ jsPsych.plugins["virtual-chinrest"] = (function () { delete trial_data.px2deg; delete trial_data.item_width_deg; } - // NEED TO REMOVE EVENT LISTENERS - - jsPsych.finishTrial(trial_data); - jsPsych.pluginAPI.cancelAllKeyboardResponses(); } - function get_item_width() { - const item_width_px = parseFloat( - getComputedStyle(document.querySelector("#item"), null).width.replace( - "px", - "" - ) - ); - - trial_data["item_width_px"] = distanceSetup.round(item_width_px, 2); - return item_width_px; + function endTrial() { + + // finish trial + trial_data.rt = performance.now() - start_time; + + // remove lingering event listeners, just in case + jsPsych.pluginAPI.cancelAllKeyboardResponses(); + + // compute final data + 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) { // pos: define where the fixation square should be. var mySVG = SVG("svgDiv"); - const item_width_px = trial_data["item_width_px"]; - const rectX = distanceSetup.px2mm(item_width_px) * pos; + const rectX = trial_data["px2mm"] * 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 - config_data["square_pos"] = distanceSetup.round(square.cx(), 2); - config_data["rectX"] = rectX; - config_data["ballX"] = ballX; + blindspot_config_data["square_pos"] = accurateRound(square.cx(), 2); + blindspot_config_data["rectX"] = rectX; + blindspot_config_data["ballX"] = ballX; } function animateBall() { ball .animate(7000) .during(function (pos) { - moveX = -pos * config_data["ballX"]; + moveX = -pos * blindspot_config_data["ballX"]; window.moveX = moveX; moveY = 0; ball.attr({ transform: "translate(" + moveX + "," + moveY + ")" }); //jqueryToVanilla: el.getAttribute(''); @@ -401,22 +409,22 @@ jsPsych.plugins["virtual-chinrest"] = (function () { .loop(true, false) .after(function () { animateBall(); - }); + }); } - - function recordPosition(info) { + + function recordPosition() { // angle: define horizontal blind spot entry point position in degrees. const angle = 13.5; - - config_data["ball_pos"].push(distanceSetup.round(ball.cx() + moveX, 2)); - var sum = config_data["ball_pos"].reduce((a, b) => a + b, 0); - var ballPosLen = config_data["ball_pos"].length; - config_data["avg_ball_pos"] = distanceSetup.round(sum / ballPosLen, 2); + + blindspot_config_data["ball_pos"].push(accurateRound(ball.cx() + moveX, 2)); + var sum = blindspot_config_data["ball_pos"].reduce((a, b) => a + b, 0); + var ballPosLen = blindspot_config_data["ball_pos"].length; + blindspot_config_data["avg_ball_pos"] = accurateRound(sum / ballPosLen, 2); 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"]; 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 var counter = Number(document.querySelector("#click").textContent); @@ -429,20 +437,17 @@ jsPsych.plugins["virtual-chinrest"] = (function () { ball.stop(); animateBall(); } - + } - var distanceSetup = { - round: function (value, decimals) { - return Number(Math.round(value + "e" + decimals) + "e-" + decimals); - }, - 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 convertPixelsToMM(item_width_px){ + const px2mm = item_width_px / trial_data["item_width_mm"]; + return px2mm; } + function accurateRound(value, decimals){ + return Number(Math.round(value+'e'+decimals)+'e-'+decimals); + } };