scroll-view左右联动

究极干货,完美实现微信小程序商品左右联动scroll-view的实现及性能优化,点击左边,右边滚动;右边滚动,左边也对应变化

究极干货,完美实现微信小程序商品左右联动scroll-view的实现及性能优化,点击左边,右边滚动;右边滚动,左边也对应变化

微信小程序左右联动,点击左边右边滚动,滑动右边左边滚动及改变状态

微信小程序左右联动,点击左边右边滚动,滑动右边左边滚动及改变状态

如上图,在小程序中,我们经常需要一个功能,就是点击左边的列表选项,右边对应的内容滚动,而滑动右边的内容,左边对应的改变状态及位置,特别是商城站基本都涉及到,那么我们该怎么实现呢,这里我们就需要用到小程序的scroll-view这个组件;

先看代码

第一步,点击左边右边会对应滚动:

这个比较简单,利用scroll-view的属性scroll-into-view="",当我们点击左侧列表子项时,就把子项的id赋值给croll-into-view,就可以实现点击左侧,右侧滚动了,

第二步,滑动右侧,左侧高亮且滚动到可见区域:

这个才是左右联动的核心所在:那该怎么实现呢,思路是:scroll-view 有监听事件bindscroll,我们在监听事件里监听右侧内容滚动的高度,进而判断当前是属于那一块区域。在bindscroll事件里我们可以直接得到scrollTop当前滚动的高度,但是我怎么判断这个高度输入第几类商品呢,这个就需要得到右侧每一类商品的高度,然而每一类商品的高度不是写死的,由数据渲染的,有的数据多一点,高度就多一点,那该怎么获取呢,这个就需要我们用到小程序的另一个api wx.createSelectorQuery(),

获取右侧商品分类的高度代码:

var that=this;

var h=0;

var heightArr=[];

wx.createSelectorQuery().selectAll(’.sc_right_item’).boundingClientRect(function (rect) {//selectAll会选择所要含有该类名的盒子

}).exec(function (res) {

res[0].forEach((item)=>{

h+=item.height;

heightArr.push(h);

})

that.setData({heightArr:heightArr})

});

这样我们就得到右侧商品的分类的高度了,如上面获得的高度heightArr是一个数组,heightArr[0]就是第一类商品的高度(我这里是菜品1的高度),而heightArr[1]就是第二类商品的高度加上第一类商品的高度,以此类推;这里获取的高度单位为px;正好和scrollTop的单位也是px;所以我们不需要在rpx和px之间进行换算;

然后右边滑动,左边对应高亮及滚动代码:

上面代码我们何以看到:左侧的active状态通过cp_index=i来实现,而左侧滚动的位置由leftTop=i*左侧子项的高度来实现(左侧子项的高度用wx.createSelectorQuery()来获取,不能是写死的,因为scroll-top="{{leftTop}}" 的值是px,所有需要获取,不然用写死的rpx,就要每个手机都要进行换算)

性能优化代码:

想想看,我们右边每滑动一下,Scroll-view 的监听就执行了好多下(比如次数为n),在加上每次执行的过程中又执行for循环(比如次数为m),那么我们每滑动一下就必须setData的次数=n*m;性能可想而知,肯定会卡顿,那么怎么避免了,我们只需要在特定范围内执行一次,比如在0-500的高度内执行一次,那么我们就得另加判断,如下

第一类商品: 一 开始,我设oneShow=true,当它执行一次的时候就赋值为false,所以在第一类商品高度区域内只执行一次,如果到达第二类以上,就让oneShow=true回来,这样回滚的时候它又能执行;

第二类商品以上: 初始值zindex=0;如果不等于当前i值就让它执行,然后让它=i;第二次及而二次以上就不再执行,当它=i+1时又执行一次,然后在这个阶段就不再执行,以此类推

源码如下:

wxml:

左右联动例子

      <scroll-view scroll-y="true" class="scr_left" scroll-top="{{leftTop}}" scroll-with-animation="true">

      <block wx:for="{{leftData}}" wx:for-item="lcai" wx:key="index">

        <view class="sc_left_item {{cp_index==index? 'active':''}}"  data-id="c_{{lcai.id}}" data-index="{{index}}" bindtap="leftTap" >

          {{lcai.name}}

        </view>

        </block>

      </scroll-view>



    <scroll-view scroll-y="true" class="scr_right" scroll-into-view="{{currentScrollId}}" scroll-with-animation="true" bindscroll="bindscroll">

      <block wx:for="{{rightData}}" wx:for-item="rcai" wx:key="index">

        <view class="sc_right_item" id="c_{{rcai.id}}"  data-id="c_{{rcai.id}}" data-index="{{index}}" bindtap="rightTap" >

        <text>{{rcai.name}}</text>

          <view class="images_wrap">

          <image wx:for="{{rcai.img}}" wx:key="index" src="{{item}}"></image>

          </view>

        </view>

        </block>

      </scroll-view>

