2

I have a section of code which is supposed to move this image ( id = 'background' ), which I have downloaded locally, and is quite large. It is supposed to move when I hover over top of a certain div(s). This then changes the opacity CSS value, which in turn is detected by the js, which then makes the image move. The js code looks like this:

setInterval(function(){ var element = document.getElementById('background'), style = window.getComputedStyle(element), left = style.getPropertyValue('left'); left = left.replace(/px/g, "") left = parseInt(left,10) if(getOpacity('rightbar') == .5){ document.getElementById('background').style.left = left - 8 + 'px' } if(getOpacity('leftbar') == .5){ document.getElementById('background').style.left = left + 8 + 'px' } },10) 

The getOpacity(idName) function looks like this:

function getOpacity(param){ var element = document.getElementById(param), style = window.getComputedStyle(element), opacity = style.getPropertyValue('opacity'); return opacity } 

So the problem is that, no matter what movement values or setInteveral time I use, the animation always makes out to be laggy. Is there a way to make this smooth with vanilla js, or better yet, to scrap the opacity detection and do it all with CSS?

It works fine when I put the above code in a fiddle, but when it actually runs full browser (on my personal chrome window), it looks like this.

Note: I am running this full browser window on a 4k monitor, is this just too much for chrome to handle?

4
  • 3
    Don't invent the wheel again. use tweenlite. Commented Nov 16, 2015 at 0:23
  • 2
    ... or velocity.js - at the very least, use requestAnimationFrame() on browsers that support it. Commented Nov 16, 2015 at 0:23
  • The question was asked specifically for vanilla javascript. If the OP does not want to use a library like tweenlite (which is not open source, by the way), why not answer his question? Commented Nov 16, 2015 at 0:54
  • Yes, the whole point is to reinvent the wheel. I'm using it as an exercise and a school project (don't worry, it is perfectly fine to use code tips from this site, my teacher knows). Commented Nov 16, 2015 at 1:02

2 Answers 2

1

1. Use requestAnimationFrame instead of setInterval

This signals the browser you want to do something before the next redraw. The callback you provide is executed exactly once per frame.

(In case this matters: requestAnimationFrame does not work in IE9 and below.)

2. Don't increase by a fixed value per frame, tween between values

Both when using requestAnimationFrame and using setInterval, the time difference between frames vary.

You can verify that yourself by using something like this in the developer toolbar:

var last = new Date(); function onFrame(){ var now = new Date(); console.log(new Date() - last); last = now; requestAnimationFrame(onFrame); } onFrame(); 

The developer console will output the frame times in ms, like this:

16 17 17 15 19 ... 

If you increase position (not so noticeable on e.g. opacity) by a fixed amount on vaying intervals, the animation will look jagged. So Instead of doing left = left + 8;, calculate at which position in the animation you are, based on the current time, something like this:

var myElement = ...; var initialOpacity = 1.0; var targetOpacity = 0.5; var duration = 2000; var startTime = new Date(); function animation() { var delta = Math.min(1, (new Date() - startTime) / duration); // delta is now a number in the range [0 ... 1] myElement.style.opacity = initialOpacity + delta * (targetOpacity - initialOpacity); if (delta < 1) requestAnimationFrame(animation); } requestAnimationFrame(animation); 

Yes, this example tweens opacity and not position, but you get the idea - and your teacher can't claim you copy-pasted ;-)

3. Don't read and write back-and-forth between JS and CSS

Assuming the initial position of your image is not viewport-related (say, left: -10%), there is no need to read the position on every frame. When your JavaScript is the only thing changing the left property, why read it from CSS? Save it in a variable and set it to CSS from your JavaScript.

var initialX = ComputeCssXPosition(myElement); ... function animate() { ... myElement.style.left = computedNewXPosition; } 

If you want to change the postition when the user hovers an element, use mouse events in your JS.

myElement.addEventListener('mouseover', function (ev) { ... }); myElement.addEventListener('mouseout', function (ev) { ... }); 

Alternative: Use CSS transitions

Already covered in the answer by Shomz.

Sign up to request clarification or add additional context in comments.

Comments

0

The best approach would be to use requestAnimationFrame instead of setInterval and not to check for the style changes, but use mouseover listeners (too much communication: CSS->JS->CSS->JS...). See here: https://jsfiddle.net/yqpun3eb/4/

However, if you still want to use setInterval, you can simply put the transition CSS rule on your background element. For example:

#background {transition: left 0.2s linear} 

That will smooth out all the value changes because CSS performs way better, so it should be fine even on the 4K screens. The problem was your changes can jump by 8 pixels.

Seems to work nice with 0.2s on my machine: https://jsfiddle.net/yqpun3eb/3/


Oh, and btw. you want good performance, but why are you raping the system with this:

function getOpacity(param){ var element = document.getElementById(param), style = window.getComputedStyle(element), opacity = style.getPropertyValue('opacity'); return opacity } 

This doesn't create extra variables (which you don't need anyway):

function getOpacity(param){ return window.getComputedStyle(document.getElementById(param)) .getPropertyValue('opacity'); } 

Finally, here's a slightly optimized version using requestAnimationFrame (that's how I would do it + use listeners instead of reading the style values): https://jsfiddle.net/yqpun3eb/4/

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.