Stack/pile dropped elements

Recommended Posts

Hi!

I'm trying to build an interactive balancing scale where the user can drag and drop 12 coins in the bowls in order to have the scale tilt left or right (this is for a puzzle for which I will apply the logic/math at a latter stage).

The svg-image has a hidden rectangle - a "drop area" - just above each bowl. I register which "drop area"/bowl a coin is dropped into. If a coin is dropped outside of the "drop areas"/bowls, it goes back to it's initial position.

The parts/requirements I need help with:

1. When a coin is dropped in a "drop area" I want it to fall down and land on top of the bowl below (not keep falling out of bounds like it does at the moment).
2. Each bowl should be able to contain anywhere from 0 to 12 coins and the coins should somehow stack/pile up in the bowl (coins cannot overlap because the user needs to see each coin's number).
3. A stack should some "unstack" or "unpile" when the user drags a coin away from it.

Any ideas or suggestions on how to solve this would be highly appreciated!

See the Pen YzzMomQ by Rawland_Hustle (@Rawland_Hustle) on CodePen

Share on other sites

Hey Rawland and welcome to the GreenSock forums!

3 hours ago, Rawland_Hustle said:

When a coin is dropped in a "drop area" I want it to fall down and land on top of the bowl below (not keep falling out of bounds like it does at the moment).

Modifying your current approach, you could use the modifiers plugin to limit how far it could travel.

3 hours ago, Rawland_Hustle said:

Each bowl should be able to contain anywhere from 0 to 12 coins and the coins should somehow stack/pile up in the bowl (coins cannot overlap because the user needs to see each coin's number).

A way I could see you doing this is having predefined positions. Then when the coin is dropped you have it go to one of those positions based on how many coins are already there. You wouldn't use the physics plugin to do this but you could make it look like it's falling by adjusting the ease.

Share on other sites

15 hours ago, ZachSaucier said:

Hey Rawland and welcome to the GreenSock forums!

Thank you! And thank you for replying.

I think the biggest challenge is to prevent the coins (circle elements) from overlapping. They might even have to collide and bounce off of eachother, since the user should be able to take a coin from the bottom of the pile and drag it away. Is there a plugin for this? If not, I might have to abandon the idea of dragging and dropping the coins.

Share on other sites

27 minutes ago, Rawland_Hustle said:

Is there a plugin for this? If not, I might have to abandon the idea of dragging and dropping the coins.

Not dynamically. Like I said if you used pre-defined positions you could fake it fairly convincing with a little work.

To do it dynamically you could use a small physics engine. When you drop dragging the coin correct it could then start using the physics engine. Not too hard to pair up

Share on other sites

34 minutes ago, ZachSaucier said:

...if you used pre-defined positions you could fake it fairly convincing with a little work.

Is there any documentation of predefined positions? I can't find any.

34 minutes ago, ZachSaucier said:

...you could use a small physics engine.

I've been looking at several physics engines for svg/js but couldn't find one that worked with existing svg elements or wasn't too complicated. Can you suggest a physics engine?

Thanks!

Share on other sites

31 minutes ago, Rawland_Hustle said:

Is there any documentation of predefined positions? I can't find any.

Nope. It just means positions that you define beforehand so like you have some coordinates that work like you want them to and then when you drop the coins they go to one of those positions.

33 minutes ago, Rawland_Hustle said:

Can you suggest a physics engine?

Blake, another moderator, suggests this one

Not sure if it works with SVG but your whole setup is pretty simple to make in non-SVG.

Share on other sites

2 minutes ago, ZachSaucier said:

Nope. It just means positions that you define beforehand so like you have some coordinates that work like you want them to and then when you drop the coins they go to one of those positions.

I'm totally new to SVG so I have no idea how one defines coordinates. Can't I just move an element (a circle/coin in the case) to some coordinates of the viewport somehow?

2 minutes ago, ZachSaucier said:

Not sure if it works with SVG but your whole setup is pretty simple to make in non-SVG.

What would a non-SVG setup be, HTML? Can I animare the scale the way I've done (press buttons at the bottom) in HTML?

Share on other sites

6 minutes ago, Rawland_Hustle said:

I'm totally new to SVG so I have no idea how one defines coordinates.

I didn't mean necessarily in SVG, though that wouldn't be a bad idea  I just meant have something like `var coords = [{x: 10, y: 20}, {x: 15, y: 20}, ...];` where you've tested beforehand that those coordinates won't have the coins overlap. Then when it's dropped you animate to that point and keep track of which positions have been taken to make sure you don't stack ones.

8 minutes ago, Rawland_Hustle said:

What would a non-SVG setup be, HTML?

Could be HTML or Canvas. Images would work in both situations.

9 minutes ago, Rawland_Hustle said:

Can I animare the scale the way I've done (press buttons at the bottom) in HTML?

Yep. HTML handles transforms just fine.

Share on other sites

I didn't know about Canvas. Do you recommend that I start all over with either Canvas or HTML?

Could you please give me a short example of moving an element to user defined coordinates with GSAP? Any coordinates will do. Like, okay, I've defined the coordinates in a array as you suggested: `var coords = [{x: 10, y: 20}, {x: 15, y: 20}, ...];`.  How do I move my dragged coin (`this.target`) to the first set of coordinates (`coords[0]`)?

Share on other sites

Should I use getBBox() to get the current x/y-values of the element when it's dropped and subtract the x/y-values of `coords[0]`?

Edit: Apparently getBBox() returns the element's original position, not it's current position . It doesn't matter though. Is getBBox() the way to go?

Share on other sites

Circle collision detection is the easiest.

See the Pen YXpdYN by osublake (@osublake) on CodePen

23 hours ago, Rawland_Hustle said:

I will apply the logic/math at a latter stage).

I would recommend watching some coding math videos before even starting on this.

Like here's one on collision detection.

3 hours ago, Rawland_Hustle said:

I've been looking at several physics engines for svg/js but couldn't find one that worked with existing svg elements or wasn't too complicated.

Physics engines don't work with elements. They just do the math. A physics engine can tell you at what coordinate something should be at, but it's up to you make sure whatever it is you are working with is drawn at that coordinate.

• 2
Share on other sites

3 hours ago, Rawland_Hustle said:

Should I use getBBox() to get the current x/y-values of the element when it's dropped and subtract the x/y-values of `coords[0]`?

Most likely you can set the target coordinates absolutely to where you don't need to calculate the values at all - you just let GSAP calculate that for you. If you use a .to() and have the correct values GSAP will automatically use the current position as the start of the tween.

3 hours ago, Rawland_Hustle said:

Do you recommend that I start all over with either Canvas or HTML?

I don't think that's necessary. But I can't say for sure as this isn't my project and I don't know what all requirements you have

Your most recent behavior on your pen looks good. Side note, going forward it might be good to fork your project when you are updating it so that the forum doesn't lose the original context.

Share on other sites

3 minutes ago, ZachSaucier said:

Most likely you can set the target coordinates absolutely to where you don't need to calculate the values at all - you just let GSAP calculate that for you. If you use a .to() and have the correct values GSAP will automatically use the current position as the start of the tween.

I have found the coordinates I want the coins to go to. The problem is that GSAP interprets `x: 0, y: 0` as the dragged coin's initial position. How can I tell GSAP that it should interpret `x: 0, y: 0` as top left of the viewport?

3 minutes ago, ZachSaucier said:

Side note, going forward it might be good to fork your project when you are updating it so that the forum doesn't lose the original context.

Got ya! ?

Share on other sites

17 hours ago, Rawland_Hustle said:

How can I tell GSAP that it should interpret `x: 0, y: 0` as top left of the viewport?

Either 1) actually place all elements at 0,0 and then move them to their "initial" position, 2) use SVG coordinates for the locations to go to (SVG coordinates by default are absolute to the top left of the SVG). Without a demo it's hard to say exactly.