wxss:

page{

width: 100%;

height: 100%;

}

.containner{

display: flex;

flex-direction: column;

width: 100%;

height: 100%;

}

.top{

width: 100%;

height: 50rpx;

display: flex;

justify-content: center;

align-items: center;

border: 1px solid #dbdbdb;

}

.cont{

display: flex;

justify-content: space-between;

width: 100%;

height: 100%;

}

.scr_left{

border-right: 1px solid #999;

width: 100rpx;

height: 100%;

box-sizing: border-box;

}

.scr_right{

width:500rpx;

height: 100%;

}

.sc_left_item{

width: 100rpx;

height: 200rpx;

display: flex;

align-items: center;

justify-content: center;

color: #333;

border-bottom: 2px solid #dbdbdb;

}

.sc_right_item{

margin-bottom: 30rpx;

}

.images_wrap{

width: 100%;

display: flex;

flex-wrap: wrap;

}

.sc_right_item image{width: 50%;display: block}

.active{

color: red;

border-bottom: 2px solid red;

}

js:

//index.js

//获取应用实例

const app = getApp()

Page({

data: {

currentScrollId:'',

cp_index:0,

leftTop:0,

left_item_height:0,

leftData:[

  {

    name:'菜品1',

    id:'cp1'

  },

  {

    name: '菜品2',

    id: 'cp2'

  },

  {

    name: '菜品3',

    id: 'cp3'

  },

  {

    name: '菜品4',

    id: 'cp4'

  },

  {

    name: '菜品5',

    id: 'cp5'

  },

  {

    name: '菜品6',

    id: 'cp6'

  },

  {

    name: '菜品7',

    id: 'cp7'

  },

  {

    name: '菜品8',

    id: 'cp8'

  },

  {

    name: '菜品9',

    id: 'cp9'

  },

  {

    name: '菜品10',

    id: 'cp10'

  },

  {

    name: '菜品11',

    id: 'cp11'

  },

  {

    name: '菜品12',

    id: 'cp12'

  }

],

rightData: [

  {

    name: '菜品1',

    id: 'cp1',

    img:[

      '../../image/cp.jpg',

    ]

  },

  {

    name: '菜品2',

    id: 'cp2',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

    ]

  },

  {

    name: '菜品3',

    id: 'cp3',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

    ]

  },

  {

    name: '菜品4',

    id: 'cp4',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品5',

    id: 'cp5',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品6',

    id: 'cp6',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品7',

    id: 'cp7',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品8',

    id: 'cp8',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品9',

    id: 'cp9',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品10',

    id: 'cp10',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品11',

    id: 'cp11',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  },

  {

    name: '菜品12',

    id: 'cp12',

    img: [

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg',

      '../../image/cp.jpg'

    ]

  }

],

heightArr:0,

zindex:0,

oneShow:true

},

onLoad:function(){

},

onReady:function(){

var that=this;

var h=0;

var heightArr=[];

wx.createSelectorQuery().select('.sc_left_item').boundingClientRect(function (rect) { //select会选择第一个类目的盒子

}).exec(function (res) {

  that.setData({ left_item_height: res[0].height })



});

wx.createSelectorQuery().selectAll('.sc_right_item').boundingClientRect(function (rect) {//selectAll会选择所要含有该类名的盒子

}).exec(function (res) {

  res[0].forEach((item)=>{

      h+=item.height;

      heightArr.push(h);

  })

  that.setData({heightArr:heightArr})

})

},

leftTap:function(e){

var index=e.currentTarget.dataset.index;

var id = e.currentTarget.dataset.id;

this.setData({ cp_index: index, currentScrollId:id})

},

bindscroll:function(e){

var zindex = this.data.zindex;

var oneShow=this.data.oneShow;

let  scrollTop = e.detail.scrollTop;

let  scrollArr = this.data.heightArr;

  for  (let  i = 0; i < scrollArr.length; i++) {

    if  (scrollTop >= 0  && scrollTop < scrollArr[0]) {

      if (oneShow){

      console.log('==============aaa'  + scrollTop + "=="  + scrollArr[0]);

      this.setData({

        cp_index: 0,

        leftTop: 0,

        zindex:0,

        oneShow:false

      })

      return

      }

    }  else  if  (scrollTop >= (scrollArr[i - 1]) && scrollTop < scrollArr[i]) {

      if (i != zindex){

        console.log('==============bbb' + i + scrollTop + "==" + scrollArr[i]);

      this.setData({

        oneShow: true,

        zindex:i,

        cp_index: i,

        leftTop: i * this.data.left_item_height

      })



    }

}

  }

}

})

源码,也可以直接打开链接在打开小程序开发工具即可查看:https://developers.weixin.qq.com/s/t7uVgOmT7gae

————————————————

