Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tween jumps (jitters) to the final value at the end of animation #116

Open
KeeleyMoore opened this issue May 3, 2014 · 27 comments · May be fixed by #619
Open

tween jumps (jitters) to the final value at the end of animation #116

KeeleyMoore opened this issue May 3, 2014 · 27 comments · May be fixed by #619
Labels

Comments

@KeeleyMoore
Copy link

Hi, thanks for this great lib its really useful!

But i've fallen into some trouble that i cant figure out. Within my three.js scene i have camera and avatar objects in a container object that is moved with my controller. On collision with another object in my scene the camera is meant to tween back and up slightly but instead of any animation it just jumps to the finished position.

This only happens while the avatar is being moved, if i initiate the tween straight away when the avatar isn't being moved the animation runs fine.

Is anybody aware of what might be causing this? and a possible fix? I'm truly stumped

This is the tween setup code i'm using incase its just a simple setup issue i've missed:

    var camPosition = { x : 0, y : 5, z : 50 };
    var camTarget1 = { x : 0, y : 10, z : 75};

    tweenCamToOrig = new TWEEN.Tween(camTarget1)
    .to(camPosition, 2000)
    .easing( TWEEN.Easing.Back.InOut )
    .onUpdate(function(){
        camera.position.x = camPosition.x;
        camera.position.y = camPosition.y;
        camera.position.z = camPosition.z;
    })
    .onComplete(function(){
    });
@sole
Copy link
Member

sole commented May 4, 2014

It's hard to say what's going on with just a bit of code which has nothing to do with the moment where you actually start the tween. You should provide some working online example (with codepen or jsfiddle or similar) so I can see it happening.

If anything, I'd guess you're trying to tween to absolute values but what you want is a relative tween to the camera position when it collides with something. Try using something like camTarget = { x: "+0", y: "+10", z: "+75" } ?

@KeeleyMoore
Copy link
Author

Ah okay sorry, i was hoping it might of been a regular or known issue.
Thanks for your suggestion, I implemented it but it made no change, I've created a much simpler version of my game and made a codePen out of it: http://codepen.io/Keeley/pen/Kpdyl

However looking in the chrome javascript console it says TWEEN is undefined, but nothing has change from my local file so it should work. I tried various different host for tween.js as well but it still doesn't recognise TWEEN.

This has puzzled me even more, i cant see any reason for it not to work within the pen. If you wouldnt mind taking a look for me i would appreciate it immensely.

If you manage to get it to recognise Tween and it runs, when you move the avatar into an object in front of you you'll see the tween jump to its finished position.
(its using the Three.js fly controller so WASD and mouse to look)

I also highlighted the tween.js code with large ////////////Comments\\\ so they will be easy to spot.

Thanks again!

@KeeleyMoore
Copy link
Author

I thought it unfair, unlikely and a bit of a waste of time for me to ask you to debug codepen, so i found out how to create a Git repo and have added my working simplified code to it.

It should be much easier to read on there heres the repo: https://github.com/KeeleyMoore/Conquer-the-Cosmos-simplified

@sole
Copy link
Member

sole commented May 4, 2014

The reason for asking you to use codepen or similar is so that I don't have to download/clone the repository then open the folder in my local drive, see what the structure is, figure out what's the error and send you a Pull Request - with codepen I can just save a new version and that should be it!

That aside, your code will never work because you're not including any of the libraries upon which your code depends :-P

So a couple of <script> tags with the right source should be added ;-)

@KeeleyMoore
Copy link
Author

Ah my apologies, I'm fairly new to this style of coding and haven't really asked for help before!

I also thought that if i was to use the "add external resource" link in codepen that it would of worked as a source tag equivalent.

Anyhow turns out that the reason it didn't recognise TWEEN in the first place was because i was stupidly linking to a create.js's tweenJs. I just assumed when looking for a hosted version that it was the same thing, my bad.

I hosted my own version of tween.js just so i was positive its was the correct file and everything works now :P
The codepen URL: http://codepen.io/Keeley/pen/Kpdyl

Just hold down the left mouse button and guide the avatar until you collide with another sphere and you'll see the camera jumps to the target position

@sole
Copy link
Member

sole commented May 5, 2014

Hey

