JSFiddle - React, Tailwind, and code Playground

by kougiland

HTML

<div class="rotator">
    <div class="ctr" data-bind="style:{width:width, height:height}">
        <div class="images" data-bind="
                template: {name:'imageTpl',
                           foreach:images,
                           beforeRemove : function(elem)   {this.removeDecorator(elem)}.bind($data)}"></div>
        <!-- ko if: loading -->
        <div class="loading">
            <img src="http://pshabalin.files.wordpress.com/2013/01/loading.gif"/>
        </div>
        <!-- /ko -->
    </div>
    <div class="controls" data-bind="template: { name: 'controlTpl', foreach: imageViewModels}"></div>
</div>
</body>

<script id="imageTpl" type="text/html"><img data-bind="attr : {src : url}"/></script>
<script id="controlTpl" type="text/html">
    <span data-bind="css : {active : active()}, click : $parent.onCtrlClick.bind($parent)"></span>
</script>

CSS

.rotator {

}

.ctr {
    overflow: hidden;
    border-radius: 2px;
    -webkit-box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.2);
    -moz-box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.2);
    box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.2);
    border: 1px solid #b6b6b6;
    border-color: rgba(0, 0, 0, 0.2);
    margin: 5px auto;
    position: relative;
}

.rotator .images {
    position: absolute;
    overflow: hidden;
    width: 100%;
    height: 100%;
}

.rotator .images img {
    position: absolute;
    text-align: center;
    display: block;
}

.loading {
    position: absolute;
    width: 100%;
    height: 100%;
    background: white;
}

.loading img {
    position: absolute;
    left: 48%;
    top:45%;
}

.controls {
    width: 100%;
    text-align: center;
    margin: 10px auto;
}

.controls span {
    display: inline-block;
    height: 12px;
    width: 12px;
    box-sizing: border-box;
    background-color: rgba(0, 0, 0, 0.02);
    border: 1px solid rgba(0, 0, 0, 0.2);
    border-radius: 7px;
    margin: 0 5px;
    cursor: pointer;
    box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.08) inset;
    -webkit-transition: all 400ms;
    transition: all 400ms;
}

.controls span.active {
    cursor: default;
    background-color: rgba(0, 0, 0, 0.4);
    border: none;    box-shadow: 0px -1px 1px 0px rgba(0, 0, 0, 0.35) inset;
}

JavaScript

var images = ['http://pshabalin.files.wordpress.com/2013/01/yellowstone-77.png', 'http://pshabalin.files.wordpress.com/2013/01/yellowstone-58.png', 'http://pshabalin.files.wordpress.com/2013/01/img_1082.png', 'http://pshabalin.files.wordpress.com/2013/01/dscn2295.png', 'http://pshabalin.files.wordpress.com/2013/01/img_1082.png', 'http://pshabalin.files.wordpress.com/2013/01/dscn2295.png', 'http://pshabalin.files.wordpress.com/2013/01/img_1082.png', 'http://pshabalin.files.wordpress.com/2013/01/dscn2295.png'];

var ImageRotator = function (options) {
    this.width = options.width + 'px';
    this.height = options.height + 'px';
    this.imageUrls = options.images;
    this.interval = options.interval;
    this.transition = options.transition;
    this.loading = ko.observable(true);
    this.imageViewModels = [];
    options.images.forEach(function (image) {
        var imageViewModel = {url:image, active:ko.observable(false)};
        this.imageViewModels.push(imageViewModel);
    }.bind(this));
    this.imageViewModels[0].active(true);
    this.images = ko.observableArray();
    this.jsInt = setInterval(this.nextImage.bind(this), this.interval);
    this.loadFirstImage();
};


ImageRotator.prototype.nextImage = function () {
    this.images.valueWillMutate();
    var top = this.images.pop();
    var ind = this.imageViewModels.indexOf(top);
    var nextInd = (ind + 2) % this.imageViewModels.length;
    var next = this.imageViewModels[nextInd];
    this.images.unshift(next);
    top.active(false);
    this.images()[1].active(true);
    this.images.valueHasMutated();
};

ImageRotator.prototype.onCtrlClick = function (imageVM) {
    if (imageVM == this.images()[1]) {
        return;
    }
    clearInterval(this.jsInt);
    var top = this.images.pop();
    this.images()[0] = imageVM;
    var ind = this.imageViewModels.indexOf(imageVM);
    var nextInd = (ind + 1) % this.imageViewModels.length;
    var next = this.imageViewModels[nextInd];
   ...