DragDrop features

PennController for IBEX Forums Support DragDrop features

Viewing 12 posts - 1 through 12 (of 12 total)
  • Author
    Posts
  • #7131
    AlexY
    Participant

    Hi, I found on the documentation that we can do drag and drop now. I have been trying to use this feature by testing it out with a simple trial but unsuccessful. Here is the demo.

    Basically for this trial, I am just trying to see if the image can be dragged to either one of the gaps.

    Another question about the documentation page: would it be possible to have a demo link that includes the commands on that page so I can see how it actually works?

    Thanks a lot in advance!

    Alex

    #7135
    Jeremy
    Keymaster

    Hi Alex,

    It is wiser to keep the debugger on until you’ve shared the data-collection link with your participants. For example, after I copied your project, I re-activated the debugger and noticed your project uses PennController 1.9, when the DragDrop element was introduced in 2.0.

    After I uploaded the most recent version of PennController.js to the Modules folder, the experiment started to run but stayed on the preloading page with the debugger complaining:

    Found an existing Image element named “fish-plural”--using name “fish-plural2” instead for new element

    Indeed, at line 43 you have newImage("fish-plural") when it should be getImage("fish-plural")—the experiment would try (and fail) to preload an inexistent image with the filename fish-plural2

    After I fixed that, the debugger no longer threw error messages, but I was still unable to drag the image: I looked up the Log tab of the debugger and noticed that the last command was add on the “drag” Canvas element, when it should have been wait on the DragDrop element if everything had run smoothly. And indeed, at line 36 you have .add(getImage("fish-plural")): coordinates are missing here, so PennController is trying to interpret the reference to the image as an X coordinate, and crashes (I should make the debugger throw an error message in such cases)

    After I provide coordinates to that add command, things work… -ish. Because the Canvas elements are created with no dimensions (e.g. newCanvas("gap-0")) they technically occupy no space on the page, and end up overlapping one another( (although uou can still see “gap-1” because it is printed 250px to the right of the left edge of the Canvas element). Giving them a size, for example 200×200 (newCanvas("gap-0", 200,200)) improves things: I can now drag and drop the image onto gap-0 or gap-1, and the trial will complete as expected (however it’s not easy to see with gap-1, because the text is printed 250px to the right, out of bounds of a 200×200 canvas)

    Note that because you print your Text elements onto Canvas elements, you don’t need any of the center, unfold or print commands you call on them: the add command will print them at the provided coordinates at once when executed. So here’s an optimized code (I added background colors to the canvases to delineate them visually):

    newCanvas("gap-0", 200,200)
            .add( "center at 50%", "middle at 50%", newText("gap0"))
            .color("lightblue")
            .center()
            .print()
            .log() //remember this
        ,
        newCanvas("gap-1", 200,200)
            .add( "center at 50%", "middle at 50%", newText("gap1"))
            .color("pink")
            .center()
            .print()
            .log() //remember this
        ,
        newCanvas("drag", 200,200)
            .add(0,0,newImage("fish-plural", "2fishRoundTank.png").size(200, 200) )
            .center()
            .print()
            .log()
        ,
        newDragDrop("dd")
            .addDrop(getCanvas("gap-0"),getCanvas("gap-1"))
            .addDrag(getImage("fish-plural"))
            .offset(25) // will pin the elements to (25,25) from the top-left edge of the dropzone
            .single()
            .log()
            .wait()
    

    Thank you for the suggestion about including a demonstration feature for code blocks on the documentation, it’s something I would like to implement one day, but unfortunately I cannot dedicate time to it at the moment

    Jeremy

    #7139
    AlexY
    Participant

    Thank you very much, Jeremy!

    Based on the codes you suggested, I am getting closer to what I wanted to do for my experiment: Basically I am trying to create a trial where the two gaps (gap-0 and gap-1) are embedded in a sentence, e.g. “This is ____ and _____.” There will be two draggable items where participants have to decide which item to put in which gap. I have tried various ways to modify the code but I can’t seem to solve the following issues:

    1. I cannot make the instruction display on top of the page.
    2. I cannot make the two canvases display horizontally. I have the same problem with the two draggable items.
    3. Is there a way to present the two canvases in a way that they are embedded in the sentence above: “This is _(gap-0)_ and _(gap-1)_.”?

    Thank you very much again for your help!

    Alex

    #7140
    AlexY
    Participant

    Sorry, please ignore #1, it was simply an ordering issue.

    #7143
    Jeremy
    Keymaster

    Hi Alex,

    Here is a template/demo project featuring the DragDrop element that you might be interested in taking a look at: https://farm.pcibex.net/r/MOytaR/

    2. the main purpose of Canvas elements is precisely to control visual layout, in particular when you need to print two elements side by side. Here’s an example, printing two Canvas elements side by side:

    
    newCanvas("container", 400, 200)
      .add( 0,0, newCanvas("pink patch", 200,200).color("pink")) 
      .add( 0,0, newCanvas("blue patch", 200,200).color("blue"))
      .print()
    

    3. look at the code of the first trial in the demo project I shared above to see an example

    Jeremy

    #8413
    mwf2120
    Participant

    Hi! I am relatively new to Pcibex and am trying to do the following: log coordinate location of images (between 2 and 5 depending on set randomization) that are dragged and dropped to another canvas. The figures (images) can be placed anywhere and in any order on the new canvas. I’d really like to record x and y coordinates where participants place each figure. I’ve been able to implement the drag and drop approach, and to collect coordinate data for clicks, but coordinate data collection fails when I try to drag and drop (probably because the images are “clicked” in one place and “unclicked” in another). Is there a better way to do this? Thanks so much!

    Drag and drop sample approach:

    newTrial(“Set_2”,
    newCanvas(“figlist”, “72vw”,”58vh”)
    .css(“padding”,”1em”)
    .color(“lightgray”)
    .print(“left at 5vw”, “middle at 50vh”)
    ,
    newCanvas(“grid”, “72vw”, “58vh”)
    .css(“padding”, “1em”)
    .color(“yellow”)
    .print(“left at 20vw”, “middle at 50vh”)
    ,
    newSelector(“figures”)
    ,
    newImage(“man”, “man_small.png”).selector(“figures”).print(getCanvas(“figlist”)),
    newImage(“boy”, “boy_small.png”).selector(“figures”).print(getCanvas(“figlist”))
    ,
    getSelector(“figures”).shuffle()
    .log()
    ,
    newDragDrop(“dd”)
    .addDrag(getImage(“man”))
    .addDrag(getImage(“boy”))
    .addDrop(getCanvas(“figlist”))
    .log()
    ,
    newTimer(5000).start()
    .wait()
    )

    Thanks for any help!

    #9584
    ediachek
    Participant

    Hi Jeremy,

    I was wondering if it’s possible to “link” two pictures together using the Drag and drop feature. In my task, participants will hear “Put the bear on the flower”, for which I expect them to place one object on top of the other. Then, they will hear “Now put them in the box”, and I would hope that the two images of the bear and flower would get “linked” so that participants could just drop them in the box at the same time using one mouse movement. Looking forward to hearing your thoughts!!

    -Yev

    #9599
    Jeremy
    Keymaster

    Hi,

    I just realized I never addressed the previous post, so I will do it now. Unfortunately there is no built-in way to log coordinates in a DragDrop element, but it can be achieved using some javascript. The set command of Var elements accepts javascript functions, so we can create one Var element for each pair of coordinates we want to log:

    newTimer(5000).start()
      .wait()
    ,
    newVar("manXY").set(v=>{ const bcr = getImage("man")._element.jQueryElement[0].getBoundingClientRect(); return bcr.x+';'+bcr.y; }).log(),
    newVar("boyXY").set(v=>{ const bcr = getImage("boy")._element.jQueryElement[0].getBoundingClientRect(); return bcr.x+';'+bcr.y; }).log(),
    newVar("figlistXY").set(v=>{ const bcr = getCanvas("figlist")._element.jQueryElement[0].getBoundingClientRect(); return bcr.x+';'+bcr.y; }).log()
    )

    Now, regarding the linking of two pictures, it should be possible in theory, but I found out there are a few bugs with such chaining in the DragDrop element. We will need a few tricks to work around those bugs, namely we will remove the image of the bear once it’s placed on the flower, and replace it with a copy of it; we will need to retrieve the coordinates of the original image to do that though, which will involve some javascript
    We will also need to have the bear and the flower placed on a Canvas element and make them “transparent” so that the Canvas element can be dragged onto the box:

    ERRATUM: see this post on how to use a single DragDrop element instead

    newText("instructions", "Put the bear on the flower").print()
    ,
    newImage("bear","bear.png").size(100,100).print(),
    newImage("bearCopy","bear.png").size(100,100).cssContainer("pointer-events","none").css("pointer-events","none")
    ,
    newCanvas("flowerCV", 200,200).add(0,0,newImage("flower","flower.png").size(200,200)).print()
    ,
    newCanvas("boxCV", 300,300).add(0,0,newImage("box","box.png").size(300,300)).print()
    ,
    newDragDrop("bearToFlower")
        .addDrag(getImage("bear"))
        .addDrop(getCanvas("flowerCV"))
        .wait()
    ,
    newFunction(()=>new Promise(async r=>{
        const bearBCR = getImage("bear")._element.jQueryElement[0].getBoundingClientRect();
        const flowerBCR = getCanvas("flowerCV")._element.jQueryElement[0].getBoundingClientRect();
        await getCanvas("flowerCV").add(bearBCR.x-flowerBCR.x,bearBCR.y-flowerBCR.y,getImage("bearCopy"))._runPromises();
        r();
    })).call()
    ,
    getImage("bear").hidden().remove(),
    getImage("flower").cssContainer("pointer-events","none")
    ,
    getText("instructions").text("Now put them in the box")
    ,
    newDragDrop("flowerToBox")
        .addDrag(getCanvas("flowerCV"))
        .addDrop(getCanvas("boxCV"))
        .wait()
    

    Let me know if you have questions

    Jeremy

    • This reply was modified 1 year, 6 months ago by Jeremy. Reason: reference to correction below
    #9600
    ediachek
    Participant

    Thank you, Jeremy! This code works great!

    One more question. If I wanted a 3×3 grid with images so that the bear and flower would be at random positions from one trial to another, I think I would need to use the selector command with the shuffle argument. Where would I plug it in this code?

    Thanks!
    Yev

    #9601
    Jeremy
    Keymaster

    Simply print the “bear” Image element and the Canvas elements containing the other images on a bigger Canvas element, and then shuffle those:

    newCanvas("mainContainer", 900,300).print()
    ,
    newImage("bear","bear.png").size(100,100).print(0,0,getCanvas("mainContainer")),
    newImage("bearCopy","bear.png").size(100,100).cssContainer("pointer-events","none").css("pointer-events","none")
    ,
    newCanvas("flowerCV", 200,200).add(0,0,newImage("flower","flower.png").size(200,200)).print(300,0,getCanvas("mainContainer"))
    ,
    newCanvas("boxCV", 300,300).add(0,0,newImage("box","box.png").size(300,300)).print(600,0,getCanvas("mainContainer"))
    ,
    newSelector("shuffle").add(getImage("bear"),getCanvas("flowerCV"),getCanvas("boxCV")).shuffle().disable()

    Jeremy

    #9602
    Jeremy
    Keymaster

    Note that I found another bug with the suggestion above: creating a second DragDrop element messes with detecting dropping events on it, hence never validating its wait command. So instead of creating two DragDrop elements, one should create and reuse a single one:

    newDragDrop("dragdrop")
        .addDrag(getImage("bear"))
        .addDrop(getCanvas("flowerCV"))
        .wait()
        .removeDrag(getImage("bear"))
        .removeDrop(getCanvas("flowerCV"))
    ,
    // ...
    getDragDrop("dragdrop")
        .addDrag(getCanvas("flowerCV"))
        .addDrop(getCanvas("boxCV"))
        .wait()

    Jeremy

    #9604
    ediachek
    Participant

    This is working perfectly! Thank you so much!

Viewing 12 posts - 1 through 12 (of 12 total)
  • You must be logged in to reply to this topic.