Sorry I don't have the time/patience to make it collide but make sure you use "+10" and not just +10 when specifying the desired values to tween to. The quotes matter in this particular case. I'm afraid I still haven't got to document this yet, but have a look at the relative values example to see the syntax in use: https://github.com/sole/tween.js/blob/dev/examples/09_relative_values.html

@KeeleyMoore
Copy link
Author

Hey again,

i implemented your suggestion but had no luck, i also studied the link your proved but that didnt help either. I have completely stripped the codePen down so now the camera tween is called on a 2 second delay from initialisation. http://codepen.io/Keeley/pen/Kpdyl

Im pretty sure that my error is with the code inside my Update function but i cant figure out the format i'm meant to use.

Sorry to keep on bugging you but i'm desperate!

@KeeleyMoore
Copy link
Author

Im going to start off again by apologise for persistently bugging you. But i'm sure you'll be glad to hear that it will stop now :P

I managed to find a fix, once i knew the problem was in the update function it was easier to search for a solution. I stumbled upon this for anyone who may see this and have the same issue: http://www.96methods.com/2012/01/three-js-moving-objects/

also here is the working code's setUp function:

function tweenSetUp(){      
    var camPosition = { x : 0, y : 5, z : 50 };
    var camTarget1 = { x : 0, y : 10, z : 75};
    var camTarget2 = { x : 0, y : 100, z : 1000};

    var actualXpos= 0;
    var actualYpos = 5;
    var actualZpos= 50;
    tweenCam1 = new TWEEN.Tween(camPosition)
        .to(camTarget1, 2000)
        .easing( TWEEN.Easing.Quadratic.InOut )
        .onUpdate(function(){
              // Calculate the difference between current frame number and where we want to be:
            var differenceX = Math.abs(camPosition.x - actualXpos);
            actualXpos = camPosition.x;
            var differenceY = Math.abs(camPosition.y - actualYpos);
            actualYpos = camPosition.y;
            var differenceZ = Math.abs(camPosition.z - actualZpos);
            actualZpos = camPosition.z;
            // Moving in -Z direction:
            camera.translateX( +differenceX);
            camera.translateY( +differenceY);
            camera.translateZ( +differenceZ);
        });
}

Thanks again for your help, it's much appreciated, along with the effort you've put into this great lib :)

@sole
Copy link
Member

sole commented May 5, 2014

Ah, that would make sense :-)

Glad you found where the error was and I'm thanking you for posting the solution here so more people can take advantage of it!

@rbary
Copy link

rbary commented Mar 23, 2015

Thanks a lot KeeleyMoore this post helped me !!

@sole
Copy link
Member

sole commented Mar 25, 2015

Perhaps we need to make an example / mention this in the docs!
@rbary what was your issue exactly and how did this issue or post link help you? I'd like to clarify whatever is it that is not clear! thanks!

@rbary
Copy link

rbary commented Mar 26, 2015

Hi my issue was perspective camera (three) animation;

https://github.com/NephtysOrg/projet_sia/other_dev/js/app/game.js

Game.prototype.cameraTranslate = function(){
    var actualXpos =this.current_camera.position.x;
    var actualYpos =this.current_camera.position.y;
    var actualZpos =this.current_camera.position.z;
    console.log(this.current_camera.position);
    var differenceX = Math.abs(this.current_camera.position.x - actualXpos);
    actualXpos = this.current_camera.position.x ;
    var differenceY = Math.abs(this.current_camera.position.y - actualYpos);
    actualYpos = this.current_camera.position.y;
    var differenceZ = Math.abs(this.current_camera.position.z - actualZpos);
    actualZpos = this.current_camera.position.z;

    this.current_camera.position.x += differenceX;
    this.current_camera.position.y += differenceY;
    this.current_camera.position.z += differenceZ;    
};
Game.prototype.cameraTransition = function (position,look) {
    console.log("camera transition function using tweenjs");
    var tweenCam = new TWEEN.Tween(this.current_camera.position).to({
        x:position.x,
        y:position.y,
        z:position.z}, 600)
    .easing(TWEEN.Easing.Sinusoidal.InOut)
    .onUpdate(this.cameraTranslate())
    .start();
};

@rbary
Copy link

rbary commented Mar 28, 2015

Finaly i found that it isn't necessary to call a translate function in
.onUpdate; this code works too:

