index.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <uni-shadow-root class="vant-index-bar-index"><view class="van-index-bar">
  3. <slot></slot>
  4. <view v-if="showSidebar" class="van-index-bar__sidebar" @click.stop.prevent="onClick" @touchmove.stop.prevent="onTouchMove" @touchend.stop.prevent="onTouchStop" @touchcancel.stop.prevent="onTouchStop">
  5. <view v-for="(item,index) in (indexList)" :key="item.index" class="van-index-bar__index" :style="'z-index: '+(zIndex + 1)+'; color: '+(activeAnchorIndex === index ? highlightColor : '')" :data-index="index">
  6. {{ item }}
  7. </view>
  8. </view>
  9. </view></uni-shadow-root>
  10. </template>
  11. <script>
  12. global['__wxRoute'] = 'vant/index-bar/index'
  13. import { VantComponent } from '../common/component';
  14. import { GREEN } from '../common/color';
  15. const indexList = () => {
  16. const indexList = [];
  17. const charCodeOfA = 'A'.charCodeAt(0);
  18. for (let i = 0; i < 26; i++) {
  19. indexList.push(String.fromCharCode(charCodeOfA + i));
  20. }
  21. return indexList;
  22. };
  23. VantComponent({
  24. relation: {
  25. name: 'index-anchor',
  26. type: 'descendant',
  27. linked() {
  28. this.updateData();
  29. },
  30. linkChanged() {
  31. this.updateData();
  32. },
  33. unlinked() {
  34. this.updateData();
  35. }
  36. },
  37. props: {
  38. sticky: {
  39. type: Boolean,
  40. value: true
  41. },
  42. zIndex: {
  43. type: Number,
  44. value: 1
  45. },
  46. highlightColor: {
  47. type: String,
  48. value: GREEN
  49. },
  50. scrollTop: {
  51. type: Number,
  52. value: 0,
  53. observer: 'onScroll'
  54. },
  55. stickyOffsetTop: {
  56. type: Number,
  57. value: 0
  58. },
  59. indexList: {
  60. type: Array,
  61. value: indexList()
  62. }
  63. },
  64. data: {
  65. activeAnchorIndex: null,
  66. showSidebar: false
  67. },
  68. methods: {
  69. updateData() {
  70. this.timer && clearTimeout(this.timer);
  71. this.timer = setTimeout(() => {
  72. this.children = this.getRelationNodes('../index-anchor/index');
  73. this.setData({
  74. showSidebar: !!this.children.length
  75. });
  76. this.setRect().then(() => {
  77. this.onScroll();
  78. });
  79. }, 0);
  80. },
  81. setRect() {
  82. return Promise.all([
  83. this.setAnchorsRect(),
  84. this.setListRect(),
  85. this.setSiderbarRect()
  86. ]);
  87. },
  88. setAnchorsRect() {
  89. return Promise.all(this.children.map(anchor => (anchor.getRect('.van-index-anchor-wrapper').then((rect) => {
  90. Object.assign(anchor, {
  91. height: rect.height,
  92. top: rect.top + this.data.scrollTop
  93. });
  94. }))));
  95. },
  96. setListRect() {
  97. return this.getRect('.van-index-bar').then((rect) => {
  98. Object.assign(this, {
  99. height: rect.height,
  100. top: rect.top + this.data.scrollTop
  101. });
  102. });
  103. },
  104. setSiderbarRect() {
  105. return this.getRect('.van-index-bar__sidebar').then(res => {
  106. this.sidebar = {
  107. height: res.height,
  108. top: res.top
  109. };
  110. });
  111. },
  112. setDiffData({ target, data }) {
  113. const diffData = {};
  114. Object.keys(data).forEach(key => {
  115. if (target.data[key] !== data[key]) {
  116. diffData[key] = data[key];
  117. }
  118. });
  119. if (Object.keys(diffData).length) {
  120. target.setData(diffData);
  121. }
  122. },
  123. getAnchorRect(anchor) {
  124. return anchor.getRect('.van-index-anchor-wrapper').then((rect) => ({
  125. height: rect.height,
  126. top: rect.top
  127. }));
  128. },
  129. getActiveAnchorIndex() {
  130. const { children } = this;
  131. const { sticky, scrollTop, stickyOffsetTop } = this.data;
  132. for (let i = this.children.length - 1; i >= 0; i--) {
  133. const preAnchorHeight = i > 0 ? children[i - 1].height : 0;
  134. const reachTop = sticky ? preAnchorHeight + stickyOffsetTop : 0;
  135. if (reachTop + scrollTop >= children[i].top) {
  136. return i;
  137. }
  138. }
  139. return -1;
  140. },
  141. onScroll() {
  142. const { children = [] } = this;
  143. if (!children.length) {
  144. return;
  145. }
  146. const { sticky, stickyOffsetTop, zIndex, highlightColor, scrollTop } = this.data;
  147. const active = this.getActiveAnchorIndex();
  148. this.setDiffData({
  149. target: this,
  150. data: {
  151. activeAnchorIndex: active
  152. }
  153. });
  154. if (sticky) {
  155. let isActiveAnchorSticky = false;
  156. if (active !== -1) {
  157. isActiveAnchorSticky = children[active].top <= stickyOffsetTop + scrollTop;
  158. }
  159. children.forEach((item, index) => {
  160. if (index === active) {
  161. let wrapperStyle = '';
  162. let anchorStyle = `
  163. color: ${highlightColor};
  164. `;
  165. if (isActiveAnchorSticky) {
  166. wrapperStyle = `
  167. height: ${children[index].height}px;
  168. `;
  169. anchorStyle = `
  170. position: fixed;
  171. top: ${stickyOffsetTop}px;
  172. z-index: ${zIndex};
  173. color: ${highlightColor};
  174. `;
  175. }
  176. this.setDiffData({
  177. target: item,
  178. data: {
  179. active: true,
  180. anchorStyle,
  181. wrapperStyle
  182. }
  183. });
  184. }
  185. else if (index === active - 1) {
  186. const currentAnchor = children[index];
  187. const currentOffsetTop = currentAnchor.top;
  188. const targetOffsetTop = index === children.length - 1
  189. ? this.top
  190. : children[index + 1].top;
  191. const parentOffsetHeight = targetOffsetTop - currentOffsetTop;
  192. const translateY = parentOffsetHeight - currentAnchor.height;
  193. const anchorStyle = `
  194. position: relative;
  195. transform: translate3d(0, ${translateY}px, 0);
  196. z-index: ${zIndex};
  197. color: ${highlightColor};
  198. `;
  199. this.setDiffData({
  200. target: item,
  201. data: {
  202. active: true,
  203. anchorStyle
  204. }
  205. });
  206. }
  207. else {
  208. this.setDiffData({
  209. target: item,
  210. data: {
  211. active: false,
  212. anchorStyle: '',
  213. wrapperStyle: '',
  214. }
  215. });
  216. }
  217. });
  218. }
  219. },
  220. onClick(event) {
  221. this.scrollToAnchor(event.target.dataset.index);
  222. },
  223. onTouchMove(event) {
  224. const sidebarLength = this.children.length;
  225. const touch = event.touches[0];
  226. const itemHeight = this.sidebar.height / sidebarLength;
  227. let index = Math.floor((touch.clientY - this.sidebar.top) / itemHeight);
  228. if (index < 0) {
  229. index = 0;
  230. }
  231. else if (index > sidebarLength - 1) {
  232. index = sidebarLength - 1;
  233. }
  234. this.scrollToAnchor(index);
  235. },
  236. onTouchStop() {
  237. this.scrollToAnchorIndex = null;
  238. },
  239. scrollToAnchor(index) {
  240. if (typeof index !== 'number' || this.scrollToAnchorIndex === index) {
  241. return;
  242. }
  243. this.scrollToAnchorIndex = index;
  244. const anchor = this.children.filter(item => item.data.index === this.data.indexList[index])[0];
  245. this.$emit('select', anchor.data.index);
  246. anchor && wx.pageScrollTo({
  247. duration: 0,
  248. scrollTop: anchor.top
  249. });
  250. }
  251. }
  252. });
  253. export default global['__wxComponents']['vant/index-bar/index']
  254. </script>
  255. <style platform="mp-weixin">
  256. @import '../common/index.css';.van-index-bar{position:relative}.van-index-bar__sidebar{position:fixed;top:50%;right:0;display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;text-align:center;-webkit-transform:translateY(-50%);transform:translateY(-50%);-webkit-user-select:none;user-select:none}.van-index-bar__index{font-weight:500;padding:0 4px 0 16px;padding:0 var(--padding-base,4px) 0 var(--padding-md,16px);font-size:10px;font-size:var(--index-bar-index-font-size,10px);line-height:14px;line-height:var(--index-bar-index-line-height,14px)}
  257. </style>