uniapp上实现左右关联滚动

文章展示了如何在uni-app中优化onPageScroll事件处理,避免在滚动事件中执行复杂的交互逻辑,改为在滚动结束时执行,提高性能。通过设置定时器在滚动停止后更新selectedId,并利用itemTopArr数组实现内容区域的同步滚动效果。
摘要由CSDN通过智能技术生成

先看效果:

在这里插入图片描述

代码:

<template>
  <view class="container">
    <!-- 左侧fixed导航区域 -->
    <view class="left">
      <view
        v-for="item in leftList"
        :key="item.id"
        class="left_item"
        :class="item.id == selectedId ? 'selected' : ''"
        @click="leftItemClick(item.id)"
      >
        {{ item.title }}
      </view>
    </view>

    <!-- 右侧内容区域 -->
    <view class="right">
      <view v-for="item in rightList" :key="item.id" class="right_item">
        {{ item.content }}
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      leftList: [],
      rightList: [],
      selectedId: 1, //左边当前选中id
      itemTopArr: [], //右侧所有item的top数组
    };
  },
  onLoad() {
    this.initData();
    this.getItemTopArr();
  },
  //页面滚动监听
  onPageScroll(e) {
    let scrollTop = e.scrollTop;
    console.log("scrollTop = " + scrollTop);
    for (let i = 0; i < this.itemTopArr.length; i++) {
      if (scrollTop >= this.itemTopArr[i]) {
        this.selectedId = this.rightList[i].parentId;
        console.log("selectedId = " + this.selectedId);
      }
    }
  },
  methods: {
    //左侧item点击
    leftItemClick(id) {
      this.selectedId = id;

      let index = 0;
      for (let i = 0; i < this.rightList.length; i++) {
        if (this.rightList[i].parentId == id) {
          index = i;
          break;
        }
      }

      //将页面滚动到目标位置
      uni.pageScrollTo({
        scrollTop: this.itemTopArr[index],
        duration: 300, //滚动动画时长
      });
    },
    //获取右侧所有item的top数组
    getItemTopArr() {
      this.$nextTick(() => {
        const query = uni.createSelectorQuery().in(this);
        const nodesRef = query.selectAll(".right > .right_item");
        nodesRef
          .fields(
            {
              size: true,
              rect: true,
              scrollOffset: true,
            },
            (res) => {
              res.forEach((item) => {
                this.itemTopArr.push(item.top);
              });
              console.log(this.itemTopArr);
            }
          )
          .exec();
      });
    },
    //初始化数据源
    initData() {
      for (let index = 1; index < 10; index++) {
        for (let i = 1; i < 4; i++) {
          this.rightList.push({
            id: index + "-" + i,
            parentId: index,
            content: "content-" + index,
          });
        }
        this.leftList.push({
          id: index,
          title: "title-" + index,
        });
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.container {
  position: relative;
  min-height: 100vh;
  background: #fff;

  .left {
    position: fixed;
    width: 120px;
    height: 100%;
    min-height: 100vh;
    overflow: auto;
    float: left;
    background: #f2f2f2;

    .left_item {
      width: 100%;
      height: 60px;
      text-align: center;
      line-height: 60px;
    }

    .selected {
      background: #fff;
      font-weight: bold;
      color: #07c160;
    }
  }

  .right {
    margin-left: 120px;
    width: calc(100vw - 120px);
    overflow: auto;

    .right_item {
      width: 100%;
      height: 200px;
      text-align: center;
      line-height: 200px;
      font-size: 24px;
      border-bottom: 1px solid #ccc;
      box-sizing: border-box; //padding、border不影响元素的宽高
    }
  }
}
</style>


在这里插入图片描述

官方onPageScroll方法的使用注意事项(见上图)里说不要在此方法里写复杂的交互,故将onPageScroll里的for循环改写到滚动结束执行:

<template>
  <view class="container">
    <!-- 左侧fixed导航区域 -->
    <view class="left">
      <view
        v-for="item in leftList"
        :key="item.id"
        class="left_item"
        :class="item.id == selectedId ? 'selected' : ''"
        @click="leftItemClick(item.id)"
      >
        {{ item.title }}
      </view>
    </view>

    <!-- 右侧内容区域 -->
    <view class="right">
      <view v-for="item in rightList" :key="item.id" class="right_item">
        {{ item.content }}
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      leftList: [],
      rightList: [],
      selectedId: 1, //左边当前选中id
      itemTopArr: [], //右侧所有item的top数组
      timeoutID: null,
    };
  },
  onLoad() {
    this.initData();
    this.getItemTopArr();
  },
  //页面滚动监听
  onPageScroll(e) {
    let scrollTop = e.scrollTop;
    console.log("scrollTop = " + scrollTop);

    clearTimeout(this.timeoutID);
    this.timeoutID = setTimeout(() => {
      console.log("结束滚动");

      for (let i = 0; i < this.itemTopArr.length; i++) {
        if (scrollTop >= this.itemTopArr[i]) {
          this.selectedId = this.rightList[i].parentId;
          console.log("selectedId = " + this.selectedId);
        }
      }
    }, 100);
  },
  methods: {
    //左侧item点击
    leftItemClick(id) {
      this.selectedId = id;

      let index = 0;
      for (let i = 0; i < this.rightList.length; i++) {
        if (this.rightList[i].parentId == id) {
          index = i;
          break;
        }
      }

      //将页面滚动到目标位置
      uni.pageScrollTo({
        scrollTop: this.itemTopArr[index],
        duration: 300, //滚动动画时长
        success: (res) => {
          console.log(res);
          console.log("scroll success");
        },
        fail: (err) => {
          console.log(err);
          console.log("scroll fail");
        },
      });
    },
    //获取右侧所有item的top数组
    getItemTopArr() {
      this.$nextTick(() => {
        const query = uni.createSelectorQuery().in(this);
        const nodesRef = query.selectAll(".right > .right_item");
        nodesRef
          .fields(
            {
              size: true,
              rect: true,
              scrollOffset: true,
            },
            (res) => {
              res.forEach((item) => {
                this.itemTopArr.push(item.top);
              });
              console.log(this.itemTopArr);
            }
          )
          .exec();
      });
    },
    //初始化数据源
    initData() {
      for (let index = 1; index < 10; index++) {
        for (let i = 1; i < 4; i++) {
          this.rightList.push({
            id: index + "-" + i,
            parentId: index,
            content: "content-" + index,
          });
        }
        this.leftList.push({
          id: index,
          title: "title-" + index,
        });
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.container {
  position: relative;
  min-height: 100vh;
  background: #fff;

  .left {
    position: fixed;
    width: 120px;
    height: 100%;
    min-height: 100vh;
    overflow: auto;
    float: left;
    background: #f2f2f2;

    .left_item {
      width: 100%;
      height: 60px;
      text-align: center;
      line-height: 60px;
    }

    .selected {
      background: #fff;
      font-weight: bold;
      color: #07c160;
    }
  }

  .right {
    margin-left: 120px;
    width: calc(100vw - 120px);
    overflow: auto;

    .right_item {
      width: 100%;
      height: 200px;
      text-align: center;
      line-height: 200px;
      font-size: 24px;
      border-bottom: 1px solid #ccc;
      box-sizing: border-box; //padding、border不影响元素的宽高
    }
  }
}
</style>

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
uniApp的ScrollView组件可以实现左右联动效果,主要是通过JavaScript或者Vue相关的API来操作。你可以通过以下几个步骤来创建这样的联动: 1. **设置基础结构**:首先,在页面上创建两个水平方向的ScrollView,例如`<scroll-view class="scroll-view-left">...</scroll-view>` 和 `<scroll-view class="scroll-view-right">...</scroll-view>`。 2. **绑定数据**:将这两个滚动视图关联到数据属性上,比如`data.leftScroll`和`data.rightScroll`,并控制它们的滚动位置。 3. **监听事件**:在左侧滚动视图上添加滚动事件监听器,当左侧滚动改变时,更新右侧滚动视图的相应位置。这通常会涉及到计算当前左侧滚动的位置,并基于此调整右侧的初始位置。 ```vue <template> <div> <scroll-view class="scroll-view-left" :scroll-y="true" @scrolltoupper="onLeftScrollToUpper"> <!-- ...左侧内容... --> </scroll-view> <scroll-view class="scroll-view-right" :scroll-x="true" :scroll-top="leftScrollPos" @scrolltolower="onRightScrollToLower"> <!-- ...右侧内容... --> </scroll-view> </div> </template> <script> export default { data() { return { leftScroll: 0, leftScrollPos: 0, // 记录左侧滚动的位置用于联动右侧 }; }, methods: { onLeftScrollToUpper(e) { this.leftScroll = e.detail.scrollTop; this.leftScrollPos = this.leftScroll; // 更新右侧初始位置 }, onRightScrollToLower(e) { // 右侧下拉后,需要更新左侧 // 在这里处理右侧滚动到底部后的联动逻辑 } }, }; </script> ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值