sticky.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. define(function(require, exports, module) {
  2. "use strict";
  3. var Util = require('../util');
  4. var Base = require('../base');
  5. //transform
  6. var transform = Util.prefixStyle("transform");
  7. // default render function for position:sticky elements
  8. var defaultStickyRenderFunc = function(e) {
  9. var stickyElement = e.stickyElement;
  10. var curStickyElement = e.curStickyElement;
  11. var xscroll = e.xscroll;
  12. var _ = e._;
  13. var infinite = xscroll.getPlugin("infinite");
  14. if (infinite) {
  15. infinite.userConfig.renderHook.call(self, stickyElement, curStickyElement);
  16. stickyElement.setAttribute("xs-guid", curStickyElement.guid);
  17. Util.addClass(stickyElement, curStickyElement.className);
  18. for (var attrName in curStickyElement.style) {
  19. if (attrName != "display" && attrName != "position") {
  20. //copy styles
  21. stickyElement.style[attrName] = attrName == _.height ? curStickyElement.style[attrName] + 'px' : curStickyElement.style[attrName];
  22. }
  23. }
  24. } else {
  25. var style = curStickyElement.getAttribute("style");
  26. stickyElement.innerHTML = curStickyElement.innerHTML;
  27. stickyElement.className = curStickyElement.className;
  28. style && stickyElement.setAttribute("style", style);
  29. }
  30. }
  31. var Sticky = function(cfg) {
  32. Sticky.superclass.constructor.call(this, cfg);
  33. this.userConfig = Util.mix({
  34. stickyRenderTo: undefined,
  35. forceSticky: true,
  36. prefix: "xs-sticky-container",
  37. stickyRenderFunc: defaultStickyRenderFunc,
  38. zoomType: "y"
  39. }, cfg);
  40. this.init();
  41. }
  42. Util.extend(Sticky, Base, {
  43. init: function() {
  44. var self = this,
  45. userConfig = self.userConfig,
  46. xscroll = self.xscroll = userConfig.xscroll;
  47. var isY = self.isY = !!(userConfig.zoomType == "y");
  48. self._ = {
  49. top: self.isY ? "top" : "left",
  50. left: self.isY ? "left" : "bottom",
  51. right: self.isY ? "right" : "top",
  52. height: self.isY ? "height" : "width",
  53. width: self.isY ? "width" : "height"
  54. };
  55. self.stickyRenderTo = Util.getNode(userConfig.stickyRenderTo);
  56. self._handlers = [];
  57. return self;
  58. },
  59. getStickiesPos: function() {
  60. var self = this;
  61. var xscroll = self.xscroll;
  62. var isInfinite = self.isInfinite;
  63. var isY = self.isY;
  64. var _ = self._;
  65. var stickiesPos = [];
  66. var getPos = function(sticky) {
  67. var pos = {};
  68. if (isInfinite) {
  69. pos[_.top] = isY ? sticky._top : sticky._left;
  70. pos[_.height] = isY ? sticky._height : sticky._width;
  71. } else {
  72. pos[_.top] = self.isY ? Util.getOffsetTop(sticky) : Util.getOffsetLeft(sticky);
  73. pos[_.height] = self.isY ? sticky.offsetHeight : sticky.offsetWidth;
  74. }
  75. return pos;
  76. }
  77. for (var i = 0; i < self.stickiesNum; i++) {
  78. var pos = getPos(self.stickyElements[i]);
  79. self._handlers[i] = self._handlers[i] || self.createStickyEl();
  80. pos.el = self._handlers[i];
  81. pos.isRender = false;
  82. stickiesPos.push(pos);
  83. }
  84. return stickiesPos
  85. },
  86. getStickyElements: function() {
  87. var self = this;
  88. var xscroll = self.xscroll;
  89. var userConfig = self.userConfig;
  90. var isInfinite = self.isInfinite;
  91. var infinite = xscroll.getPlugin("infinite");
  92. if (infinite) {
  93. var stickyElements = [],
  94. serializedData = infinite.__serializedData;
  95. for (var i in serializedData) {
  96. var rowData = serializedData[i];
  97. if (rowData && rowData.style && "sticky" == rowData.style.position) {
  98. stickyElements.push(rowData);
  99. }
  100. }
  101. return stickyElements;
  102. } else {
  103. return Util.getNodes(xscroll.userConfig.stickyElements, xscroll.content);
  104. }
  105. },
  106. render: function(force) {
  107. var self = this;
  108. var userConfig = self.userConfig;
  109. var xscroll = self.xscroll;
  110. self.isInfinite = !!xscroll.getPlugin("infinite");
  111. var _ = self._;
  112. self.stickyElements = self.getStickyElements();
  113. self.stickiesNum = self.stickyElements && self.stickyElements.length;
  114. if (!self.stickiesNum) return;
  115. if (!self.stickyRenderTo) {
  116. self.stickyRenderTo = document.createElement('div');
  117. xscroll.renderTo.appendChild(self.stickyRenderTo);
  118. }
  119. self.stickiesPos = self.getStickiesPos();
  120. var stickyRenderTo = self.stickyRenderTo;
  121. stickyRenderTo.style[_.top] = 0;
  122. stickyRenderTo.style[_.left] = 0;
  123. stickyRenderTo.style[_.right] = 0;
  124. stickyRenderTo.style.position = xscroll.userConfig.useOriginScroll ? "fixed" : "absolute";
  125. Util.addClass(self.stickyRenderTo, userConfig.prefix);
  126. self.stickyHandler(force);
  127. self._bindEvt();
  128. },
  129. createStickyEl: function() {
  130. var self = this;
  131. var el = document.createElement('div');
  132. el.style.display = "none";
  133. Util.addClass(el, "xs-sticky-handler");
  134. self.stickyRenderTo.appendChild(el);
  135. return el;
  136. },
  137. _bindEvt: function() {
  138. var self = this,
  139. xscroll = self.xscroll;
  140. xscroll.on("scroll", self.stickyHandler, self);
  141. },
  142. stickyHandler: function(force) {
  143. var self = this;
  144. var xscroll = self.xscroll;
  145. var userConfig = self.userConfig;
  146. var scrollTop = self.isY ? xscroll.getScrollTop() : xscroll.getScrollLeft();
  147. var stickiesPos = self.stickiesPos;
  148. var _ = self._;
  149. var indexes = [];
  150. for (var i = 0, l = stickiesPos.length; i < l; i++) {
  151. var top = stickiesPos[i][_.top];
  152. if (scrollTop > top) {
  153. indexes.push(i);
  154. }
  155. }
  156. if (!indexes.length) {
  157. if (self.stickyElement) {
  158. self.stickyElement.style.display = "none";
  159. }
  160. self.curStickyIndex = undefined;
  161. return;
  162. }
  163. var curStickyIndex = Math.max.apply(null, indexes);
  164. if (self.curStickyIndex != curStickyIndex || force) {
  165. var prevStickyIndex = self.curStickyIndex;
  166. self.curStickyIndex = curStickyIndex;
  167. self.curStickyElement = self.stickyElements[curStickyIndex];
  168. self.curStickyPos = stickiesPos[curStickyIndex];
  169. self.stickyElement = self.curStickyPos.el;
  170. for (var i = 0, l = stickiesPos.length; i < l; i++) {
  171. stickiesPos[i].el.style.display = "none";
  172. }
  173. var eventsObj = {
  174. stickyElement: self.stickyElement,
  175. curStickyIndex: self.curStickyIndex,
  176. prevStickyIndex: prevStickyIndex,
  177. curStickyPos: self.curStickyPos,
  178. isRender: self.curStickyPos.isRender
  179. };
  180. xscroll.trigger("beforestickychange", eventsObj);
  181. self._stickyRenderFunc(self);
  182. xscroll.trigger("stickychange", eventsObj);
  183. }
  184. var trans = 0;
  185. if (self.stickiesPos[self.curStickyIndex + 1]) {
  186. var cur = self.stickiesPos[self.curStickyIndex];
  187. var next = self.stickiesPos[self.curStickyIndex + 1];
  188. if (scrollTop + cur[_.height] > next[_.top] && scrollTop + cur[_.height] < next[_.top] + cur[_.height]) {
  189. trans = cur[_.height] + scrollTop - next[_.top];
  190. } else {
  191. trans = 0;
  192. }
  193. }
  194. self.stickyElement.style[transform] = self.isY ? "translateY(-" + (trans) + "px) translateZ(0)" : "translateX(-" + (trans) + "px) translateZ(0)";
  195. },
  196. _stickyRenderFunc: function(e) {
  197. var self = this;
  198. var _ = self._;
  199. var stickyRenderFunc = self.userConfig.stickyRenderFunc;
  200. var el = self.curStickyPos.el;
  201. if (!self.curStickyPos.isRender) {
  202. el.style[_.left] = 0;
  203. el.style[_.right] = 0;
  204. stickyRenderFunc && stickyRenderFunc.call(self, e);
  205. }
  206. el.style.display = "block";
  207. self.curStickyPos.isRender = true;
  208. },
  209. destroy: function() {
  210. var self = this;
  211. self.stickyElements = undefined;
  212. self.stickiesNum = undefined;
  213. self.stickiesPos = undefined;
  214. Util.remove(self.stickyElement);
  215. self.stickyElement = undefined;
  216. }
  217. });
  218. if (typeof module == 'object' && module.exports) {
  219. module.exports = Sticky;
  220. }
  221. /** ignored by jsdoc **/
  222. else {
  223. return Sticky;
  224. }
  225. });