index.js 4.6 KB

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