Jquery animation is jumpy

I am trying to create an animated slider.

The slider should auto rotate when screen resolution is more than 1265px (use Ctrl+- to simulate on laptop)

I use jQuery.animation but the result is too jumpy.

How can I fix it so that the animation will be smoother?

var wSlider = {
    slider_width : 0,
    slide_count : 0,
    state_count : 0,
    isAutoRotate : false,
    autoRotateTimeout : null,
    isRotating : false,
    autoRotatePause : false,
    isInit: false
}

$(document).ready(function  () {
    wSlider.adjustScreen();
    $(window).focus(function () {
        wSlider.startAutoRotate();
    }).blur(function () {
        wSlider.stopAutoRotate();
        wSlider.autoRotatePause = true;

    });
})

window.onresize = function() {
    wSlider.adjustScreen();
}

wSlider.autoRotate = function() {
    if (!wSlider.isAutoRotate) return;
    wSlider.isRotating = true;
    timeFrame = 20000;

    sliderTrack = $(".sliderTrack");

    [diffLeft,diffRight] = wSlider.getDiffs();

    // console.log("diffRight",diffRight)
    // console.log("wSlider.slider_width",wSlider.slider_width)
    diff = wSlider.slider_width 

    if (diffLeft != 0) {
        diff = diffRight;
        timeFrame = timeFrame*(diffRight/wSlider.slider_width);
        // console.log("after pause",timeFrame)
    }

    // console.log("timeFrame",timeFrame)
    // console.log("diff",diff)

    left = Math.round(sliderTrack.position().left) - diff;
    sliderTrack.animate({"left": left}, timeFrame,"linear", function() {});


    wSlider.autoRotateTimeout = setTimeout(function() {
        [diffLeft,diffRight] = wSlider.getDiffs();
        if (diffLeft != 0) {
            // console.log("fix diffRight",diffRight)
            left = Math.round(sliderTrack.position().left) - diffRight;
            sliderTrack.css({"left": left})
        }

        wSlider.adjustCorusel(1);
        wSlider.autoRotate();
    },timeFrame);
}

wSlider.stopAutoRotate = function() {
    $(".sliderTrack").stop();
    clearTimeout(wSlider.autoRotateTimeout);
    wSlider.isRotating = false;
}
wSlider.startAutoRotate = function(){
    clearTimeout(wSlider.autoRotateTimeout);
    if (wSlider.autoRotatePause) {
        // wSlider.autoRotateTimeout = setTimeout(wSlider.autoRotate, 1000);
        wSlider.autoRotate();
        wSlider.autoRotatePause= false;
    } else {
        wSlider.autoRotateTimeout = setTimeout(wSlider.autoRotate, 3500);
    }

}
wSlider.adjustScreen = function () {    
    wSlider.stopAutoRotate();

    if ($(window).width() <=  1265) {
        wSlider.isAutoRotate = false;
        wSlider.slider_width = $(".w-container").width()/2;

    } else {
        wSlider.isAutoRotate = true;
        wSlider.startAutoRotate();
        wSlider.slider_width = $(".w-container").width()/3;
    }
    wSlider.slider_width = Math.round(wSlider.slider_width);


    if (!wSlider.isInit) {
        wSlider.isInit = true;
        // duuplicate before/after
        slider_entries = $('.mySlides');
        slider_entries.clone().appendTo( ".sliderTrack" )
        slider_entries.clone().prependTo( ".sliderTrack" )
        wSlider.slide_count = slider_entries.length;

    }

    $(".sliderTrack").css({ 'left':0});

    slider_entries = $('.mySlides');

    slider_entries.each(function(i,o) {
        $(o).width($(".w-container").width());
        $(o).css({ 'left': wSlider.slider_width*(i-wSlider.slide_count)+'px'});
        $(o).attr("sid",i);
    })
    $(".w-container").height(slider_entries.height()*0.79);
}

wSlider.adjustCorusel = function(n) {
    slider_entries = $('.mySlides');
    if (n > 0) {
        // console.log(">")
        // move first left element to the last right
        last_postition = $(slider_entries[slider_entries.length-1]).position().left;
        last_postition = Math.round(last_postition);
        last_postition += wSlider.slider_width;
        $(slider_entries[0]).css("left",last_postition+'px');
        $(slider_entries[0] ).detach().appendTo( ".sliderTrack" ).show();
    } else {
        // console.log("<")

        // move last right element to the first left
        fisrt_postition = $(slider_entries[0]).position().left;
        fisrt_postition = Math.round(fisrt_postition);

        fisrt_postition -= wSlider.slider_width;
        $(slider_entries[slider_entries.length-1]).css("left",fisrt_postition+'px');
        $(slider_entries[slider_entries.length-1] ).detach().prependTo( ".sliderTrack" ).show();
    }
}