版权声明:本文为CSDN博主「疯!不会停息-春哥」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_42120767/article/details/100030269

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 如果想实现多个scroll-view组件同步滚动,可以在每个scroll-view组件的滚动事件中通过设置scrollTop或scrollLeft属性来控制另一个scroll-view组件的滚动位置。这样每个scroll-view组件的滚动都会触发另一个scroll-view组件的滚动,但是如果内容很多的话可能会引起性能问题。 为了解决这个问题,可以采用以下方法: 1. 使用预渲染,将预先渲染的内容预先加载进缓存中,在滚动时不再重新渲染,提高滚动性能。 2. 减少DOM元素的数量,通过虚拟滚动或分页加载的方式来实现滚动,只渲染当前显示的内容,减少需要渲染的内容。 以下是代码示例: ``` <template> <view class="page"> <scroll-view class="scroll-view" :scroll-top="scrollTop" @scroll="handleScroll" > <!-- 内容 --> </scroll-view> <scroll-view class="scroll-view" :scroll-top="scrollTop" > <!-- 内容 --> </scroll-view> </view> </template> <script> export default { data() { return { scrollTop: 0 }; }, methods: { handleScroll(e) { this.scrollTop = e.detail.scrollTop; } } }; </script> ``` ### 回答2: 实现滑动同步的方法有两种,一种是使用监听滚动事件的方式,另一种是使用双向绑定的方式。 第一种方法是,监听scroll-view组件的滚动事件,获取滚动的位置,在滚动时同时设置另一个scroll-view组件的滚动位置,代码示例如下: ```javascript // scroll-view组件1的滚动事件 scroll1(e) { // 获取当前滚动的位置 let scrollLeft = e.detail.scrollLeft; // 设置scroll-view组件2的滚动位置 this.setData({ scrollLeft2: scrollLeft }); } ``` ```html <!-- scroll-view组件1 --> <scroll-view scroll-x="true" bindscroll="scroll1"> <!-- 内容 --> </scroll-view> <!-- scroll-view组件2 --> <scroll-view scroll-x="true" scroll-left="{{scrollLeft2}}"> <!-- 内容 --> </scroll-view> ``` 第二种方法是使用双向绑定的方式,将scroll-view组件1的滚动位置绑定到一个变量上,然后再将这个变量绑定到scroll-view组件2的滚动位置上,代码示例如下: ```javascript // 设置初始的滚动位置 data: { scrollLeft1: 0 }, // scroll-view组件1的滚动事件 scroll1(e) { // 获取当前滚动的位置 let scrollLeft = e.detail.scrollLeft; // 更新滚动位置 this.setData({ scrollLeft1: scrollLeft }); } ``` ```html <!-- scroll-view组件1 --> <scroll-view scroll-x="true" bindscroll="scroll1" scroll-left="{{scrollLeft1}}"> <!-- 内容 --> </scroll-view> <!-- scroll-view组件2 --> <scroll-view scroll-x="true" scroll-left="{{scrollLeft1}}"> <!-- 内容 --> </scroll-view> ``` 根据实际情况选择其中一种方法,即可实现在H5页面滑动X轴时,一个scroll-view组件的滑动的时候,另一个scroll-view组件也能同时移动。如果内容较多导致卡顿,可以考虑优化滚动性能,如减少渲染的元素数量或使用虚拟滚动等方法。 ### 回答3: 要解决这个问题,可以使用uniapp提供的监听事件和动态控制scroll-view滚动的方法。 首先,给两个scroll-view组件分别添加id属性,例如scroll-view1和scroll-view2。 然后,在页面的js代码中,监听scroll-view1的滚动事件。当scroll-view1发生滚动时,获取其滚动的距离,并将该距离赋值给scroll-view2的scrollTop属性,即实现了两个scroll-view组件的同步滚动。代码示例如下: ```javascript // 页面的js代码 // 监听scroll-view1的滚动事件 onScroll(event) { // 获取scroll-view1滚动的距离 let scrollTop = event.detail.scrollTop; // 将滚动的距离赋值给scroll-view2 uni.pageScrollTo({ scrollTop: scrollTop, duration: 0 }); } ``` 接下来,在scroll-view1的标签上添加滚动事件。代码示例如下: ```html <scroll-view id="scroll-view1" scroll-y style="height: 300rpx;overflow: hidden;" @scroll="onScroll"> <!-- scroll-view1的内容 --> </scroll-view> ``` 最后,在scroll-view2的标签上设置scrollTop属性,让其根据scroll-view1的滚动而滚动。代码示例如下: ```html <scroll-view id="scroll-view2" scroll-y style="height: 300rpx;overflow: hidden;" :scrollTop="scrollTop"> <!-- scroll-view2的内容 --> </scroll-view> ``` 这样,当滑动scroll-view1时,scroll-view2也会同步滚动,实现了两个scroll-view组件的联动

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值