index.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. const throttle = function (func, wait, options) {
  2. let context
  3. let args
  4. let result
  5. let timeout = null
  6. // 上次执行时间点
  7. let previous = 0
  8. if (!options) options = {}
  9. // 延迟执行函数
  10. const later = function () {
  11. // 若设定了开始边界不执行选项,上次执行时间始终为0
  12. previous = options.leading === false ? 0 : Date.now()
  13. timeout = null
  14. result = func.apply(context, args)
  15. if (!timeout) context = args = null
  16. }
  17. return function () {
  18. const now = Date.now()
  19. // 首次执行时,如果设定了开始边界不执行选项,将上次执行时间设定为当前时间。
  20. if (!previous && options.leading === false) previous = now
  21. // 延迟执行时间间隔
  22. const remaining = wait - (now - previous)
  23. context = this
  24. args = arguments
  25. // 延迟时间间隔remaining小于等于0,表示上次执行至此所间隔时间已经超过一个时间窗口
  26. // remaining大于时间窗口wait,表示客户端系统时间被调整过
  27. if (remaining <= 0 || remaining > wait) {
  28. clearTimeout(timeout)
  29. timeout = null
  30. previous = now
  31. result = func.apply(context, args)
  32. if (!timeout) context = args = null
  33. // 如果延迟执行不存在,且没有设定结尾边界不执行选项
  34. } else if (!timeout && options.trailing !== false) {
  35. timeout = setTimeout(later, remaining)
  36. }
  37. return result
  38. }
  39. }
  40. Component({
  41. options: {
  42. addGlobalClass: true,
  43. pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段
  44. },
  45. properties: {
  46. list: {
  47. type: Array,
  48. value: [],
  49. observer: function (newVal) {
  50. if (newVal.length === 0) return
  51. const data = this.data
  52. const alphabet = data.list.map(item => item.alpha)
  53. this.setData({
  54. alphabet,
  55. current: alphabet[0]
  56. }, () => {
  57. this.computedSize()
  58. })
  59. }
  60. },
  61. vibrated: {
  62. type: Boolean,
  63. value: true
  64. }
  65. },
  66. data: {
  67. windowHeight: 612,
  68. current: 'A',
  69. intoView: '',
  70. touching: false,
  71. alphabet: [],
  72. _tops: [],
  73. _anchorItemH: 0,
  74. _anchorItemW: 0,
  75. _anchorTop: 0,
  76. _listUpperBound: 0
  77. },
  78. lifetimes: {
  79. created() {
  80. },
  81. attached() {
  82. this.__scrollTo = throttle(this._scrollTo, 100, {})
  83. this.__onScroll = throttle(this._onScroll, 100, {})
  84. const { windowHeight } = wx.getSystemInfoSync()
  85. this.setData({ windowHeight })
  86. }
  87. },
  88. methods: {
  89. handleSelectAlpha(current) {
  90. this.setData({ current, intoView: current })
  91. },
  92. choose(e) {
  93. const item = e.target.dataset.item
  94. this.triggerEvent('choose', { item })
  95. },
  96. scrollTo(e) {
  97. this.__scrollTo(e)
  98. },
  99. _scrollTo(e) {
  100. const data = this.data
  101. const clientY = e.changedTouches[0].clientY
  102. const index = Math.floor((clientY - data._anchorTop) / data._anchorItemH)
  103. const current = data.alphabet[index]
  104. this.setData({ current, intoView: current, touching: true })
  105. // 振动效果
  106. if (data.vibrated) wx.vibrateShort()
  107. },
  108. computedSize() {
  109. const data = this.data
  110. // 计算列表每个区块的高度等信息
  111. const query = this.createSelectorQuery()
  112. query.selectAll('.index_list_item').boundingClientRect(rects => {
  113. const result = rects
  114. data._tops = result.map(item => item.top)
  115. }).exec()
  116. // 计算右侧字母栏小区块的高度等信息
  117. query.select('.anchor-list').boundingClientRect(rect => {
  118. data._anchorItemH = rect.height / data.alphabet.length
  119. data._anchorItemW = rect.width
  120. data._anchorTop = rect.top
  121. }).exec()
  122. // 计算滚动区域的上边界
  123. query.select('.page-select-index').boundingClientRect(rect => {
  124. data._listUpperBound = rect.top
  125. })
  126. },
  127. // throttle 的延迟
  128. removeTouching() {
  129. setTimeout(() => {
  130. this.setData({ touching: false })
  131. }, 150)
  132. },
  133. onScroll(e) {
  134. this.__onScroll(e)
  135. },
  136. _onScroll(e) {
  137. const data = this.data
  138. const { _tops, alphabet } = data
  139. const scrollTop = e.detail.scrollTop
  140. let current = ''
  141. if (scrollTop < _tops[0]) {
  142. current = alphabet[0]
  143. } else {
  144. for (let i = 0, len = _tops.length; i < len - 1; i++) {
  145. if (scrollTop >= _tops[i] && scrollTop < _tops[i + 1]) {
  146. current = alphabet[i]
  147. }
  148. }
  149. }
  150. if (!current) current = alphabet[alphabet.length - 1]
  151. this.setData({ current })
  152. }
  153. }
  154. })