wSlider.sliderMove = function(n) {

    // don't run if previous run still running
    if (wSlider.state_count != 0) return;
    wSlider.state_count = 1;
    wSlider.stopAutoRotate();
    // console.log("wSlider.sliderMove",n)


    sliderTrack = $(".sliderTrack");
    stLeft = Math.round(sliderTrack.position().left);

    [diffLeft,diffRight] = wSlider.getDiffs();

    if (diffLeft  == 0){
        // normal case - auto rotate is off
        stLeft -= wSlider.slider_width * n;
        wSlider.adjustCorusel(n);

    } else {
        // rotating in progress 
        if (n > 0) {
            stLeft -= diffRight;
            wSlider.adjustCorusel(n);
        } else {
            // no need to wSlider.adjustCorusel since we simply undoing the last rotation
            stLeft += diffLeft;
        }

    }




    // console.log('stLeft: '+stLeft);

    sliderTrack.animate({"left": stLeft}, 250,"swing", function() {
        wSlider.state_count = 0;
        if (wSlider.isAutoRotate) {
            wSlider.startAutoRotate();
        }
    });


}

// returns [diffLeft,diffRight] 
// diffLeft - the pixel value of the left most visible slider that is invisible 
// diffRight - the pixel value of the left most visible slider that is visible
wSlider.getDiffs = function() {
    sliderTrack = $(".sliderTrack");
    stLeft = Math.round(sliderTrack.position().left);

    diff = Math.abs(stLeft) % wSlider.slider_width;
    if (Math.abs(Math.abs(diff) - wSlider.slider_width) < 1) diff = 0;

    diffLeft = diff;
    diffRight = wSlider.slider_width - diff;

    if (stLeft > 0 && diff != 0) {
        // swap
        [diffLeft,diffRight] = [diffRight,diffLeft];
    }
    return [diffLeft,diffRight];
}

wSlider.focusSlide = function(o) {
    // don't run if previous run still running
    if (wSlider.state_count != 0) return;
    wSlider.state_count = 1;
    if (wSlider.isRotating) {
        wSlider.autoRotatePause = true;
    }
    wSlider.stopAutoRotate();
    // console.log("wSlider.focusSlide")



    $(".ctrl-buttons").hide();
    o = $(o);
    $(".growImg",o).removeClass("growImg").addClass("growImg-tmp");

    oLeft = Math.round(o.position().left);
    // console.log("left",oLeft);

    sliderTrack = $(".sliderTrack");
    stLeft = Math.round(sliderTrack.position().left);

    slider_entries = $('.mySlides');

    cWidth = $(".w-container").width();
    slider_entries.each(function  (i,sibling) {
        sibling = $(sibling);
        sLeft = Math.round(sibling.position().left);
        relativeLeft = sLeft + stLeft;
        if (relativeLeft < -wSlider.slider_width ||
            relativeLeft >= cWidth) 
            {
                return;
            }

        sibling.data('origLeft',sLeft);
        if (sLeft <= oLeft) {
            if (relativeLeft <= 0 && sLeft != oLeft) {
                [diffLeft,diffRight] = wSlider.getDiffs();
                sibling.animate({"left": -diffRight - stLeft}, 250,"swing", function() {});
            } else {
                sibling.animate({"left": 0 - stLeft}, 250,"swing", function() {});
            }
        } else {
            sibling.animate({"left": cWidth - stLeft}, 250,"swing", function() {});
        }

    })

    // redraw text
    content = $(".content",o);
    content.hide();
    $(".displayLeft",content).removeClass("displayLeft").addClass("displayRight");
    content.fadeIn(1000);

}
wSlider.unfocusSlide = function(o) {
    event.stopPropagation();
    if (wSlider.state_count != 1) return;
    wSlider.state_count = 0;
    o = $(o).parent();

    slider_entries = $('.mySlides');

    slider_entries.each(function  (i,sibling) {
        sibling = $(sibling);
        l = sibling.data('origLeft');
        sibling.data('origLeft',null);
        if (l != null) {
            sibling.animate({"left": l}, 250,"swing", function() {});
        }

    })

    $(".growImg-tmp",o).removeClass("growImg-tmp").addClass("growImg");
    $(".ctrl-buttons").show();

    // redraw text
    content = $(".content",o);
    content.hide();
    $(".displayRight",content).removeClass("displayRight").addClass("displayLeft");
    content.fadeIn(1000);
    wSlider.startAutoRotate();
}
button:focus {
    outline:none  !important;
}
.sliderTrack {
    position: absolute;  
}
.mySlides {
    z-index: 1;
    position: absolute;
    overflow: hidden;
    -webkit-transition: all 0.1s ease-in-out;
    border-left: 1px solid;

}
.w-container {
  /*display: flex; */
  /* or inline-flex */
  overflow: hidden;
  position: relative;
  /*-webkit-transition: all 0.1s ease-in-out;*/

}
.grow {     
    background: black;
 }
 .growImg {
    transition-property: opacity, transform, -webkit-transform;
    transition-duration: 0.75s, 0.75s, 0.75s;
    transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1), cubic-bezier(0.19, 1, 0.22, 1), cubic-bezier(0.19, 1, 0.22, 1);
    transition-delay: 0s, 0s, 0s;
    opacity: 0.8;
    z-index: 0;

 }
