Jump to content
Search Community

Is it possible to rotate a svg based on the position of a group or path child?

Robert Wildling test
Moderator Tag

Recommended Posts

I am trying to imitate a "sprocket turn". Using "transformOrigin: 50% 50%" won't work in this case, since there is an extending part with the pedal. But the center of the sprocket is a separate svg child element (called "sprocket-center" in the example).

 

Is there any way I can get the position of that sprocket center and use those values for transformation origin?

Also, the pedal itself has a center point around which it should turn. If there was a way to get its center point (svg child), would those value be affected by the rotation of the sprocket or would a proper grouping and nesting of svg children take care of a "constant" value that does not need to be reevaluated on each of the sprocket's turns?

Eventually, a physics engine might probably be a better approach... but I have no idea, if there is any that can deal with svg children relations. Do you happen to have any suggestions?

 

Thank you! :-)

See the Pen eYVmJdE by rowild (@rowild) on CodePen

Link to comment
Share on other sites

You could use .getBBox() and set the svgOrigin accordingly: 

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

 

But obviously that's animating the SVG <g> rather than the <svg> itself. The reason your svgOrigin wasn't working is because that's only for SVG elements (ones inside an <svg>). 

 

Another option is to use the MotionPathPlugin.convertCoordinate() method. 

 

You can animate the pedal the opposite direction too (as seen in the demo) but you'll obviously need to adjust the artwork of the crank to extend more.

 

Good luck!

  • Like 5
Link to comment
Share on other sites

I played around with what was suggested here and stuck with the getBB approach (couldn't find a way how to use the MotionPathPlugin to find the angle point for the sprocket).

 

Meanwhile I applied Draggable (new for me, and extremely exciting!).

 

Out of curiosity: I was wondering if there is a "onMoveEnd" functionality? "onDragEnd" is fired, when the mouse is released, but in this case I was playing with the idea to react to the sprocket's "onMoveEnd" motion and let the, e.g., pedal swing a bit (which does not necessarily mean that the user stopped dragging it; (s)he might just be waiting...)

 

The codepen in the first (updated) post would still be my "plaground" for reference...

Thank you! :-)

 

PS: I implemented a function in "onDrag" that updates the rotation of the pedal. That seems to pretty much seem to do what I need. I only wonder: is this the proper way to go in terms of performance?

Link to comment
Share on other sites

21 hours ago, Robert Wildling said:

Out of curiosity: I was wondering if there is a "onMoveEnd" functionality? "onDragEnd" is fired, when the mouse is released, but in this case I was playing with the idea to react to the sprocket's "onMoveEnd" motion and let the, e.g., pedal swing a bit (which does not necessarily mean that the user stopped dragging it; (s)he might just be waiting...)

You're creating a tween at the end for that, so you could just use an onComplete on that tween to know when it's done. 

 

Beware, though - you are creating a whole new tween EVERY time there's any movement and you didn't set overwrite: true (or "auto") so you're technically creating a bunch of conflicts. I'd recommend setting overwrite: true or "auto". 

 

21 hours ago, Robert Wildling said:

I implemented a function in "onDrag" that updates the rotation of the pedal. That seems to pretty much seem to do what I need. I only wonder: is this the proper way to go in terms of performance?

I'd personally recommend using the new gsap.quickTo() method instead for maximum performance. 

Link to comment
Share on other sites

Thanks for bringing "quickTo" to my attention! However, in my case it does not work. No idea, what I am missing...

I do not know what you mean by

"you could just use an onComplete on that tween to know when it's done".
When using "onMove", when is there an "onComplete" event fired?

 

I might have been unclear in my question: I am looking for an "onMoveEnd" option, one, that detects that the mouse is not moving (but it is still clicked).

 

UPDATE

You mention that I create a tween every time there is a movement. Is there a better way? 

 

 

Link to comment
Share on other sites

19 hours ago, Robert Wildling said:

UPDATE

You mention that I create a tween every time there is a movement. Is there a better way? 

 

That's why he was mentioning the quickTo. 😉

 

19 hours ago, Robert Wildling said:

I might have been unclear in my question: I am looking for an "onMoveEnd" option, one, that detects that the mouse is not moving (but it is still clicked).

 

