index.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Utils
  2. import { createNamespace, isDef } from '../utils';
  3. import { isHidden } from '../utils/dom/style';
  4. import { preventDefault } from '../utils/dom/event';
  5. import { getScroller, getScrollTop, getRootScrollTop, setRootScrollTop } from '../utils/dom/scroll'; // Mixins
  6. import { TouchMixin } from '../mixins/touch';
  7. import { ParentMixin } from '../mixins/relation';
  8. import { BindEventMixin } from '../mixins/bind-event';
  9. function genAlphabet() {
  10. var indexList = [];
  11. var charCodeOfA = 'A'.charCodeAt(0);
  12. for (var i = 0; i < 26; i++) {
  13. indexList.push(String.fromCharCode(charCodeOfA + i));
  14. }
  15. return indexList;
  16. }
  17. var _createNamespace = createNamespace('index-bar'),
  18. createComponent = _createNamespace[0],
  19. bem = _createNamespace[1];
  20. export default createComponent({
  21. mixins: [TouchMixin, ParentMixin('vanIndexBar'), BindEventMixin(function (bind) {
  22. if (!this.scroller) {
  23. this.scroller = getScroller(this.$el);
  24. }
  25. bind(this.scroller, 'scroll', this.onScroll);
  26. })],
  27. props: {
  28. zIndex: [Number, String],
  29. highlightColor: String,
  30. sticky: {
  31. type: Boolean,
  32. default: true
  33. },
  34. stickyOffsetTop: {
  35. type: Number,
  36. default: 0
  37. },
  38. indexList: {
  39. type: Array,
  40. default: genAlphabet
  41. }
  42. },
  43. data: function data() {
  44. return {
  45. activeAnchorIndex: null
  46. };
  47. },
  48. computed: {
  49. sidebarStyle: function sidebarStyle() {
  50. if (isDef(this.zIndex)) {
  51. return {
  52. zIndex: this.zIndex + 1
  53. };
  54. }
  55. },
  56. highlightStyle: function highlightStyle() {
  57. var highlightColor = this.highlightColor;
  58. if (highlightColor) {
  59. return {
  60. color: highlightColor
  61. };
  62. }
  63. }
  64. },
  65. watch: {
  66. indexList: function indexList() {
  67. this.$nextTick(this.onScroll);
  68. },
  69. activeAnchorIndex: function activeAnchorIndex(value) {
  70. if (value) {
  71. this.$emit('change', value);
  72. }
  73. }
  74. },
  75. methods: {
  76. onScroll: function onScroll() {
  77. var _this = this;
  78. if (isHidden(this.$el)) {
  79. return;
  80. }
  81. var scrollTop = getScrollTop(this.scroller);
  82. var scrollerRect = this.getScrollerRect();
  83. var rects = this.children.map(function (item) {
  84. return item.getRect(_this.scroller, scrollerRect);
  85. });
  86. var active = this.getActiveAnchorIndex(scrollTop, rects);
  87. this.activeAnchorIndex = this.indexList[active];
  88. if (this.sticky) {
  89. this.children.forEach(function (item, index) {
  90. if (index === active || index === active - 1) {
  91. var rect = item.$el.getBoundingClientRect();
  92. item.left = rect.left;
  93. item.width = rect.width;
  94. } else {
  95. item.left = null;
  96. item.width = null;
  97. }
  98. if (index === active) {
  99. item.active = true;
  100. item.top = Math.max(_this.stickyOffsetTop, rects[index].top - scrollTop) + scrollerRect.top;
  101. } else if (index === active - 1) {
  102. var activeItemTop = rects[active].top - scrollTop;
  103. item.active = activeItemTop > 0;
  104. item.top = activeItemTop + scrollerRect.top - rects[index].height;
  105. } else {
  106. item.active = false;
  107. }
  108. });
  109. }
  110. },
  111. getScrollerRect: function getScrollerRect() {
  112. if (this.scroller.getBoundingClientRect) {
  113. return this.scroller.getBoundingClientRect();
  114. }
  115. return {
  116. top: 0,
  117. left: 0
  118. };
  119. },
  120. getActiveAnchorIndex: function getActiveAnchorIndex(scrollTop, rects) {
  121. for (var i = this.children.length - 1; i >= 0; i--) {
  122. var prevHeight = i > 0 ? rects[i - 1].height : 0;
  123. var reachTop = this.sticky ? prevHeight + this.stickyOffsetTop : 0;
  124. if (scrollTop + reachTop >= rects[i].top) {
  125. return i;
  126. }
  127. }
  128. return -1;
  129. },
  130. onClick: function onClick(event) {
  131. this.scrollToElement(event.target);
  132. },
  133. onTouchMove: function onTouchMove(event) {
  134. this.touchMove(event);
  135. if (this.direction === 'vertical') {
  136. preventDefault(event);
  137. var _event$touches$ = event.touches[0],
  138. clientX = _event$touches$.clientX,
  139. clientY = _event$touches$.clientY;
  140. var target = document.elementFromPoint(clientX, clientY);
  141. if (target) {
  142. var index = target.dataset.index;
  143. /* istanbul ignore else */
  144. if (this.touchActiveIndex !== index) {
  145. this.touchActiveIndex = index;
  146. this.scrollToElement(target);
  147. }
  148. }
  149. }
  150. },
  151. scrollTo: function scrollTo(index) {
  152. var match = this.children.filter(function (item) {
  153. return String(item.index) === index;
  154. });
  155. if (match[0]) {
  156. match[0].scrollIntoView();
  157. if (this.sticky && this.stickyOffsetTop) {
  158. setRootScrollTop(getRootScrollTop() - this.stickyOffsetTop);
  159. }
  160. this.$emit('select', match[0].index);
  161. }
  162. },
  163. scrollToElement: function scrollToElement(element) {
  164. var index = element.dataset.index;
  165. this.scrollTo(index);
  166. },
  167. onTouchEnd: function onTouchEnd() {
  168. this.active = null;
  169. }
  170. },
  171. render: function render() {
  172. var _this2 = this;
  173. var h = arguments[0];
  174. var Indexes = this.indexList.map(function (index) {
  175. var active = index === _this2.activeAnchorIndex;
  176. return h("span", {
  177. "class": bem('index', {
  178. active: active
  179. }),
  180. "style": active ? _this2.highlightStyle : null,
  181. "attrs": {
  182. "data-index": index
  183. }
  184. }, [index]);
  185. });
  186. return h("div", {
  187. "class": bem()
  188. }, [h("div", {
  189. "class": bem('sidebar'),
  190. "style": this.sidebarStyle,
  191. "on": {
  192. "click": this.onClick,
  193. "touchstart": this.touchStart,
  194. "touchmove": this.onTouchMove,
  195. "touchend": this.onTouchEnd,
  196. "touchcancel": this.onTouchEnd
  197. }
  198. }, [Indexes]), this.slots('default')]);
  199. }
  200. });