index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. Component({
  2. options: {
  3. multipleSlots: true,
  4. addGlobalClass: true
  5. },
  6. properties: {
  7. list: {
  8. type: Array
  9. },
  10. active: {
  11. type: Number,
  12. value: 0
  13. },
  14. onChenge: {
  15. type: Function
  16. },
  17. showIcon: {
  18. type: Boolean,
  19. value: true
  20. },
  21. anchor: {
  22. type: Number,
  23. value: 0.4 // 锚点位置,0-1,表示选中项中心在容器宽度的比例位置
  24. },
  25. mode: {
  26. type: String,
  27. value: 'scroll' // 'scroll' 滚动模式, 'flat' 平铺模式
  28. }
  29. },
  30. data: {
  31. scrollLeft: 0,
  32. startPoint: 0,
  33. slipFlag: false,
  34. },
  35. lifetimes: {
  36. attached: function () {
  37. getApp().globalData.Language.getLanguagePackage(this);
  38. },
  39. ready: function () {
  40. this.setActive();
  41. }
  42. },
  43. methods: {
  44. tabsChenge(e) {
  45. const { index } = e.currentTarget.dataset;
  46. if (this.data.active == index) return;
  47. this.setData({
  48. active: index
  49. }, () => {
  50. this.setActive();
  51. });
  52. },
  53. setActive() {
  54. const that = this;
  55. const active = this.data.active;
  56. const anchor = this.data.anchor;
  57. const query = wx.createSelectorQuery().in(this);
  58. query.select('.scroll').fields({ rect: true, scrollOffset: true })
  59. .select(`#active${active}`).boundingClientRect()
  60. .selectAll('.item').boundingClientRect()
  61. .exec((res) => {
  62. const container = res[0];
  63. const elem = res[1];
  64. const items = res[2];
  65. if (!container || !elem || !items || items.length === 0) return;
  66. // 容器宽度通过 right - left 计算
  67. const containerWidth = container.right - container.left;
  68. const containerLeft = container.left;
  69. const currentScrollLeft = container.scrollLeft || 0;
  70. const firstItem = items[0];
  71. const lastItem = items[items.length - 1];
  72. // 第一个元素相对于内容左边界的偏移(通常为0)
  73. const firstItemLeftContent = firstItem.left - containerLeft + currentScrollLeft;
  74. // 最后一个元素右边界相对于内容左边界的偏移
  75. const lastItemRightContent = lastItem.left - containerLeft + currentScrollLeft + lastItem.width;
  76. const contentWidth = lastItemRightContent - firstItemLeftContent;
  77. const viewportWidth = containerWidth;
  78. const maxScrollLeft = Math.max(0, contentWidth - viewportWidth);
  79. // 选中元素在内容中的左偏移(相对于第一个元素)
  80. const elemLeftContent = elem.left - containerLeft + currentScrollLeft - firstItemLeftContent;
  81. // 目标:元素中心位于可视区域宽度 * anchor 处
  82. const targetElemLeftInViewport = viewportWidth * anchor - elem.width / 2;
  83. let targetScrollLeft = elemLeftContent - targetElemLeftInViewport;
  84. // 边界限制
  85. if (targetScrollLeft < 0) targetScrollLeft = 0;
  86. if (targetScrollLeft > maxScrollLeft) targetScrollLeft = maxScrollLeft;
  87. // 避免微小移动
  88. if (Math.abs(targetScrollLeft - currentScrollLeft) < 1) return;
  89. that.setData({
  90. scrollLeft: targetScrollLeft
  91. });
  92. });
  93. this.triggerEvent("onChenge", active);
  94. },
  95. myTouchStart(e) {
  96. return; // 禁用滑动切换
  97. this.setData({
  98. slipFlag: true,
  99. startPoint: e.touches[0]
  100. })
  101. },
  102. myTouchMove(e) {
  103. return; // 禁用滑动切换
  104. let active = this.data.active;
  105. if (((this.data.startPoint.clientX - e.touches[e.touches.length - 1].clientX) > 80) && this.data.slipFlag) {
  106. if (active != this.data.list.length - 1) active++;
  107. this.setData({
  108. slipFlag: false,
  109. active
  110. })
  111. } else if (((this.data.startPoint.clientX - e.touches[e.touches.length - 1].clientX) < -80) && this.data.slipFlag) {
  112. if (active != 0) active--;
  113. this.setData({
  114. slipFlag: false,
  115. active
  116. })
  117. }
  118. this.setActive();
  119. },
  120. }
  121. });