Jump to content
Search Community

Shake object in place

stot test
Moderator Tag

Go to solution Solved by Carl,

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I wonder if there is a way to shake an object in place around its current position. I tried RoughEase and tweening from x,y to x,y with the same value. But the shaking will only be visible when they differ. Is there a simpler way than use an onUpdate callback?

 

Thanks

Link to comment
Share on other sites

Long time reader but first time posting on this forum, so please excuse me if I break any written or unwritten rules. :)

IMHO, I don't think you need to use TweenMax for this because you can pretty much get the same effect using the requestAnimationFrame API i.e. if I have been able to understand your question correctly in the first place.

Take a look at this example that I just created and try playing with the values.
 
HTML:

<div> </div>

CSS:

div {
    position:absolute;
    top:50%;
    left:50%;
    transform: translate(-50%, -50%);
    background:#f00;
    width:80px;
    height:80px;
}

JavaScript:

window.requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })();

var myDIV = document.querySelector('div');
var interval = 2,
    iterator = 0;
var translateMin = -48,
    translateMax = -52,
    scaleMin = 0.98,
    scaleMax = 1.02,
    rotateMin = -4,
    rotateMax = 4;

function rand(min, max) {
    return min + (Math.random() * (max - min));
};

function roundDecimal(value, place) {
    return Math.round(value * Math.pow(10, place)) / Math.pow(10, place);
};

function randInt(min, max) {
    return Math.floor(Math.random() * (1 + max - min) + min);
};

function render() {
    iterator+=1;
    requestAnimFrame(render);
    if (iterator > interval) {
        myDIV.style.transform = 'translate(' + randInt(translateMin, translateMax) + '%,' + randInt(translateMin, translateMax) + '%) scale(' + roundDecimal(rand(scaleMin, scaleMax), 2) + ') rotate(' + randInt(rotateMin, rotateMax) + 'deg)';
        iterator=0;
    }
}
requestAnimFrame(render);

 
Notice that the available transform-able properties to play with are scale(), translate(), rotate() and their is an interval variable to control the frequency of the updates, set it to zero to render at full 60 fps.

Hope it helps in some way.

 

T

Link to comment
Share on other sites

Hi Tahir and welcome to the GreenSock forums,

 

Thanks for posting your alternate version, it definitely has some nice features.

I imagine you could easily find a some recent posts on these forums and post a suitable vanilla js replacement.

However, any time you decide to roll your own RAF solution you are going to sacrifice:

  • the ability to easily play, pause, resume, reverse any animation at any time
  • the ability to progressively accelerate or decelerate any animation while it is running
  • the ability to nest any animation into a timeline (for sequencing)
  • the ability to animate SVG child elements (<rect>, <g>, <circle>, <ellipse>, etc) as easily as DOM elements 
  • the ability to apply numerous callbacks to any animation (onStart, onUpdate, onComplete, onReverseComplete)

among countless other conveniences of an animation engine as robust as GSAP.

 

So let's say you have perfected your shake code and then the client says, "cool, I love this effect, lets apply it to our next project where I need:"

  1. Two SVG <rect> elements that both scale up from scale:0 (with a transform-origin in their center) in a staggered fashion
  2. One second after the second box scales up it should shake
  3. repeat the above sequence two times with a delay of 0.5 seconds between iterations

 

With GSAP it might take 5 minutes with only 3 new lines of code:

var tl = new TimelineMax({repeat:2, repeatDelay:0.5})
tl.staggerFrom('rect', 0.2, {scale:0, transformOrigin:"50% 50%"}, 0.2) 
  .fromTo('#r2', 0.15, {x:"-=20"},{x:"+=40",repeat:5,yoyo:true,ease:Sine.easeInOut, immediateRender:false}, 1)
  .to('#r2', 0.05, {x:0})
 

Also, another great GSAP solution for a jittery shake is to use RoughEase which has lots of config options.

demo: http://codepen.io/GreenSock/pen/hznvq

 

So even though the user's request did not demand all the conveniences of GSAP, and your solution adequately performed a very nice shake, I just have a very difficult time seeing how any alternate solution is worth the time investment. Once SVGs are involved most other solutions are going to come to a grinding halt (literally in IE). Check out our recent SVG Challenge: http://codepen.io/GreenSock/pen/dooWWp and stay tuned for a big article loaded with tips for animating SVG with GSAP. 

 

Also, I know you weren't intending to take anything away from GSAP with your post. I just wanted to lay out this case for GSAP as I imagine it might help others understand the benefits. 

 

 

Thanks again for posting. We welcome your participation and appreciate all your support.

  • Like 3
Link to comment
Share on other sites

Not the kind of start I was looking for :)

 

Completely understand that Carl. No way was I trying to take anything away from GSAP. I owe many 'this looks fantastic'-like moments to this great tool over the years.

 

The intention, for a simple requirement with such limited scope as this one, was to propose a solution not dependant on external tools i.e. if this is something stot is fine with in the first place. If they already are using TweenMax then by all means hand all your animation needs over to it. That is all.

Now, let me try this solution again. RoughEase is definitely a game-changer and it brings wealth of options to play with, which is what I have tried to do with this modified version of the same thing using TweenMax.

 

Here is the updated code of the same:

 

JavaScript:

var myDIV = document.querySelector('div');
var translateMin = -40,
    translateMax = -60,
    scaleMin = .9,
    scaleMax = 1.1,
    rotateMin = -10,
    rotateMax = 10;
var strengthMin = 2,
    strengthMax = 4,
    pointsMin = 40,
    pointsMax = 100;
