草庐IT

scroll-view左右联动

zZ_d205 2023-03-28 原文

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

image

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

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

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

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

先看代码

image

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

这个比较简单,利用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之间进行换算;

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

image

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

性能优化代码:

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

image

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

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

源码如下:

wxml:

<view class="containner">

  <view class="top">左右联动例子</view>

  <view class="cont">

          <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>

  </view>

</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

有关scroll-view左右联动的更多相关文章

  1. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  2. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  3. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  4. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  5. ruby-on-rails - 复数 for fields_for has_many 关联未显示在 View 中 - 2

    目前,Itembelongs_toCompany和has_manyItemVariants。我正在尝试使用嵌套的fields_for通过Item表单添加ItemVariant字段,但是使用:item_variants不显示该表单。只有当我使用单数时才会显示。我检查了我的关联,它们似乎是正确的,这可能与嵌套在公司下的项目有关,还是我遗漏了其他东西?提前致谢。注意:下面的代码片段中省略了不相关的代码。编辑:不知道这是否相关,但我正在使用CanCan进行身份验证。routes.rbresources:companiesdoresources:itemsenditem.rbclassItemi

  6. ruby-on-rails - 在 haml View 中重构条件 - 2

    除了可访问性标准不鼓励使用这一事实指向当前页面的链接,我应该怎么做重构以下View代码?#navigation%ul.tabbed-ifcurrent_page?(new_profile_path)%li{:class=>"current_page_item"}=link_tot("new_profile"),new_profile_path-else%li=link_tot("new_profile"),new_profile_path-ifcurrent_page?(profiles_path)%li{:class=>"current_page_item"}=link_tot("p

  7. ruby - Sinatra 找不到 View 目录 - 2

    我正在尝试以一种更类似于普通RubyGem结构的方式构建我的Sinatra应用程序。我有以下文件树:.├──app.rb├──config.ru├──Gemfile├──Gemfile.lock├──helpers│  ├──dbconfig.rb│  ├──functions.rb│  └──init.rb├──hidden│  └──Rakefile├──lib│  ├──admin.rb│  ├──api.rb│  ├──indexer.rb│  ├──init.rb│  └──magnet.rb├──models│  ├──init.rb│  ├──invite.rb│  ├─

  8. ruby-on-rails - 如何让 Rails View 返回其关联的操作名称? - 2

    我有一个非常简单的Controller来管理我的Rails应用程序中的静态页面:classPagesController我怎样才能让View模板返回它自己的名字,这样我就可以做这样的事情:#pricing.html.erb#-->"Pricing"感谢您的帮助。 最佳答案 4.3RoutingParametersTheparamshashwillalwayscontainthe:controllerand:actionkeys,butyoushouldusethemethodscontroller_nameandaction_nam

  9. ruby-on-rails - 如何从按钮或链接单击的 View 调用 Rails 方法 - 2

    基本上,我试图在用户单击链接(或按钮或某种类型的交互元素)时执行Rails方法。我试着把它放在View中:但这似乎没有用。它最终只是在用户甚至没有点击“添加”链接的情况下调用该函数。我也用link_to试过了,但也没用。我开始认为没有一种干净的方法可以做到这一点。无论如何,感谢您的帮助。附言。我在ApplicationController中定义了该方法,它是一个辅助方法。 最佳答案 View和Controller是相互独立的。为了使链接在Controller内执行函数调用,您需要对应用程序中的端点执行ajax调用。该路由应调用rub

  10. ruby-on-rails - 如何编写跨模型、 Controller 和 View 的 Rails mixin - 2

    为了减少我的小Rails应用程序中的代码重复,我一直致力于将我的模型之间的通用代码放入它自己的单独模块中,到目前为止一切顺利。模型的东西相当简单,我只需要在开头包含模块,例如:classIso这工作正常,但是现在,我将有一些Controller和View代码,这些代码也将在这些模型之间通用,到目前为止,我有这个用于我的可发送内容:#Thisisamodulethatisusedforpages/formsthatarecanbe"sent"#eitherviafax,email,orprinted.moduleSendablemoduleModeldefself.included(kl

随机推荐