Game.prototype.cameraTransition = function(position, look) {
    var that = this;
        var tweenCam = new TWEEN.Tween(that.current_camera.position).to({
            x: position.x,
            y: position.y,
            z: position.z}, 3000)
            .easing(TWEEN.Easing.Linear.None)
            .onUpdate(function() {
                that.current_camera.lookAt(look);
            })
            .onComplete(function() {
                //that.current_camera.rotation.y = 180 * Math.PI / 180;
                //that.current_camera.rotation.z = -45 * Math.PI /180;
            })
            .start();

            console.log("tween to player pos");
};

@trusktr
Copy link
Member

trusktr commented Mar 17, 2018

This seems to be an issue with all tweens. The issue become apparent when a small change in the tweened number (the small amount that the number should change in one tick) translates to a visibly large rendering difference.

What I mean is, if suppose we change the rotation of a circle with a texture on it. If the circle has a large radius, then a small change in the rotation of the circle (by Tween.js) can make the jumping become obvious.

TweenMax has the exact same problem (see the live example there): https://greensock.com/forums/topic/14874-long-animations-jumping-at-the-startend-when-using-easing/

In many cases, this is not noticeable because if the small change in number value corresponds to a small visual difference, then you can't really see it with your eye.

But in that example TweenMax, the jump is obvious.

Here's some numbers of a tween that is slowing down, generated by Tween.js, and we can see the jump at the end:

10.046829364239343
10.042912961057219
10.039487976809298
10.036269246798945
10.033420811990638
10.030853057216234
10

This is an Exponential.Out you see, the last few numbers. There's clearly a jump at the last value that is out of proportion compared to the distance between the other values.

If this tween is the position of a pixel, you may definitely not notice it. But it this tween is tweening across a scaled distance, or rotating a huge object, you can definitely see it.


What causes this? Is it some sort of floating point error? Hmmmmm.......

@trusktr
Copy link
Member

trusktr commented Mar 17, 2018

Re-opening, needs to be solved.

@trusktr trusktr reopened this Mar 17, 2018
@trusktr
Copy link
Member

trusktr commented Mar 17, 2018

@trusktr
Copy link
Member

trusktr commented Mar 17, 2018

Alright, so, Popmotion doesn't have the issue. Here's two pens.

The first pen uses Popmotion with a Tween.js easing curve, and the jump is visible:

https://codepen.io/anon/pen/OvROWN

The second pen uses a Popmotion easing curve. This time, the easing curve is nothing fancy the like the "Penner" equations here, just k ** power here for the exponential curve, and there's no jump:

https://codepen.io/anon/pen/bvwYww

@trusktr trusktr changed the title Tween jumping to finish position tween jumps (jitters) to the final value at the end of animation Mar 17, 2018
@trusktr trusktr added the Bug label Mar 17, 2018
@mikebolt
Copy link
Contributor

The math is actually "wrong", it's off by .05%

https://www.wolframalpha.com/input/?i=0.5+*+(-(2)%5E(-10+*+(k+-+1))+%2B+2)+for+k+%3D+2

Likewise there is a jump at the beginning. One solution is to stretch the function about 0.5 so that the extreme values are exactly at 0 and 1:

g(x) = (f(x) - 0.5) * (0.5 / 0.49951171875) + 0.5

@trusktr
Copy link
Member

trusktr commented Mar 19, 2018

A quick solution which worked in a project I just finished, was to just pass in other easing curves. For example, using Popmotion:

import {easing} from 'popmotion'
import {Tween} from 'tween.js'

// similar to TWEEN.Easing.Exponential.InOut
const curve = popmotion.easing.createMirroredEasing(
    popmotion.easing.createExpoIn(4)
),

new Tween(someObject)
  .to(...)
  .easing( curve )
  .start()

Under the hood, Popmotion's expo function is literally just k ** power where power is 4 in the above example. Are there any disadvantages with this? I am getting nice results with no jitter.

@MasatoMakino
Copy link
Contributor

I did some research on jitter in Exponential function.

Cause of jump

Easing.Exponential function, unlike other easing functions, does not interpolate from 0 to 1.

return amount === 0 ? 0 : Math.pow(1024, amount - 1)

The core of the Exponential function's processing is in this line.

