Update InfiniteCircularScroll component

This commit is contained in:
denz
2015-07-15 16:34:20 +09:00
parent aa6a4b318a
commit ef982cbb27
2 changed files with 198 additions and 165 deletions
@@ -1,170 +1,203 @@
(function(window, $) {
'use strict';
var ROW_SPARE_COUNT = 8;
window.InfiniteCircularScroll = $.Class({
$init: function(options) {
this.option(options);
this._init();
this._initVar();
this._initEvent();
},
_init: function() {
this.$wrapper = this.option("wrapper");
this._threshold = this.option("elementHeight") * (ROW_SPARE_COUNT / 4); // 2 * elementHeight
this._previousTop = 0;
this._previousTime = -1;
this._selectedRow = -1;
},
_initVar: function() {
this._elementArray = [];
this._elementArrayStartIndex = 0;
this._elementArrayEndIndex = 0;
this._elementArraySize = 0;
this._previousTop = 0;
},
_initEvent: function() {
var self = this;
this.option("scroller").on("scroll", function(event) {
self._scrollEventHandler(event, $(this));
});
},
_calculateElementStartCount: function() {
var temp = parseInt( this._viewAreaHeight / this.option("elementHeight") ) + ROW_SPARE_COUNT;
this._elementArraySize = temp > this._source.length ? this._source.length : temp;
this._elementArrayEndIndex = this._elementArraySize - 1;
},
_initElementArray: function() {
this.$wrapper.empty();
for( var i = 0 ; i < this._elementArraySize ; i++ ) {
var $element = $(this.option("template"));
this._renderElement($element, i);
this._elementArray.push($element);
this.$wrapper.append($element);
}
},
_renderElement: function($element, index) {
this._renderFunc.call( this, $element, index, this._source[index] );
$element.attr("data-index", index).css("top", (index * this.option("elementHeight")) + "px" );
},
_getDataIndex: function(index) {
return parseInt(this._elementArray[index].attr("data-index"));
},
_getTopByIndex: function(index) {
return parseInt(this._elementArray[index].css("top"));
},
_getNextArrayIndex: function(index) {
return index + 1 >= this._elementArraySize ? 0 : index + 1;
},
_getPreviousArrayIndex: function(index) {
return ( index - 1 < 0 ? this._elementArraySize : index ) - 1;
},
_scrollEventHandler: function(event, $targetElement) {
var top = $targetElement.scrollTop();
var mTime = new Date().valueOf();
if ( top == this._previousTop ) return;
var bIsDown = top - this._previousTop > 0;
if ( bIsDown ) { // scroll down
this._scrollDown(top, this._getTopByIndex(this._elementArrayStartIndex));
} else { // scroll up
this._scrollUp(top, this._getTopByIndex(this._elementArrayEndIndex));
}
this._previousTop = top;
this._previousTime = mTime;
},
_scrollDown: function( top, firstElementTop ) {
var exceededDistance = top - firstElementTop - this._threshold;
if ( exceededDistance >= 0 ) {
var sourceLastIndex = this._getDataIndex(this._elementArrayEndIndex);
var nextIndex = this._elementArrayStartIndex;
var nextIndexStep = parseInt(exceededDistance / this.option("elementHeight"));
for( var i = 0 ; i < nextIndexStep ; i++ ) {
var sourceIndex = sourceLastIndex + i + 1;
if ( sourceIndex >= this._source.length ) {
break;
}
if ( nextIndexStep - i <= this._elementArraySize * 2 ) {
this._renderElement(this._elementArray[nextIndex], sourceIndex);
}
nextIndex = this._getNextArrayIndex(nextIndex);
}
this._elementArrayStartIndex = nextIndex;
this._elementArrayEndIndex = this._getPreviousArrayIndex(nextIndex);
}
},
_scrollUp: function( top, lastElementTop ) {
var exceededDistance = lastElementTop + this.option("elementHeight") - top - this._viewAreaHeight - this._threshold;
if ( exceededDistance >= 0 ) {
var sourceFirstIndex = this._getDataIndex(this._elementArrayStartIndex);
var previousIndex = this._elementArrayEndIndex;
var previousIndexStep = parseInt(exceededDistance / this.option("elementHeight"));
for( var i = 0 ; i < previousIndexStep ; i++ ) {
var sourceIndex = sourceFirstIndex - (i + 1);
if ( sourceIndex < 0 ) {
break;
}
if ( previousIndexStep - i <= this._elementArraySize * 2 ) {
this._renderElement(this._elementArray[previousIndex], sourceIndex);
}
previousIndex = this._getPreviousArrayIndex(previousIndex);
}
this._elementArrayEndIndex = previousIndex;
this._elementArrayStartIndex = this._getNextArrayIndex(previousIndex);
}
},
setSource: function( source ) {
this._source= source;
return this;
},
setViewAreaHeight: function( viewAreaHeight ) {
this._viewAreaHeight = viewAreaHeight;
return this;
},
setRenderFunc: function( fnRender ) {
this._renderFunc = fnRender;
return this;
},
reset: function() {
this._initVar();
this._calculateElementStartCount();
this._initElementArray();
return this;
},
getContentsAreaHeight: function() {
return this.option("elementHeight") * this._source.length;
},
destroy: function() {
this.$wrapper.parent().off("scroll");
},
resize: function( resizedViewAreaHeight ) {
var currentRow = parseInt(this.option("scroller").scrollTop() / this.option("elementHeight"));
this.setViewAreaHeight(resizedViewAreaHeight);
var ICS = function( options ){
this.options = options;
this._init();
this._initOption();
this._initVar();
this._initEvent();
this._renderScrollArea();
};
ICS.prototype._init = function() {
this.$wrapper = this.options.wrapper;
this._threshold = this.options.elementHeight * (ROW_SPARE_COUNT / 4); // 2 * elementHeight
};
ICS.prototype._initOption = function() {
this._viewAreaHeight = this.options.viewAreaHeight;
this._renderFunc = this.options.renderFunc || function() {};
this._source = this.options.source;
this._previousTop = 0;
this._previousTime = -1;
this._selectedRow = -1;
}
ICS.prototype._initVar = function() {
this._elementArray = [];
this._elementArrayStartIndex = 0;
this._elementArrayEndIndex = 0;
this._elementArraySize = 0;
this._previousTop = 0;
};
ICS.prototype._initEvent = function() {
var self = this;
this.options.scroller.on("scroll", function(event) {
self._scrollEventHandler(event, $(this));
});
},
ICS.prototype._isDefined = function( v ) {
return typeof v != "undefined";
};
ICS.prototype._renderScrollArea = function() {
if ( this._isDefined( this._source ) && this._isDefined( this.options.viewAreaHeight ) ) {
this.reset();
this.moveByRow( currentRow );
},
moveByRow: function( row ) { // from 0
var self = this;
setTimeout(function() {
self.option("scroller").scrollTop(row * self.option("elementHeight"));
},0);
},
searchRow: function( from, to, func) {
var resultRow = -1;
to = (to == -1 ? this._source.length : to);
for( var i = from ; i < to ; i++ ) {
if ( func(this._source[i]) ) {
resultRow = i;
}
},
ICS.prototype._calculateElementStartCount = function() {
var temp = parseInt( this._viewAreaHeight / this.options.elementHeight ) + ROW_SPARE_COUNT;
this._elementArraySize = temp > this._source.length ? this._source.length : temp;
this._elementArrayEndIndex = this._elementArraySize - 1;
this.$wrapper.height( this._source.length * this.options.elementHeight );
};
ICS.prototype._initElementArray = function() {
this.$wrapper.empty();
for( var i = 0 ; i < this._elementArraySize ; i++ ) {
var $element = $(this.options.template);
this._renderElement($element, i);
this._elementArray.push($element);
this.$wrapper.append($element);
}
};
ICS.prototype._renderElement = function($element, index) {
this._renderFunc.call( this, $element, index, this._source[index] );
$element.attr("data-index", index).css("top", (index * this.options.elementHeight) + "px" );
};
ICS.prototype._getDataIndex = function(index) {
return parseInt(this._elementArray[index].attr("data-index"));
};
ICS.prototype._getTopByIndex = function(index) {
return parseInt(this._elementArray[index].css("top"));
};
ICS.prototype._getNextArrayIndex = function(index) {
return index + 1 >= this._elementArraySize ? 0 : index + 1;
};
ICS.prototype._getPreviousArrayIndex = function(index) {
return ( index - 1 < 0 ? this._elementArraySize : index ) - 1;
};
ICS.prototype._scrollEventHandler = function(event, $targetElement) {
var top = $targetElement.scrollTop();
var mTime = new Date().valueOf();
if ( top == this._previousTop ) return;
var bIsDown = top - this._previousTop > 0;
if ( bIsDown ) { // scroll down
this._scrollDown(top, this._getTopByIndex(this._elementArrayStartIndex));
} else { // scroll up
this._scrollUp(top, this._getTopByIndex(this._elementArrayEndIndex));
}
this._previousTop = top;
this._previousTime = mTime;
};
ICS.prototype._scrollDown = function( top, firstElementTop ) {
var exceededDistance = top - firstElementTop - this._threshold;
if ( exceededDistance >= 0 ) {
var sourceLastIndex = this._getDataIndex(this._elementArrayEndIndex);
var nextIndex = this._elementArrayStartIndex;
var nextIndexStep = parseInt(exceededDistance / this.options.elementHeight);
for( var i = 0 ; i < nextIndexStep ; i++ ) {
var sourceIndex = sourceLastIndex + i + 1;
if ( sourceIndex >= this._source.length ) {
break;
}
if ( nextIndexStep - i <= this._elementArraySize * 2 ) {
this._renderElement(this._elementArray[nextIndex], sourceIndex);
}
nextIndex = this._getNextArrayIndex(nextIndex);
}
return resultRow;
},
setSelectedRow: function(row, func) {
this._elementArrayStartIndex = nextIndex;
this._elementArrayEndIndex = this._getPreviousArrayIndex(nextIndex);
}
},
ICS.prototype._scrollUp = function( top, lastElementTop ) {
var exceededDistance = lastElementTop + this.options.elementHeight - top - this._viewAreaHeight - this._threshold;
if ( exceededDistance >= 0 ) {
var sourceFirstIndex = this._getDataIndex(this._elementArrayStartIndex);
var previousIndex = this._elementArrayEndIndex;
var previousIndexStep = parseInt(exceededDistance / this.options.elementHeight);
for( var i = 0 ; i < previousIndexStep ; i++ ) {
var sourceIndex = sourceFirstIndex - (i + 1);
if ( sourceIndex < 0 ) {
break;
}
if ( previousIndexStep - i <= this._elementArraySize * 2 ) {
this._renderElement(this._elementArray[previousIndex], sourceIndex);
}
previousIndex = this._getPreviousArrayIndex(previousIndex);
}
this._elementArrayEndIndex = previousIndex;
this._elementArrayStartIndex = this._getNextArrayIndex(previousIndex);
}
};
ICS.prototype.source = function() {
if ( arguments.length == 1 ) {
this.options.source = arguments[0];
return this;
} else {
return this._source;
}
};
ICS.prototype.viewAreaHeight = function() {
if ( arguments.length == 1 ) {
this.options.viewAreaHeight = arguments[0];
return this;
} else {
return this._viewAreaHeight;
}
};
ICS.prototype.renderFunc = function() {
if ( arguments.length == 1 ) {
this.options.renderFunc = arguments[0];
return this;
} else {
return this._renderFunc;
}
};
ICS.prototype.reset = function() {
this._initOption();
this._initVar();
this._calculateElementStartCount();
this._initElementArray();
return this;
};
ICS.prototype.contentsAreaHeight = function() {
return this.options.elementHeight * this._source.length;
};
ICS.prototype.destroy = function() {
this.options.scroller.off("scroll");
};
ICS.prototype.resize = function( resizedViewAreaHeight ) {
var currentRow = parseInt(this.options.scroller.scrollTop() / this.options.elementHeight);
this.setViewAreaHeight(resizedViewAreaHeight);
this.reset();
this.moveByRow( currentRow );
};
ICS.prototype.moveByRow = function( row ) { // from 0
var self = this;
setTimeout(function() {
self.options.scroller.scrollTop(row * self.options.elementHeight);
},0);
};
ICS.prototype.searchRow = function( from, to, func) {
var resultRow = -1;
to = (to == -1 ? this._source.length : to);
for( var i = from ; i < to ; i++ ) {
if ( func(this._source[i]) ) {
resultRow = i;
break;
}
}
return resultRow;
};
ICS.prototype.selectedRow = function(row, func) {
if ( arguments.length == 0 ) {
return this._selectedRow;
} else {
func.call(this, row);
this._selectedRow = row;
return this;
}
});
})(window, jQuery);
};
window.InfiniteCircularScroll = ICS;
})(window, jQuery);
@@ -33,7 +33,7 @@
'</div>'
].join("")
});
icscroller.setRenderFunc(function( $element, index, elementData ) {
icscroller.renderFunc(function( $element, index, elementData ) {
var marginLeft = getMarginLeft(elementData);
$element[ index == this._selectedRow ? "addClass" : "removeClass" ]("timeline-bar-selected")
.find("div.clickable-bar").css({
@@ -66,11 +66,11 @@
scope.key = transactionDetail.callStackIndex;
scope.barRatio = 1000 / (transactionDetail.callStack[0][scope.key.end] - transactionDetail.callStack[0][scope.key.begin]);
scope.newCallStacks = filterCallStacks();
icscroller.setSource( scope.newCallStacks )
.setViewAreaHeight( $(element).parentsUntil("div.wrapper").height() - 70 ) // 70 is header area height
.setSelectedRow(-1, angular.noop)
icscroller.source( scope.newCallStacks )
.viewAreaHeight( $(element).parentsUntil("div.wrapper").height() - 70 ) // 70 is header area height
.selectedRow(-1, angular.noop)
.reset();
scope.maxHeight = icscroller.getContentsAreaHeight();
scope.maxHeight = icscroller.contentsAreaHeight();
searchStartIndex = 0;
scope.$digest();
@@ -148,7 +148,7 @@
});
};
searchSuccess = function(resultIndex, message) {
icscroller.setSelectedRow( resultIndex, function(newIndex) {
icscroller.selectedRow( resultIndex, function(newIndex) {
this.$wrapper.find("div[data-index=" + this._selectedRow + "]").removeClass("timeline-bar-selected");
this.$wrapper.find("div[data-index=" + newIndex + "]").addClass("timeline-bar-selected");
}).moveByRow( resultIndex );