Skip to main content
3 of 3
Spelling of the marquee element in the title.

Custom marquee consumes a lot of CPU power

I've a code which applies marquee to certain elements by using the requestAnimFrame method. However, when I test my application on a lower spec PC (Intel Celeron 2.13GHz dou core) the CPU usage is skyrocketing and gets to a minimum of around 80%! (I have to mentioned that I have 2 elements that the custom maruqee is applied to).

Also, there are cases where even 3 or 4 elements are being targeted by the marquee. There is also a case in my application, not so common, that there is another animation that scrolls text from the right to the left.

My development environment is a lot different, I'm using a 3.8GHz 8-cores CPU with a strong GPU so my CPU usage is no more than 11%.

I'm not sure if it matters but locally I'm using xampp and on the tested PC I've been using wamp.

The code for my marquee is splitted into few functions:

/**
 * Applies the marquee function to the elements who are overflowing
 */
function tryMarquee(/**/) {

    var args = arguments;

    for(var i=0; i<args.length; i++) {
        var elem = $(args[i]);
        var containerHeight = elem.outerHeight(true);
        var contentHeight = calculateContentHeight(elem);

        // extract args
        var settings = $('.marqueeSettings');
        var speed = settings.find('input[data-target="'+args[i]+'"]input[name=marqueeSpeed]').val() || 1;
        var spacer = settings.find('input[data-target="'+args[i]+'"]input[name=marqueeSpacer]').val() == "1";
        var spacerHeight = settings.find('input[data-target="'+args[i]+'"]input[name=marqueeSpacerHeight]').val() || 60;

        if(contentHeight > containerHeight && containerHeight > 10) {
            marquee(args[i], speed, spacer, spacerHeight);
        }

    }

}

/**
 * Calculates the content height of an element by it's children's height
 * @param elem
 * @returns {number}
 */
function calculateContentHeight(elem) {

    var total = 0;
    elem.children().not('.clone').each(function() {
        if(parseInt($(this).css('margin-top')) >= 0)
            total += $(this).height() + parseInt($(this).css('margin-top'));
        else
            total += $(this).height();
    });

    total -= 10;
    return total;

}

/**
 * Generates a spacer.
 * @param marginTop
 * @param height
 * @returns {*|jQuery|HTMLElement}
 */
function generateSpacer(marginTop, height) {

    var spacer = $('<div class="marquee-spacer clone" style="margin-top: '+marginTop+'px;"></div>');

    height = height || 60;

    spacer.css({
        height: height
    });

    return spacer;

}

/**
 * returns a clone of an element's children
 * @param elem
 * @returns {*|jQuery|HTMLElement}
 */
function duplicateContent(elem) {
    return elem.children().clone().addClass('clone');
}
/**
 * Checks the prayers element still needs the marquee.
 * @returns {boolean}
 */
function keepPrayersMaruqee() {

    var containerHeight = elem.outerHeight(true);
    var contentHeight = calculateContentHeight(prayers);

    return contentHeight > containerHeight;

}

These functions are only triggered once so I'm not sure if it has something to do when the application is fully loaded and already running.

function marquee(className, scrollAmount, spacer, spacerHeight) {

    // parse spacer
    spacer = spacer || false;

    // select the elements
    var elemSet = $(className);

    // loop through the element set
    elemSet.each(function() {

        var $this = $(this);

        $this.addClass('marquee');

        /**
         *  TODO: if the container is taller than the content we should clone the content so there's no gap in the loop
         */

        var initialMargin = parseInt($this.find("div").first().css('margin-top'));

        if(spacer)
            $this.append(generateSpacer($this, initialMargin, spacerHeight));

        $this.append(duplicateContent($this));

        (function loop(){

            /**
             *  This block of code is only executed on a certain element so check if it still needs to be scrolled
             *  because there is a possibility that the element's children will be removed dynamically.
             */
            if($this.hasClass('prayers'))
                if( ! keepPrayersMaruqee()) {
                    // remove clone elements
                    $this.children('.clone').remove();
                    // cancel the maruqee
                    return;
                }


            var first = $this.find("div").first();
            var top = parseInt(first.css('margin-top'));
            var height = first.outerHeight();

            if ((height+top) > 0){
                first.css('margin-top','-='+scrollAmount);
            } else {
                first.appendTo($this);
                first.css('margin-top',initialMargin);
            }

            /**
             * repeat the animation
             * @see window.requestAnimFrame
             */
            requestAnimFrame(loop);
        })();

    });
}

A working fiddle

This is where the magic happens. I was trying to make this code as efficient as possible. Any suggestion on how I can actually improve this code and make it consume much less CPU power?

kfirba
  • 163
  • 5