diff --git a/.changeset/cold-jobs-begin.md b/.changeset/cold-jobs-begin.md new file mode 100644 index 00000000..c7cfefcb --- /dev/null +++ b/.changeset/cold-jobs-begin.md @@ -0,0 +1,5 @@ +--- +"@jspsych-contrib/plugin-html-swipe-response": patch +--- + +the patch ensures that both the container (`#jspsych-html-swipe-response-stimulus-container`) and the stimulus (`#jspsych-html-swipe-response-stimulus`) move together when dragged, providing a unified and seamless interaction. diff --git a/packages/plugin-html-swipe-response/README.md b/packages/plugin-html-swipe-response/README.md index 88137f9e..47a8f0aa 100644 --- a/packages/plugin-html-swipe-response/README.md +++ b/packages/plugin-html-swipe-response/README.md @@ -6,6 +6,8 @@ This plugin displays HTML content and records responses generated by swipe gestu Setting the `stimulus_duration` parameter while using the swipe modality can result in a user experience issue, wherein the user must swipe a stimulus div tag that has been hidden after the stimulus duration has elapsed. To solve this, this plugin wraps the stimulus div tag in another tag with the ID `#jspsych-html-swipe-response-stimulus-container`. This div tag can then be styled so that they user has some visual representation of the stimulus even after the `#jspsych-html-swipe-response-stimulus-container` div has been hidden. +The plugin now ensures that both the container (`#jspsych-html-swipe-response-stimulus-container`) and the stimulus (`#jspsych-html-swipe-response-stimulus`) move together when dragged, providing a unified and seamless interaction. + ## Loading ```js diff --git a/packages/plugin-html-swipe-response/docs/jspsych-html-swipe-response.md b/packages/plugin-html-swipe-response/docs/jspsych-html-swipe-response.md index 480fd696..322e8201 100644 --- a/packages/plugin-html-swipe-response/docs/jspsych-html-swipe-response.md +++ b/packages/plugin-html-swipe-response/docs/jspsych-html-swipe-response.md @@ -4,6 +4,8 @@ This plugin displays HTML content and records responses generated by swipe gestu Setting the `stimulus_duration` parameter while using the swipe modality can result in a user experience issue, wherein the user must swipe a stimulus div tag that has been hidden after the stimulus duration has elapsed. To solve this, this plugin wraps the stimulus div tag in another tag with the ID `#jspsych-html-swipe-response-stimulus-container`. This div tag can then be styled so that they user has some visual representation of the stimulus even after the `#jspsych-html-swipe-response-stimulus-container` div has been hidden. +The plugin now ensures that both the container (`#jspsych-html-swipe-response-stimulus-container`) and the stimulus (`#jspsych-html-swipe-response-stimulus`) move together when dragged, providing a unified and seamless interaction. + ## Parameters In addition to the [parameters available in all plugins](https://www.jspsych.org/overview/plugins#parameters-available-in-all-plugins), this plugin accepts the following parameters. Parameters with a default value of *undefined* must be specified. Parameters can be left unspecified if the default value is acceptable. diff --git a/packages/plugin-html-swipe-response/src/index.spec.ts b/packages/plugin-html-swipe-response/src/index.spec.ts index 92195f7b..cfe5dbea 100644 --- a/packages/plugin-html-swipe-response/src/index.spec.ts +++ b/packages/plugin-html-swipe-response/src/index.spec.ts @@ -1,4 +1,5 @@ import { clickTarget, pressKey, simulateTimeline, startTimeline } from "@jspsych/test-utils"; +import interact from "interactjs"; import htmlSwipeResponse from "."; @@ -289,6 +290,34 @@ describe("plugin-html-swipe-response", () => { expect(element.getAttribute("disabled")).toBe("disabled"); }); }); + + test("should move container and stimulus together during drag", async () => { + const { displayElement } = await startTimeline([ + { + type: htmlSwipeResponse, + stimulus: "this is html", + swipe_animation_duration: 0, + }, + ]); + + const container = displayElement.querySelector( + "#jspsych-html-swipe-response-stimulus-container" + ); + const stimulus = displayElement.querySelector( + "#jspsych-html-swipe-response-stimulus" + ); + + // Simulate drag event manually using interact.js drag events + interact(container).fire({ + type: "dragmove", + target: container, + delta: { x: 100, y: 50 }, + }); + + // Now test if the transforms are applied correctly + expect(container.style.transform).toBe("translate3D(100px, 50px, 0)"); + expect(stimulus.style.transform).toBe("translate3D(100px, 50px, 0) rotate(20deg)"); + }); }); describe("html-swipe-response simulation", () => { diff --git a/packages/plugin-html-swipe-response/src/index.ts b/packages/plugin-html-swipe-response/src/index.ts index 6c5d9348..a1c0c6c5 100644 --- a/packages/plugin-html-swipe-response/src/index.ts +++ b/packages/plugin-html-swipe-response/src/index.ts @@ -167,6 +167,8 @@ class HtmlSwipeResponsePlugin implements JsPsychPlugin { source: null, }; + // References to container and stimulus + const container_div = document.getElementById("jspsych-html-swipe-response-stimulus-container"); const stimulus_div = document.getElementById("jspsych-html-swipe-response-stimulus"); let position = { @@ -178,26 +180,35 @@ class HtmlSwipeResponsePlugin implements JsPsychPlugin { const setPosition = (coordinates) => { const { x = 0, y = 0, rotation = 0 } = coordinates; position = { x, y, rotation }; + container_div.style.transform = `translate3D(${x}px, ${y}px, 0)`; stimulus_div.style.transform = `translate3D(${x}px, ${y}px, 0) rotate(${rotation}deg)`; }; + // Reset the position of the stimulus and container const resetPosition = async () => { stimulus_div.style.transition = `${trial.swipe_animation_duration / 1000}s ease-in-out, ${ trial.swipe_animation_duration / 1000 }s ease-in`; + container_div.style.transition = `${trial.swipe_animation_duration / 1000}s ease-in-out, ${ + trial.swipe_animation_duration / 1000 + }s ease-in`; setPosition({ x: 0, y: 0, rotation: 0 }); stimulus_div.style.transition = null; + container_div.style.transition = null; }; + // Handle drag movement of the stimulus and container together const dragMoveListener = (event) => { const x = position.x + event.delta.x; const y = position.y + event.delta.y; let rotation = 0; - if (position.x > 0) { - rotation = Math.min(trial.swipe_animation_max_rotation, position.x / 4); + + if (x > 0) { + rotation = Math.min(trial.swipe_animation_max_rotation, x / 4); } else { - rotation = Math.max(-trial.swipe_animation_max_rotation, position.x / 4); + rotation = Math.max(-trial.swipe_animation_max_rotation, x / 4); } + setPosition({ x: x, y: y, rotation }); }; @@ -219,6 +230,9 @@ class HtmlSwipeResponsePlugin implements JsPsychPlugin { stimulus_div.style.transition = `${trial.swipe_animation_duration / 1000}s ease-in-out, ${ trial.swipe_animation_duration / 1000 }s ease-in`; + container_div.style.transition = `${trial.swipe_animation_duration / 1000}s ease-in-out, ${ + trial.swipe_animation_duration / 1000 + }s ease-in`; setPosition({ x: -trial.swipe_offscreen_coordinate, y: position.y, @@ -230,6 +244,9 @@ class HtmlSwipeResponsePlugin implements JsPsychPlugin { stimulus_div.style.transition = `${trial.swipe_animation_duration / 1000}s ease-in-out, ${ trial.swipe_animation_duration / 1000 }s ease-in`; + container_div.style.transition = `${trial.swipe_animation_duration / 1000}s ease-in-out, ${ + trial.swipe_animation_duration / 1000 + }s ease-in`; setPosition({ x: trial.swipe_offscreen_coordinate, y: position.y, @@ -288,7 +305,7 @@ class HtmlSwipeResponsePlugin implements JsPsychPlugin { } }; - interact(stimulus_div).draggable({ + interact(container_div).draggable({ inertia: false, autoScroll: true, modifiers: [ @@ -393,6 +410,7 @@ class HtmlSwipeResponsePlugin implements JsPsychPlugin { } interact(stimulus_div).unset(); + interact(container_div).unset(); // gather the data to store for the trial const trial_data = {