Are you talking about for Draggable? There isn't anything like that, so that's what you'd have to set up yourself. Like how do you determine if something isn't moving? You'd have to choose what that time that should be and maybe do like a custom debounce function.

 

  • Like 1
Link to comment
Share on other sites

20 hours ago, Robert Wildling said:

Thanks for bringing "quickTo" to my attention! However, in my case it does not work. No idea, what I am missing...

I suspect you're using it wrong, but it's impossible to say without seeing a minimal demo

 

20 hours ago, Robert Wildling said:

I do not know what you mean by

"you could just use an onComplete on that tween to know when it's done".
When using "onMove", when is there an "onComplete" event fired?

I thought you were talking about "move" as in "tween movement" (in which case an onComplete on that tween is what you'd need), but it now sounds like you meant mouse movement in which case Blake is correct. 

Link to comment
Share on other sites

@OSUblake @GreenSock Thanks again for your feedbacks!

 

Sorry for not posting the link to the demo again; it is still in the first post (which I adapted):

 

There I added the "quickTo", which I cannot get to work.

 

And yes, when talking about "onMoveEnd", I actually mean Draggable – sorry for being so unclear!

 

About my performance question: since I cannot get "quickTo" to work, I define the tween that is frequently called in "onDrag" as extra timeline and invoke "resume()" on it (I now also can call this tween from "onDragEnd"):

 

function pedalOnDragReaction(rotation) {

  const tl = gsap.timeline({ paused: true })

  tl.to(pedal, { duration: 0.5, rotation: -rotation, overwrite: true })

  return tl

}

 

const handleOnDrag = (rotation) => {

  pedalOnDragReaction(rotation).resume()}

}


Draggable.create(bikePedal, {
  type: "rotation",
  onDrag: function () {
    handleOnDrag(parseInt(this.rotation % 360, 10));
  },
  onDrag: function () {
    handleOnDrag(parseInt(this.rotation % 360, 10));
  },

  [...]
});

 

See the Pen bGLpbQe?editors=1111 by rowild (@rowild) on CodePen

 

It seems to work - but is it an improvement performance-wise?

Link to comment
Share on other sites

10 hours ago, Robert Wildling said:

There I added the "quickTo", which I cannot get to work.

Yeah, you're using it wrong - I'd encourage you to read the docs and look at the demo there. It's not just a simple replacement for gsap.to(). It returns a function that you reuse to set a singular value. 

 

// INCORRECT
const handleOnDrag = (rotation) => {
  gsap.quickTo(pedal, {
    duration: 0.5,
    rotate: -rotation,
    ease: "power1.out"
  });
}

// CORRECT
const rotateTo = gsap.quickTo(pedal, "rotation");
const handleOnDrag = rotation => rotateTo(-rotation);

 

10 hours ago, Robert Wildling said:

And yes, when talking about "onMoveEnd", I actually mean Draggable – sorry for being so unclear!

This is still fuzzy for me - what exactly are you wanting the Draggable to do that it's not doing? There's an onDragEnd which you said is not what you want. If it's not drag-related, why would you say "I actually mean Draggable"? 

  • Like 1
Link to comment
Share on other sites

@GreenSock Thank you very much for the "quickTo" example! (Indeed, I thought it was just meant as a replacement of ".to"... will look into the docu now.)

Draggable's "onDragEnd" only fires, when the mouse button is released. 

What I am asking is, if there is a way to detect a "onMoveEnd" without releasing the mouse button. 

 

In my example this would mean: 

- drag the sprocket

- move the sprocket

- stop moving the sprocket, but do not release the button (because I might continue moving after a while)

-- if that happens, invoke a function that causes the pedal to move to it's proper position

 

The reason, why a user might not release the mouse button, has to do with the (not yet implemented) idea that at various positions there will be some kind of reactions (e.g. some item, that is only a dot, but expands, when the sprocket is at a specific angle...)

 

But OSUBlake already mentioned that I have to be implement some kind of check on my own. 

May I ask you for your advice on the "timeline" solution? 

Link to comment
Share on other sites

7 hours ago, Robert Wildling said:

What I am asking is, if there is a way to detect a "onMoveEnd" without releasing the mouse button. 

