| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- define(function(require, exports, module) {
- "use strict";
- var Util = require('../util'),
- Base = require('../base');
- var transform = Util.prefixStyle("transform");
- var transition = Util.prefixStyle("transition");
- /**
- * An infinity dom-recycled list plugin for xscroll.
- * @constructor
- * @param {object} cfg
- * @param {string} cfg.transition recomposition cell with a transition
- * @param {string} cfg.infiniteElements dom-selector for reused elements
- * @param {function} cfg.renderHook render function for cell by per col or per row duration scrolling
- * @extends {Base}
- */
- var Infinite = function(cfg) {
- Infinite.superclass.constructor.call(this, cfg);
- this.userConfig = Util.mix({
- transition: 'all 0.5s ease'
- }, cfg);
- }
- Util.extend(Infinite, Base, {
- /**
- * a pluginId
- * @memberOf Infinite
- * @type {string}
- */
- pluginId: "infinite",
- /**
- * store the visible elements inside of view.
- * @memberOf Infinite
- * @type {object}
- */
- visibleElements: {},
- /**
- * store all elements data.
- * @memberOf Infinite
- * @type {object}
- */
- sections: {},
- /**
- * plugin initializer
- * @memberOf Infinite
- * @override Base
- * @return {Infinite}
- */
- pluginInitializer: function(xscroll) {
- var self = this;
- self.xscroll = xscroll;
- self.isY = !!(xscroll.userConfig.zoomType == "y");
- self._ = {
- _top:self.isY ? "_top" : "_left",
- _height:self.isY ? "_height" : "_width",
- top:self.isY ? "top" : "left",
- height:self.isY ? "height" : "width",
- width:self.isY ? "width" : "height",
- y:self.isY ? "y" : "x",
- translate:self.isY ? "translateY" : "translateX",
- containerHeight:self.isY ? "containerHeight" : "containerWidth",
- scrollTop:self.isY ? "scrollTop" : "scrollLeft",
- }
- self._initInfinite();
- xscroll.on("afterrender", function() {
- self.render();
- self._bindEvt();
- });
- return self;
- },
- /**
- * detroy the plugin
- * @memberOf Infinite
- * @override Base
- * @return {Infinite}
- */
- pluginDestructor: function() {
- var self = this;
- var _ = self._;
- for (var i = 0; i < self.infiniteLength; i++) {
- self.infiniteElements[i].style[_.top] = "auto";
- self.infiniteElements[i].style[transform] = "none";
- self.infiniteElements[i].style.visibility = "hidden";
- }
- self.xscroll && self.xscroll.off("scroll", self._updateByScroll, self);
- self.xscroll && self.xscroll.off("tap panstart pan panend", self._cellEventsHandler, self);
- return self;
- },
- _initInfinite: function() {
- var self = this;
- var xscroll = self.xscroll;
- var _ = self._;
- self.sections = {};
- self.infiniteElements = xscroll.renderTo.querySelectorAll(self.userConfig.infiniteElements);
- self.infiniteLength = self.infiniteElements.length;
- self.infiniteElementsCache = (function() {
- var tmp = []
- for (var i = 0; i < self.infiniteLength; i++) {
- tmp.push({});
- self.infiniteElements[i].style.position = "absolute";
- self.infiniteElements[i].style[_.top] = 0;
- self.infiniteElements[i].style.visibility = "hidden";
- self.infiniteElements[i].style.display = "block";
- Util.addClass(self.infiniteElements[i], "_xs_infinite_elements_");
- }
- return tmp;
- })();
- self.elementsPos = {};
- return self;
- },
- _renderUnRecycledEl: function() {
- var self = this;
- var _ = self._;
- var translateZ = self.userConfig.gpuAcceleration ? " translateZ(0) " : "";
- for (var i in self.__serializedData) {
- var unrecycledEl = self.__serializedData[i];
- if (self.__serializedData[i]['recycled'] === false) {
- var el = unrecycledEl.id && document.getElementById(unrecycledEl.id.replace("#", "")) || document.createElement("div");
- var randomId = Util.guid("xs-row-");
- el.id = unrecycledEl.id || randomId;
- unrecycledEl.id = el.id;
- self.xscroll.content.appendChild(el);
- for (var attrName in unrecycledEl.style) {
- if (attrName != _.height && attrName != "display" && attrName != "position") {
- el.style[attrName] = unrecycledEl.style[attrName];
- }
- }
- el.style[_.top] = 0;
- el.style.position = "absolute";
- el.style.display = "block";
- el.style[_.height] = unrecycledEl[_._height] + "px";
- el.style[transform] = _.translate + "(" + unrecycledEl[_._top] + "px) " + translateZ;
- Util.addClass(el, unrecycledEl.className);
- self.userConfig.renderHook.call(self, el, unrecycledEl);
- }
- }
- },
- /**
- * render or update the scroll contents
- * @memberOf Infinite
- * @return {Infinite}
- */
- render: function() {
- var self = this;
- var _ = self._;
- var xscroll = self.xscroll;
- var offset = self.isY ? xscroll.getScrollTop() : xscroll.getScrollLeft();
- self.visibleElements = self.getVisibleElements(offset);
- self.__serializedData = self._computeDomPositions();
- xscroll.sticky && xscroll.sticky.render(true); //force render
- xscroll.fixed && xscroll.fixed.render();
- var size = xscroll[_.height];
- var containerSize = self._containerSize;
- if (containerSize < size) {
- containerSize = size;
- }
- xscroll[_.containerHeight] = containerSize;
- xscroll.container.style[_.height] = containerSize + "px";
- xscroll.content.style[_.height] = containerSize + "px";
- self._renderUnRecycledEl();
- self._updateByScroll();
- self._updateByRender(offset);
- self.xscroll.boundryCheck();
- return self;
- },
- _getChangedRows: function(newElementsPos) {
- var self = this;
- var changedRows = {};
- for (var i in self.elementsPos) {
- if (!newElementsPos.hasOwnProperty(i)) {
- changedRows[i] = "delete";
- }
- }
- for (var i in newElementsPos) {
- if (newElementsPos[i].recycled && !self.elementsPos.hasOwnProperty(i)) {
- changedRows[i] = "add";
- }
- }
- self.elementsPos = newElementsPos;
- return changedRows;
- },
- _updateByScroll: function(e) {
- var self = this;
- var xscroll = self.xscroll;
- var _ = self._;
- var _pos = e && e[_.scrollTop];
- var pos = _pos === undefined ? (self.isY ? xscroll.getScrollTop() : xscroll.getScrollLeft()) : _pos;
- var elementsPos = self.getVisibleElements(pos);
- var changedRows = self.changedRows = self._getChangedRows(elementsPos);
- try{
- for (var i in changedRows) {
- if (changedRows[i] == "delete") {
- self._pushEl(i);
- }
- if (changedRows[i] == "add") {
- var elObj = self._popEl(elementsPos[i][self.guid]);
- var index = elObj.index;
- var el = elObj.el;
- if (el) {
- self.infiniteElementsCache[index].guid = elementsPos[i].guid;
- self.__serializedData[elementsPos[i].guid].__infiniteIndex = index;
- self._renderData(el, elementsPos[i]);
- self._renderStyle(el, elementsPos[i]);
- }
- }
- }
- }catch(e){
- console.warn('Not enough infiniteElements setted!');
- }
- return self;
- },
- _updateByRender: function(pos) {
- var self = this;
- var _ = self._;
- var xscroll = self.xscroll;
- var pos = pos === undefined ? (self.isY ? xscroll.getScrollTop() : xscroll.getScrollLeft()) : pos;
- var prevElementsPos = self.visibleElements;
- var newElementsPos = self.getVisibleElements(pos);
- var prevEl, newEl;
- //repaint
- for (var i in newElementsPos) {
- newEl = newElementsPos[i];
- for (var j in prevElementsPos) {
- prevEl = prevElementsPos[j];
- if (prevEl.guid === newEl.guid) {
- if (newEl.style != prevEl.style || newEl[_._top] != prevEl[_._top] || newEl[_._height] != prevEl[_._height]) {
- self._renderStyle(self.infiniteElements[newEl.__infiniteIndex], newEl, true);
- }
- if (JSON.stringify(newEl.data) != JSON.stringify(prevEl.data)) {
- self._renderData(self.infiniteElements[newEl.__infiniteIndex], newEl);
- }
- } else {
- // paint
- if (self.__serializedData[newEl.guid].recycled && self.__serializedData[newEl.guid].__infiniteIndex === undefined) {
- var elObj = self._popEl();
- self.__serializedData[newEl.guid].__infiniteIndex = elObj.index;
- self._renderData(elObj.el, newEl);
- self._renderStyle(elObj.el, newEl);
- }
- }
- }
- }
- self.visibleElements = newElementsPos;
- },
- /**
- * get all element posInfo such as top,height,template,html
- * @return {Array}
- **/
- _computeDomPositions: function() {
- var self = this;
- var _ = self._;
- var pos = 0,
- size = 0,
- sections = self.sections,
- section;
- var data = [];
- var serializedData = {};
- for (var i in sections) {
- for (var j = 0, len = sections[i].length; j < len; j++) {
- section = sections[i][j];
- section.sectionId = i;
- section.index = j;
- data.push(section);
- }
- }
- //f = v/itemSize*1000 < 60 => v = 0.06 * itemSize
- self.userConfig.maxSpeed = 0.06 * 50;
- for (var i = 0, l = data.length; i < l; i++) {
- var item = data[i];
- size = item.style && item.style[_.height] >= 0 && item.style.position != "fixed" ? item.style[_.height] : 0;
- item.guid = item.guid || Util.guid();
- item[_._top] = pos;
- item[_._height] = size;
- item.recycled = item.recycled === false ? false : true;
- pos += size;
- serializedData[item.guid] = item;
- }
- self._containerSize = pos;
- return serializedData;
- },
- /**
- * get all elements inside of the view.
- * @memberOf Infinite
- * @param {number} pos scrollLeft or scrollTop
- * @return {object} visibleElements
- */
- getVisibleElements: function(pos) {
- var self = this;
- var xscroll = self.xscroll;
- var _ = self._;
- var pos = pos === undefined ? (self.isY ? xscroll.getScrollTop() : xscroll.getScrollLeft()) : pos;
- var threshold = self.userConfig.threshold >= 0 ? self.userConfig.threshold : xscroll[_.height] / 3;
- var tmp = {},
- item;
- var data = self.__serializedData;
- for (var i in data) {
- item = data[i];
- if (item[_._top] >= pos - threshold && item[_._top] <= pos + xscroll[_.height] + threshold) {
- tmp[item.guid] = item;
- }
- }
- return JSON.parse(JSON.stringify(tmp));
- },
- _popEl: function() {
- var self = this;
- for (var i = 0; i < self.infiniteLength; i++) {
- if (!self.infiniteElementsCache[i]._visible) {
- self.infiniteElementsCache[i]._visible = true;
- return {
- index: i,
- el: self.infiniteElements[i]
- }
- }
- }
- },
- _pushEl: function(guid) {
- var self = this;
- for (var i = 0; i < self.infiniteLength; i++) {
- if (self.infiniteElementsCache[i].guid == guid) {
- self.infiniteElementsCache[i]._visible = false;
- self.infiniteElements[i].style.visibility = "hidden";
- self.infiniteElementsCache[i].guid = null;
- }
- }
- },
- _renderData: function(el, elementObj) {
- var self = this;
- if (!el || !elementObj || elementObj.style.position == "fixed") return;
- self.userConfig.renderHook.call(self, el, elementObj);
- },
- _renderStyle: function(el, elementObj, useTransition) {
- var self = this;
- var _ = self._;
- if (!el) return;
- var translateZ = self.xscroll.userConfig.gpuAcceleration ? " translateZ(0) " : "";
- //update style
- for (var attrName in elementObj.style) {
- if (attrName != _.height && attrName != "display" && attrName != "position") {
- el.style[attrName] = elementObj.style[attrName];
- }
- }
- el.setAttribute("xs-index", elementObj.index);
- el.setAttribute("xs-sectionid", elementObj.sectionId);
- el.setAttribute("xs-guid", elementObj.guid);
- el.style.visibility = "visible";
- el.style[_.height] = elementObj[_._height] + "px";
- el.style[transform] = _.translate + "(" + elementObj[_._top] + "px) " + translateZ;
- el.style[transition] = useTransition ? self.userConfig.transition : "none";
- },
- getCell: function(e) {
- var self = this,
- cell;
- var el = Util.findParentEl(e.target, "._xs_infinite_elements_", self.xscroll.renderTo);
- if(!el){
- el = Util.findParentEl(e.target, ".xs-sticky-handler", self.xscroll.renderTo);
- }
- var guid = el && el.getAttribute("xs-guid");
- if (undefined === guid) return;
- return {
- data:self.__serializedData[guid],
- el:el
- };
- },
- _bindEvt: function() {
- var self = this;
- if (self._isEvtBinded) return;
- self._isEvtBinded = true;
- self.xscroll.renderTo.addEventListener("webkitTransitionEnd", function(e) {
- if (e.target.className.match(/xs-row/)) {
- e.target.style.webkitTransition = "";
- }
- });
- self.xscroll.on("scroll", self._updateByScroll, self);
- self.xscroll.on("tap panstart pan panend", self._cellEventsHandler, self);
- return self;
- },
- _cellEventsHandler: function(e) {
- var self = this;
- var cell = self.getCell(e);
- e.cell = cell.data;
- e.cellEl = cell.el;
- e.cell && self[e.type].call(self, e);
- },
- /**
- * tap event
- * @memberOf Infinite
- * @param {object} e events data include cell object
- * @event
- */
- tap: function(e) {
- this.trigger("tap", e);
- return this;
- },
- /**
- * panstart event
- * @memberOf Infinite
- * @param {object} e events data include cell object
- * @event
- */
- panstart: function(e) {
- this.trigger("panstart", e);
- return this;
- },
- /**
- * pan event
- * @memberOf Infinite
- * @param {object} e events data include cell object
- * @event
- */
- pan: function(e) {
- this.trigger("pan", e);
- return this;
- },
- /**
- * panend event
- * @memberOf Infinite
- * @param {object} e events data include cell object
- * @event
- */
- panend: function(e) {
- this.trigger("panend", e);
- return this;
- },
- /**
- * insert data before a position
- * @memberOf Infinite
- * @param {string} sectionId sectionId of the target cell
- * @param {number} index index of the target cell
- * @param {object} data data to insert
- * @return {Infinite}
- */
- insertBefore: function(sectionId, index, data) {
- var self = this;
- if (sectionId === undefined || index === undefined || data === undefined) return self;
- if (!self.sections[sectionId]) {
- self.sections[sectionId] = [];
- }
- self.sections[sectionId].splice(index, 0, data);
- return self;
- },
- /**
- * insert data after a position
- * @memberOf Infinite
- * @param {string} sectionId sectionId of the target cell
- * @param {number} index index of the target cell
- * @param {object} data data to insert
- * @return {Infinite}
- */
- insertAfter: function(sectionId, index, data) {
- var self = this;
- if (sectionId === undefined || index === undefined || data === undefined) return self;
- if (!self.sections[sectionId]) {
- self.sections[sectionId] = [];
- }
- self.sections[sectionId].splice(Number(index) + 1, 0, data);
- return self;
- },
- /**
- * append data after a section
- * @memberOf Infinite
- * @param {string} sectionId sectionId for the append cell
- * @param {object} data data to append
- * @return {Infinite}
- */
- append: function(sectionId, data) {
- var self = this;
- if (!self.sections[sectionId]) {
- self.sections[sectionId] = [];
- }
- self.sections[sectionId] = self.sections[sectionId].concat(data);
- return self;
- },
- /**
- * remove some data by sectionId,from,number
- * @memberOf Infinite
- * @param {string} sectionId sectionId for the append cell
- * @param {number} from removed index from
- * @param {number} number removed data number
- * @return {Infinite}
- */
- remove: function(sectionId, from, number) {
- var self = this;
- var number = number || 1;
- if (undefined === sectionId || !self.sections[sectionId]) return self;
- //remove a section
- if (undefined === from) {
- self.sections[sectionId] = null;
- return self;
- }
- //remove some data in section
- if (self.sections[sectionId] && self.sections[sectionId][from]) {
- self.sections[sectionId].splice(from, number);
- return self;
- }
- return self;
- },
- /**
- * replace some data by sectionId and index
- * @memberOf Infinite
- * @param {string} sectionId sectionId to replace
- * @param {number} index removed index from
- * @param {object} data new data to replace
- * @return {Infinite}
- */
- replace: function(sectionId, index, data) {
- var self = this;
- if (undefined === sectionId || !self.sections[sectionId]) return self;
- self.sections[sectionId][index] = data;
- return self;
- },
- /**
- * get data by sectionId and index
- * @memberOf Infinite
- * @param {string} sectionId sectionId
- * @param {number} index index in the section
- * @return {object} data data
- */
- get: function(sectionId, index) {
- if (undefined === sectionId) return;
- if (undefined === index) return this.sections[sectionId];
- return this.sections[sectionId][index];
- }
- });
- if (typeof module == 'object' && module.exports) {
- module.exports = Infinite;
- }
- /** ignored by jsdoc **/
- else if (window.XScroll && window.XScroll.Plugins) {
- return XScroll.Plugins.Infinite = Infinite;
- }
- });
|