Share on other sites

18 minutes ago, ZachSaucier said:

...use SVG coordinates for the locations to go to...

How do I use SVG coordinates for the locations to go to?

Thanks!

Share on other sites

1 minute ago, Rawland_Hustle said:

How do I use SVG coordinates for the locations to go to?

GSAP automatically uses SVG coordinates when you use the `x` and `y` properties. If you use other ones (like `cx` and `cy` for example) you probably need to use GSAP's builtin attribute plugin

Looking at your demo, it'd probably be best to restructure your SVG a little bit. Instead of positioning every single text and circle in reference to the global SVG space, I would position each parent `<g>` in reference to the global SVG space and then offset its text to be in the center of its circle (it'd be very small numbers). That way you can move the parent `<g>` as needed and not worry about the additional offset that the coins have. Does that make sense?

Share on other sites

2 minutes ago, ZachSaucier said:

GSAP automatically uses SVG coordinates when you use the `x` and `y` properties. If you use other ones (like `cx` and `cy` for example) you probably need to use GSAP's builtin attribute plugin

Looking at your demo, it'd probably be best to restructure your SVG a little bit. Instead of positioning every single text and circle in reference to the global SVG space, I would position each parent `<g>` in reference to the global SVG space and then offset its text to be in the center of its circle (it'd be very small numbers). That way you can move the parent `<g>` as needed and not worry about the additional offset that the coins have. Does that make sense?