One idea would be: 

let onStopDC = gsap.delayedCall(0.2, () => console.log("STOPPED!")).pause();

Draggable.create({
  ...
  onDrag: () => onStopDC.restart(true)
});

Basically it's a delayedCall that constantly gets restarted every time there's a drag event, so the only time it gets called is if 0.2 seconds elapses WITHOUT an onDrag. Obviously adjust the number to whatever you want. 

 

7 hours ago, Robert Wildling said:

May I ask you for your advice on the "timeline" solution? 

 

18 hours ago, Robert Wildling said:

About my performance question: since I cannot get "quickTo" to work, I define the tween that is frequently called in "onDrag" as extra timeline and invoke "resume()" on it (I now also can call this tween from "onDragEnd"):

 

function pedalOnDragReaction(rotation) {

  const tl = gsap.timeline({ paused: true })

  tl.to(pedal, { duration: 0.5, rotation: -rotation, overwrite: true })

  return tl

}

 

const handleOnDrag = (rotation) => {

  pedalOnDragReaction(rotation).resume()}

}

That is definitely not a good solution :)

 

You're constantly recreating a whole new timeline and tween on every single drag event, and then you're needlessly calling .resume() on it. Timelines/tweens always start playing by default. Also, it's wasteful to create a timeline when you're only populating it with one tween. Might as well just create the standalone tween. 

 

The gsap.quickTo() solution will certainly perform better but honestly you probably would never notice a real-world difference anyway because the JS usually executes so fast. 

  • Like 1
Link to comment
Share on other sites

1 hour ago, Robert Wildling said:

Excellent! Thank you very much, @GreenSock, this is incredibly helpful for me! 

Glad to hear it. 

 

1 hour ago, Robert Wildling said:

(I think you are not `@ J a c k`, right? Sorry for that. I'll fix that in the previous posts.)

Yep, my name is Jack but in these forums my account is @GreenSock :) No big deal. I knew who you meant. 

 

Good luck!

  • Like 1
Link to comment
Share on other sites

@Greensock I started experimenting with "snap", which I couldn' get to work yet ("liveSnap" works, though).

 

This is a very minor issue, but since I didn't know, where to post it, I do it right here: While reading through the api:

https://greensock.com/docs/v3/Plugins/Draggable

I realised that there is no "snap" under "Config object properties" (main text). Also, "inertia" is not listed alphabetically. The same is true, when clicking in "Draggable.create", where I can find "bounds", "edgeResistence", "liveSnap" etc, but no "snap" (and inertia before "t"). 

 

To y understanding "snap" is only available with "inertia", right? If so, shouldn't "onThrowComplete" and "onThrowUpdate" also go into the "inertia" block ONLY? (Currently they are on both "levels"). 

 

(The anchor link on "See the snapping section" does not work either.)

  • Thanks 1
Link to comment
Share on other sites

14 hours ago, Robert Wildling said:

To y understanding "snap" is only available with "inertia", right?

Yep, that's correct. It's only for when inertia is enabled. 

 

Are you saying that you enabled inertia, loaded InertiaPlugin, and you still couldn't get snap to work? If that's the case, please provide a minimal demo and we'd be happy to take a peek. 

 

I've made some improvements to the docs according to your suggestions. Thanks!

Link to comment
Share on other sites

@Greensock No, I have not installed inertia yet. And my last post was indeed only a question concerning the docu.

 

I looked at your changes in the docu – and I must admit I am still a bit confused. "snap" (like "onThrowUpdate" and "onThrowComplete") is now on the main list level, but other functions, that are only available with inertia, are only listed within the "inertia" list level. This seems a bit arbitrary. Wouldn't it make more sense to either put **all** functions on the main level OR **group all inertia-related functions** within the inertia list level? Or maybe even put the inertia API below the main "Config Properties" as a separate list?

 

Just thinking out loud... personally I would prefer a separate "Inertia Config Properties" section below the "Main Config Properties" that has a comment like "Only available with inertia"; ideally with a jump menu above the "Main Config Properties". Or in the "Main Config Properties" "inertia" could be mentioned, but the text would say sth like "See below" or maybe offer a jump link...

 

What do you think?

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...