写一个uniapp跟随点击项滚动的scrollview组件

232 阅读1分钟

项目中经常出现一个横向scroll-view套了一层列表,需要配套做到点击列表项时scrollview滚动到相应位置,因为这种情况比较多,所以封装了一个scrollview组件,主要原理是通过计算点击项的位置更新scrollleft的改变

  <scroll-view
    scroll-x
    id="scrollContainer"
    class="scroll-to-view"
    :scroll-left="scrollLeft"
    scroll-with-animation
    @touchmove.stop.prevent="() => {}"
  >
    <view
      class="scroll-to-view-item"
      v-for="(item, inx) in list"
      :key="inx"
      :style="[value === inx ? activeStyle : customStyle]"
      @click="checkItem(inx)"
      >{{ item[field] }}</view
    >
  </scroll-view>
</template>
<script>
/**
 * @property {Number} value 选中项的下标
 * @property {Array} list 列表
 * @property {String} field='name' 显示对应列表中字段
 * @property {Object} customStyle 列表项样式 
 * @property {Object} activeStyle 项样式 
 *
 * @event {Function(current)} change 改变标签触发
 */
export default {
  props: {
    list: {
      type: Array,
      default() {
        return [];
      },
    },
    value: {
      type: Number,
      default() {
        return 0;
      },
    },
    field: {
      type: String,
      default() {
        return 'name';
      },
    },
    customStyle: {
      type: Object,
      default() {
        return {};
      },
    },
    activeStyle: {
      type: Object,
      default() {
        return {
          fontSize: '28rpx',
          color: '#000',
          fontWeight: 'bold',
          background: '#f1f1f1',
        };
      },
    },
  },
  data() {
    return {
      containerWidth: 0,
      scrollLeft: 0,
    };
  },
  watch: {
    value(val) {
      this.$nextTick(() => {
        this.scrollIntoView(val);
      });
    },
  },
  methods: {
    checkItem(inx) {
      this.$emit('check', inx);
    },
    scrollIntoView(val) {
      const query = uni
        .createSelectorQuery()
        // #ifndef MP-ALIPAY
        .in(this);
      // #endif
      // 获取容器的宽度
      query
        .select('#scrollContainer')
        .boundingClientRect((data) => {
          this.containerWidth = data.width;
        })
        .exec();
      // 累加内部项长度和最后一项长度的一半,设置最后一项滚动到最中间
      query
        .selectAll('.scroll-to-view-item')
        .boundingClientRect((data) => {
          if (!data) {
            return;
          }
          let lineLeft = 0;
          let currentWidth = 0;
          if (data) {
            for (let i = 0; i < data.length; i++) {
              if (i < val) {
                lineLeft += data[i].width;
              } else if (i == val) {
                currentWidth = data[i].width;
              } else {
                break;
              }
            }
          }
          lineLeft = lineLeft + currentWidth / 2;
          this.scrollLeft = lineLeft - this.containerWidth / 2;
        })
        .exec();
    },
  },
};
  1. 组件可以复用,可以用customStyle和activeStyle自定义组件样式
  2. uni.createSelectorQuery()在各端可以替换为my.createSelectorQuery或wx.createSelectorQuery
  3. 列表过长如果产生性能问题,可以使用event.target.left的方式避免query.selectAll和长度计算.