What I'm trying to understand is how to define which reference to use for a certain element. In my current setup, each coin is a group with id's coin1, coin2, coin3 and so on. These "coin-groups" consists of a circle and a text element, which have their own coordinates in reference to the global SVG space.

How would you change my current setup below in order for the the coin-group to carry the coordinates in reference to the global SVG space, and the circle and text elements to carry the coordinates in reference to the their parent coin group?

```<g id="coin1" class="draggable coin" number="1" weight="2">
<circle cx="159" cy="60" r="10" fill="#FFFA8D" stroke="#000" id="svg_4"/>
<text x="159" y="65" text-anchor="middle" fill="black" id="svg_5">1</text>
</g>```

Share on other sites

7 minutes ago, Rawland_Hustle said:

How would you change my current setup below in order for the the coin-group to carry the coordinates in reference to the global SVG space, and the circle and text elements to carry the coordinates in reference to the their parent coin group?

```<g id="coin1" class="draggable coin" transform="translate(159, 60)" number="1" weight="2">
<circle r="10" fill="#FFFA8D" stroke="#000" id="svg_4"/>
<text y="5" text-anchor="middle" fill="black" id="svg_5">1</text>
</g>```

Share on other sites

4 hours ago, ZachSaucier said:
```
<g id="coin1" class="draggable coin" transform="translate(159, 60)" number="1" weight="2">
<circle r="10" fill="#FFFA8D" stroke="#000" id="svg_4"/>
<text y="5" text-anchor="middle" fill="black" id="svg_5">1</text>
</g>```

Thanks!!! Is there a way to move an element say, 30 px on the X axis, from it's current position?

I found the answer to this question: `{x: "+=30"}`.

Edited by Rawland_Hustle
Share on other sites

@ZachSaucier I have changed my setup to the one you suggested and I just noticed a challenge with it. Sometimes a coin needs to be moved back to it's original position, which now is `x:0, y:0` since I use `translate()` to position the coins. How can I move a coin back to the coordinates within the initial `translate()`? I.e. move coin1 back to `x:159, y:60`?

Share on other sites

2 hours ago, Rawland_Hustle said:

How can I move a coin back to the coordinates within the initial `translate()`? I.e. move coin1 back to `x:159, y:60`?

You can just use GSAP to `.set()` the x and y to 0: `gsap.set(".coin", {x: 0, y: 0})`

See the Pen rNNgXrx?editors=0010 by GreenSock (@GreenSock) on CodePen

Share on other sites

1 minute ago, ZachSaucier said:

You can just use GSAP to `.set()` the x and y to 0: `gsap.set(".coin", {x: 0, y: 0})`

Thanks! I'm almost done with this little project. Thanks alot for your help!

Share on other sites

You could also animate them back to 0 if you need to as well, 'course.

1 minute ago, Rawland_Hustle said:

Happy to help.

Share on other sites

10 minutes ago, ZachSaucier said:

You could also animate them back to 0 if you need to as well, 'course.

What do you mean?

Share on other sites

Just now, Rawland_Hustle said:

What do you mean?

You could use a `gsap.to(".coin", {x: 0, y: 0})` to animate it