.growImg:hover, .content:hover + .growImg {
       opacity: 1;
       transform: scale(1.1);

}
.close-slide {
    position: absolute;
    right: 50px;
    top: 40px;
    font-size: 200%;
    color:white;
    z-index: 1;
}
.w-white {
    color: #fff!important;
    /* background-color: #000!important; */
    font-size: 400%;
}
.w-button {
    border: none;
    display: inline-block;
        margin: 8px 40px;
    vertical-align: middle;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    background-color: inherit;
    text-align: center;
    cursor: pointer;
    white-space: nowrap;
    opacity: 0.8;
    z-index: 10;

}
.content {
    position: absolute;
    top: 0;
    color: white;
    width: 100%;
    z-index: 1;

}
.displayLeft {
    margin-left: 3em;
    font-size: 400%;
    margin-top: 6em;
}
.displayRight {
    margin-left: 1em;
    font-size: 600%;
    margin-top: 3em;
    width: 50%;
    float: right;
}
p {
    text-transform: uppercase;
    font-family: Brutal_Bold;
}
.displayLeft p {

    float: left;
    margin-right: 0.3em;
}
.displayLeft .explore {
    display: none;
}
.displayRight .explore {
    display: block;
}
.explore {
    text-decoration: none !important;
    color: white;
    font-size: 50%;
    background-color: transparent;
    border: 1px solid white;

}
.explore:hover {
    color: black;
    background-color: white;
}
.ctrl-buttons {
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class=" w-container">
        <div class="sliderTrack">
            <div class="mySlides grow" onclick="wSlider.focusSlide(this)">
                <div class="content">
                    <div class="displayLeft h1 word-amount-2">
                        <p>
                            <span>Unreal</span>
                        </p>
                        <p>
                            <span>Engine</span>
                        </p>
                        <button href="https://www.unrealengine.com" class="explore">
                            Explore</a>
                    </div>
                </div>
                <img class="growImg"
                    src="https://cdn2.unrealengine.com/Epic+Games+Node%2Fue-alt-1920x1080-e653a4a4dae65307fd2420076abe44bb71b22f06.jpg"
                    style="width: 100%">

                <div class="close-slide" onclick="wSlider.unfocusSlide(this)">X</div>

            </div>
            <div class="mySlides grow" onclick="wSlider.focusSlide(this)">
                <div class="content">
                    <div class="displayLeft h1 word-amount-1">
                        <p>
                            <span>Fortnite</span>
                        </p>
                        <button class="explore">
                            Explore</a>
                    </div>
                </div>
                <img class="growImg"
                    src="https://cdn2.unrealengine.com/Epic+Games+Node%2Ffn-1920x1080-05e434e24b3170bc6cc6003c102270ee4cde3a75.jpg"
                    style="width: 100%">
                <div class="close-slide" onclick="wSlider.unfocusSlide(this)">X</div>

            </div>
            <div class="mySlides grow" onclick="wSlider.focusSlide(this)">

                <div class="content" style="">
                    <div class="displayLeft h1 word-amount-2">
                        <p>
                            <span>Robo</span>
                        </p>
                        <p>
                            <span>Recall</span>
                        </p>
                        <button class="explore">
                            Explore</a>
                    </div>
                </div>
                <img class="growImg"
                    src="https://cdn2.unrealengine.com/Epic+Games+Node%2Frr-1920x1080-5a3f8bce26f45d078c2738cf5140ad18be39041c.jpg"
                    style="width: 100%">

                <div class="close-slide" onclick="wSlider.unfocusSlide(this)">X</div>
            </div>

        </div>
    </div>

    <div class="ctrl-buttons">
        <button class="w-button w-white w3-display-left"
            onclick="wSlider.sliderMove(-1)">&#10094;</button>
        <button class="w-button w-white w3-display-right"
            onclick="wSlider.sliderMove(1)">&#10095;</button>
    </div>

2 answers

  • answered 2018-10-11 20:23 DCdaz

    The issue here is down to hardware & software.

    Some browsers are a pain in the ass when it comes to preforming animations. This is because hardware acceleration isn't enabled by default. so the best thing to do is force hardware acceleration to trigger.

    to do this use translate3d on the elements that are subject to animation. For you I believe it is your sliderTrack element & your grow element.

    After adding this to your grow element I noticed a vast improvement right off the bat when clicking into your slides.

    So simply add :

    .sliderTrack, .grow{
       -webkit-transform: translate3d(0, 0, 0);
       -moz-transform: translate3d(0, 0, 0);
       -ms-transform: translate3d(0, 0, 0);
       transform: translate3d(0, 0, 0);
    }
    

    I will also note that in my case on 1080 your slider track worked fine without acceleration. You will notice this device to device. The ability of the device & the browser will impact your animations greatly. Using translate3d will give them the best chance but if the hardware or software just isn't up to the task it will not preform as smooth as you would hope.

  • answered 2018-10-12 01:37 Elia Weiss

    I found that on my environment the border-left: 1px solid; on the .mySlides causes the jumpiness -

    removing it solve the problem.