var tween = null,
    duration = .2;
var ease = RoughEase.ease.config({
    template: Power0.easeNone,
    strength: randInt(strengthMin, strengthMax),
    points: randInt(pointsMin, pointsMax),
    taper: 'none',
    randomize: true
}); //set taper to 'in' and randomize to 'false'
var tempObject = {};
tempObject.posX = tempObject.posY = -50;
tempObject.destX = randInt(translateMin, translateMax);
tempObject.destY = randInt(translateMin, translateMax);
tempObject.scale = 1;
tempObject.destScale = roundDecimal(rand(scaleMin, scaleMax), 2);
tempObject.rotation = 0;
tempObject.destRotation = randInt(rotateMin, rotateMax);
tween = new TweenMax(tempObject, duration, {
    posX: tempObject.destX,
    posY: tempObject.destY,
    scale: tempObject.destScale,
    rotation: tempObject.destRotation,
    onUpdate: onUpdate,
    onRepeat: onRepeat,
    repeat: -1,
    yoyo: true,
    ease: ease
});


function rand(min, max) {
    return min + (Math.random() * (max - min));
}


function roundDecimal(value, place) {
    return Math.round(value * Math.pow(10, place)) / Math.pow(10, place);
}


function randInt(min, max) {
    return Math.floor(Math.random() * (1 + max - min) + min);
}


function onRepeat() {
    //ease=RoughEase.ease.config({template:Power0.easeNone,strength:randInt(strengthMin,strengthMax),points:randInt(pointsMin,pointsMax)});
    tempObject.destX = randInt(translateMin, translateMax);
    tempObject.destY = randInt(translateMin, translateMax);
    tempObject.destScale = roundDecimal(rand(scaleMin, scaleMax), 2);
    tempObject.destRotation = randInt(rotateMin, rotateMax);
    tween.updateTo({
        posX: tempObject.destX,
        posY: tempObject.destY,
        scale: tempObject.destScale,
        rotation: tempObject.destRotation
    }, true);
}


function onUpdate() {
    tempObject.posX = roundDecimal(tempObject.posX, 2);
    tempObject.posY = roundDecimal(tempObject.posY, 2);
    tempObject.scale = roundDecimal(tempObject.scale, 2);
    myDIV.style.transform = 'translate(' + tempObject.posX + '%,' + tempObject.posY + '%) scale(' + tempObject.scale + ') rotate(' + tempObject.rotation + 'deg)';
}

I have used RoughEase before and I am always blown away by the randomness that it brings to our animations (or a controlled randomness thanks to taper and randomize parameters). 

 

Would love to hear comments on the approach used here by the way? I am still learning.

 

T

Link to comment
Share on other sites

Hi Tahir Ahmed  :)

 

don't forget that we don't speak/think about a webpage just with ONE simple shaking Animation/Element !!!... 

 

pls check this out ( your demo with less than 4 line code ) : 

See the Pen vOLoww by MAW (@MAW) on CodePen

 

by the way , you lose performance issues ,

 

pls check your demo and our demo in chrome > developer tools > Rendering > Show paint rectangles !!  :geek:

  • Like 1
Link to comment
Share on other sites

Hey Diaco,

 

Totally in agreement about the animations on a webpage part. As for your example, this is indeed a clean and elegant solution. AFAIK the fewer lines of code, the better in most cases, right? right?

 

(I do not know why didn't I just directly tween the DIV itself like you are instead of a pseudo tempObject. Duh! silly me.) 

 

However, I am curious to know if recursion, which is what your solution essentially is if I understand it correctly, is something we should be aiming at when we need repetitive animations like those. Control of the tween is lost perhaps in such a case? Plus there is a repeat:-1 for such cases precisely. Open to hearing ideas / suggestions on this.

 

As for the Paint Rectangles, I think my example can modified to add translate3d into it instead of the simple translate and voila! No more re-drawing, takes it to its own GPU layer if I am not wrong. Does TweenMax automatically take it to GPU? Is this force3D in action without actually being apparent in the code?

 

As I said, from a pure implementation perspective, your solution seems elegant. Thanks a lot for your feedback man.

 

T

Link to comment
Share on other sites

- about force3D :  GSAP version 1.15.0+  set ( force3D : "auto" ) as default for all tweens .

 

- set repeat:-1 : will repeat same animation for infinity term

 

- you can kill a delayedCall with this : TweenMax.killDelayedCallsTo()

Link to comment
Share on other sites

Hi Guys,

 

thanks for all your answers. My thoughts go more to very short solutions which are easy to apply to different circumstances. Maybe I will write a plugin based on the "shaking code" provided which accepts a bunch of parameters (amplitude of x,y,z,rotation, scaleX, scaleY,... and duration of a shake).

 

I use canvas and want to shake bitmaps after a "hit" in a game.

 

 

stot

Link to comment
Share on other sites

  • 2 months later...
  • 2 years later...

I like Carl's solution the best. The only improvement I would make is to add the following in the shake:

 

transformOrigin: "center center"

 

It makes it more balanced, especially for the rectangular shape I'm using.

 

That is, in his code example, change:

 

for(var i = 0; i < shakes; i++){
     tl.to(element, speed ,{x:initProps.x + R(-4,4), y:initProps.y + R(-2,2), rotation:initProps.rotation + R(-5,5)})
}

 

to:

 

for(var i = 0; i < shakes; i++){
     tl.to(element, speed ,{x:initProps.x + R(-4,4), y:initProps.y + R(-2,2), rotation:initProps.rotation + R(-5,5), transformOrigin: "center center"})
}

 

 

  • Like 1
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...