chromium/tools/perf/page_sets/key_silk_cases/list_animation_simple.html

<!doctype html>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<style>
html, body {
    height: 100%;
    overflow: hidden;
}
* {
    box-sizing: border-box;
}
.scrollable {
    position: relative;
    height: 100%;
    overflow-y: scroll;
    overflow-x: hidden;
}
.stretcher {
    position: absolute;
    visibility: hidden;
    left: 0;
    top: 0;
    width: 1px;
    height: 1000000px;
}
.item {
    background-color: #FFF;
    width: 100%;
    position: absolute;
    left: 0;
    top: 0;
    will-change: transform;
    height: 50px;
    border: 1px solid #000;
    padding: 10px;
}
body {
    -ms-touch-action: none;
    display: none;
}
@keyframes slideIn {
    from {
        transform: translate3d(100%, 0, 0);
    }
    to {
        transform: translate3d(0, 0, 0);
    }
}
@keyframes slideOut {
    from {
        transform: translate3d(0, 0, 0);
    }
    to {
        transform: translate3d(-100%, 0, 0);
    }
}
@-webkit-keyframes slideIn {
    from {
        -webkit-transform: translate3d(100%, 0, 0);
    }
    to {
        -webkit-transform: translate3d(0, 0, 0);
    }
}
@-webkit-keyframes slideOut {
    from {
        -webkit-transform: translate3d(0, 0, 0);
    }
    to {
        -webkit-transform: translate3d(-100%, 0, 0);
    }
}
.list {
    transform: translate3d(100%, 0, 0);
    -webkit-transform: translate3d(100%, 0, 0);
    border: 5px solid red;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}
</style>
<script>
var LIST_COUNT = 10;
for (var i = 0; i < LIST_COUNT; i++) {
    document.write('<div class="list scrollable" style="border-color: #' +
            Math.floor(Math.random() * 16777215).toString(16) + '"><div class="stretcher"></div></div>');
}
var transform = 'webkitTransform' in document.body.style ? 'webkitTransform' : 'transform';

function InfiniteList(options) {
    var scrollable = options.element,
        itemTemplate = options.template,
        renderFn = options.render,
        itemHeight = options.itemHeight || 50,
        itemsPool = [],
        itemsPoolCount = 0,
        topItemIndex = 0,
        scrollableHeight = window.innerHeight,
        div, i;

    function update() {
        var newScrollTop = scrollable.scrollTop,
            newTopItemIndex = Math.floor(newScrollTop / itemHeight),
            delta = newTopItemIndex - topItemIndex;

        if (delta !== 0) {
            if (Math.abs(delta) > itemsPoolCount) {
                renderAllItems(newTopItemIndex);
            }
            else {
                renderItems(delta);
            }
        }
    }

    function renderAllItems(index) {
        for (i = 0; i < itemsPoolCount; i++) {
            renderItem(itemsPool[i], index + i);
        }

        topItemIndex = index;
    }

    function renderItems(count) {
        var lastIndex = itemsPoolCount - 1;

        // Recycle top items to render at the bottom
        if (count > 0) {
            for (i = 0; i < count; i++) {
                div = itemsPool[i];
                renderItem(div, topItemIndex + itemsPoolCount + i);
            }

            itemsPool.push.apply(itemsPool, itemsPool.splice(0, count));
            topItemIndex += count;
        }
        // Recycle bottom items to render at the top
        else {
            count = Math.abs(count);
            for (i = lastIndex; i > lastIndex - count; i--) {
                div = itemsPool[i];
                renderItem(div, --topItemIndex);
            }

            itemsPool.unshift.apply(itemsPool, itemsPool.splice(lastIndex - count + 1, count));
        }
    }

    function renderItem(div, index) {
        renderFn(div, index);
        div.style[transform] = 'translate3d(0, ' + (index * itemHeight) + 'px' + ', 0)';
    }

    function loop() {
        window.requestAnimationFrame(function() {
            update();
            loop();
        });
    }

    window.addEventListener('DOMContentLoaded', function() {
      console.log('DOMContentLoaded');
        var holder = document.createElement('div'),
            itemsHtml = [];

        itemsPoolCount = Math.ceil(scrollableHeight / itemHeight) + 1;

        for (i = 0; i < itemsPoolCount; i++) {
            itemsHtml.push(itemTemplate);
        }

        holder.innerHTML = itemsHtml.join('');
        itemsPool.push.apply(itemsPool, holder.children);
        itemsPool.forEach(function(item) {
            item.style.height = itemHeight + 'px';
            scrollable.appendChild(item);
        });

        renderAllItems(0);
        loop();
    });
}
document.body.style.display = 'block';
var lists = document.querySelectorAll('.list'),
    prefix = 'onwebkitanimationend' in window ? '-webkit-' : '',
    count = 0,
    index = 0;
[].forEach.call(lists, function(list) {
    new InfiniteList({
        element: list,
        template: '<div class="item"></div>',
        render: function(div, index) {
            div.textContent = 'This is item #' + index;
        }
    });
});
function animate(index, name) {
    var list = lists[index];
    list.style.setProperty(prefix + 'animation-name', name, null);
    list.style.setProperty(prefix + 'animation-duration', '5s', null);
    list.style.setProperty(prefix + 'animation-timing-function', 'ease-out', null);
    list.style.setProperty(prefix + 'animation-fill-mode', 'forwards', null);
    count++;
}
function next() {
    if (index > lists.length - 1) {
        index = 0;
    }
    animate(index, 'slideOut');
    if (++index > lists.length - 1) {
        index = 0;
    }
    animate(index, 'slideIn');
}
document.addEventListener(prefix ? 'webkitAnimationEnd' : 'animationend', function() {
  console.log('animationend');
    if (--count === 0) {
        next();
    }
}, true);
next();
</script>