3

I'm trying to make a news ticker (without any plug-in). I use jQuery UI's animate method to make the news move from right to left.

I'm removing the first news (li) and attaching it to the end of the list (ul) and readjusting the margin-left. This is not to get a long white space every after cycle.

Problem is... when I .remove() the first news, it's causing an undesirable UI glitch (at least for me). This part:

leftMargin = leftMargin + $('.news-ticker-display li:first').width() - 2;
$('.news-ticker-display li:first').remove();

Here's my jsFiddle link:

Any help/suggestion/comment/alternative will be greatly appreciated. Sorry I can't think a constructive title for this. lol

2 Answers2

3

Your code is waiting fo the next animate cycle to update the left margin, which happens a few frames later. You need to set it immediately (on the same frame as the remove()) to avoid visual glitches.

var leftMargin, scrollSpeed;
var playScroll = true;

scrollSpeed = 5;
leftMargin = 0;

function autoPlay() {
    if (playScroll) {
        $('.news-ticker-display').animate({ marginLeft: --leftMargin }, scrollSpeed, "linear", function () {
            var $first = $('.news-ticker-display li:first');
            var width = $first.outerWidth();
            if (leftMargin < -width) {
                $first.clone().appendTo('.news-ticker-display ul');
                leftMargin = leftMargin + width;
                $first.remove();
                $(this).css({marginLeft: --leftMargin});
            }
            autoPlay();
        });
    }
}

autoPlay();

JSFiddle: https://jsfiddle.net/TrueBlueAussie/8djw6qen/8/

Notes:

  • You will see I have simplified the code using temp vars. It is good practice to not repeat jQuery selectors.
  • You also need to use outerWidth(), rather than try to compensate for the border.
  • I sped up the animation for the testing (taking too long to see the glitches otherwise) :) Just set it back to your own value.
  • You can reduce the first selector using a context. e.g. var $first = $('li:first', this);
  • .first() is traditionally faster than using :first in a selector, but that really does not matter here :)
iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
  • I know I am biased, but I think my solution is better. Because using `--leftMargin` will cause the animation callback to run *every single pixel* because you are basically animating one pixel at a time. – alan0xd7 Jul 15 '15 at 09:10
  • @alan0xd7: I do not disagree that your specific solution may be better technically. I simply *explained the problem asked* and fixed the code *as-presented* and explain in detail any improvements/changes. This will usually make it easier for the OP to consume. Perhaps if you improved your answer (not the code, which I am sure is fine)? – iCollect.it Ltd Jul 15 '15 at 09:14
  • @TrueBlueAussie thanks, I have updated my answer and explained why I changed how the animation works. – alan0xd7 Jul 15 '15 at 09:33
  • 1
    Hi alan0xd7, I appreciate your pointers. ^_^ But I actually needed the animation to happen every single pixel. The rationale is, since it is a news ticker, I have a play/pause button which I haven't included in my snippets... + stop animation on hover, etc. (lol users). Running the animation everytime makes it easy to break it (playScroll = false) when I wish to. While it would be harder to stop/restart the animation if it's not running everytime. – gellowmellow Jul 15 '15 at 10:21
2

I have re-implemented how the animation works. Now it animates one width of a ticker item at a time.

I believe this is a better approach, because by using --leftMargin, your animation callback is run every single pixel, whereas with this approach, it is only run after one item has scrolled by. There is also no need to check if the item has went past the start or not.

By setting the animation duration to width*scrollSpeed, every pixel is scrolled by at the same rate.

Note outerWidth is used, as it will include any border and padding of the item.

function autoPlay() {
    if(!playScroll) return;

    var width = $('.news-ticker-display li:first').outerWidth();
    var animateTime = width*scrollSpeed;

    $('.news-ticker-display').animate({ marginLeft: -width }, animateTime, "linear", function(){
        $('.news-ticker-display li:first').clone().appendTo('.news-ticker-display ul');
        $('.news-ticker-display li:first').remove();
        $('.news-ticker-display').css("marginLeft", 0);
        autoPlay();
    });
}

Demo: https://jsfiddle.net/alan0xd7/8djw6qen/6/

alan0xd7
  • 1,831
  • 1
  • 15
  • 19