Math.pow(1024, amount - 1)

The interpolation process is here, where amount is a value between 0 and 1.

amount : 0 = 1024^-1 = 1/1024
amount : 1 = 1024^0 = 1

If the amount is 0, the function will not return 0, it will return 1/1024 ( = 0.0009765625 ). This is the cause of the jumps.

https://www.desmos.com/calculator/xbgt04gh4v

If you zoom in on the area around 0 in the graph, you can see a jump of 1/1024.

Difference from popmotion

The expo function of popmotion is implemented as follows.

https://github.com/Popmotion/popmotion/blob/a1903c808757b31d26d876dc2d8d3dea2d131c12/packages/popmotion/src/easing/utils.ts#L14

The interpolation process is as follows

Math.pow(amount, power)

No jumps will occur in this process. If you set the power to about 5.8, you will get results similar to Exponential. But the results aren't exactly the same.

Proposal

Exponential and linear interpolation can be used to adjust for jumps.

Math.pow(1024, amount - 1) - 1 / 1024 * ( 1 - amount )

This method breaks Exponential's compatibility and slightly slows down its performance. I cannot decide if this is a good idea or not.

I hope this information helps you.
Thanks.

@trusktr
Copy link
Member

trusktr commented Apr 19, 2021

@MasatoMakino Hello! Thanks for looking into this. I think proper behavior should come before any optimization, so if you would like to open a pull request with the change you think is best, it would be greatly appreciated.

This method breaks Exponential's compatibility

Can you expand on that?

I really like popmotion's ability to create arbitrary curves. Maybe we can adopt that approach.

MasatoMakino added a commit to MasatoMakino/tween.js that referenced this issue Apr 22, 2021
MasatoMakino added a commit to MasatoMakino/tween.js that referenced this issue Apr 22, 2021
@MasatoMakino
Copy link
Contributor

@trusktr
Thanks for your advice.

I really like popmotion's ability to create arbitrary curves. Maybe we can adopt that approach.

I also think that this approach is the best solution for this issue. I have made a PR that adopts this approach.

This method breaks Exponential's compatibility

Can you expand on that?

I add an explanation to this point.

The easing function is repeatable, always returning the same value when given the same value. But if we patch it, this function returns slightly different values for all amount. This difference will affect, for example, animation projects that use intermediate values of easing.

This function is inherited from Robert Penner's Easing Functions, which is the base of jquery-easing. I think the reason it hasn't been a problem in the past is because jitter has been acceptable to users.
( The original easing function may have been even older, but I was unable to research it. )

For this issue, I think patching the exponential function does not have the right balance of advantages and disadvantages.

@trusktr
Copy link
Member

trusktr commented Aug 2, 2021

@MasatoMakino I came up with an alternative that is very close to Exponential, but touches 0,0:

https://www.desmos.com/calculator/pioplwo3zq

I'll make a PR later, time for bed. :)

trusktr added a commit that referenced this issue Aug 2, 2021
Given a value of 300, this is very close Easing.Exponential, but it will hit 0 when amount is 0, therefore being an alternative to Exponential to avoid the jump issue mentioned in #116.
@trusktr trusktr linked a pull request Aug 2, 2021 that will close this issue
2 tasks
@trusktr
Copy link
Member

trusktr commented Aug 2, 2021

I made the PR anyway. 😄

@trusktr
Copy link
Member

trusktr commented Aug 2, 2021

#619 provides an alternative for Exponential. Need to verify if any other easings still need to a fix.

@Zaniyar
Copy link

Zaniyar commented Jun 3, 2022

so this is still not solved yet right?
I get the same behavior with popmotion.. same jumpin in the end; how can this be solved?

import { createMirroredEasing,  createExpoIn  } from "@popmotion/easing";
const expoIn = createExpoIn(5.8)
const curve = createMirroredEasing(expoIn)

new TWEEN.Tween(camera.position)
			.to(
				{
					x: target.position.x,
					y: 2,
					z: target.position.z + 2
				},
				1500
			)
			.start();

@trusktr
Copy link
Member

trusktr commented Jun 27, 2022

@Zaniyar in your code you made a curve variable and you didn't use it anywhere.

@trusktr trusktr linked a pull request Apr 23, 2023 that will close this issue
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants