仿写京东购物小程序之我的界面——头部渐变效果的实现

2,251 阅读6分钟

昨天在仿写京东购物小程序,它的“我的”界面头部的下拉渐变效果很酷,于是研究了一下,向大家分享一下我的研究历程~~

1、京东购物小程序之我的界面

具体的效果是这样的(要稍微等等加载,下面的是动图):

jingdong_me.gif

2、整体布局

看动图就知道,京东购物小程序的头部导航栏肯定不是小程序默认的头部,通过在小程序的.json配置文件的window项中加入 "navigationStyle":"custom" 就可以自定义小程序顶部导航栏啦;

分析一下“我的”界面,可以看出头部导航栏的背景图片和下面的图片肯定是同一张图片,只不过是分割了一下,一部分放上面,一部分放下面;

image-20210731180526179

我们通过css的background放置背景图片和对背景图片进行定位,可以轻松的将图片放在两个不同的盒子里而拼成一张完整的图片;

并且我们在动图里可以看到界面主体是一个可以滚动的区域,而实现滚动使用小程序的scroll-view组件就可以了~

于是有如下布局:

image-20210731174916109

3、渐变方案

基本框架搭起来了,那怎样实现让头部导航栏跟随主体滚动发生渐变呢。只要监听界面主体的滚动事件,获取当前的滚动位置,然后根据位置改变头部导航栏的背景的状态就行了;

实际上小程序的scroll-view提供了监听滚动事件的方法,我们只需要写一个js函数就就可以实时获得界面主体当前的位置;

获取界面主体的当前滚动位置后,怎样让导航栏的背景发生改变呢?我首先想到的是下面的方案:

  1. 一个背景图片,一个背景颜色,然后背景颜色逐渐显现,逐渐遮挡背景图片;

    (实际上此方案不行,因为css的background属性的background-colorbackground-image之间无法自定义层次,背景颜色在背景图片的下方,无法让背景颜色跑到上面来;)

后面我想到了使用css的opacity属性,我可以通过设置opacity:0来让背景图片逐渐消失;并且小程序使用Mustache方法提供了页面和js的数据绑定方法;所以最后可以将 头部导航栏的背景状态界面主体的当前滚动位置 绑定起来;

效果如下(要稍微等等加载,下面的是动图):

jingdong_me_head.gif

以下是代码:

<!--me.wxml文件-->
<view class="page">
    
  <!--头部导航--> 
  <view class="page-hd">
    <view class="hd-background" style="opacity: {{opacity}};"></view>
    <!--因为opacity会导致盒子内的所有内容的透明度都发生变化,所以头部导航栏的名字用另外一个view装,然后使用position让其脱离文档流,定位到hd-background背景图片的上方-->
    <view class="hd-text">我的</view>
  </view>
    
  <!--界面主体-->  
  <view class="page-bd">
   <!--注意只有scroll-view里内容的高度超过了scroll-view的高度时,scroll-view才能倍滚动-->
   <scroll-view scroll-y style="height: 90vh;" bindscroll="scrollPage">
    <view class="me-container">
      <view class="me-hd">1</view>
      <view class="me-bd">2</view>
    </view>
    </scroll-view>
  </view>
    
</view>
/*me.wxss文件*/
.page{
  height: 100vh;
  width: 100vw;
  display: flex;
  flex-direction: column;
  
}
​
/* 界面头部 */
.page-hd{
  height: 10vh;
  width: 100%;
}
.hd-background{
  width: 100%;
  height: 100%;
  background: url(https://636c-cloud1-5gfii8jlc56b5045-1306536140.tcb.qcloud.la/wxfile/background.png?sign=6a62c2c13c024f0091a1f8034f4d6155&t=1627552695) ;
  background-repeat: no-repeat;
  background-size: 100vw;
}
.hd-text{
  position: relative;
  width: 100%;
  text-align: center;
  top: -50rpx;
​
}
​
/* 界面主体 */
.page-bd{
  flex:1;
  
}
.me-container{
  height: 100%;
  width: 100%;
}
.me-hd{
  
  width: 100vw;
  height: 40vh;
  background-image: url(https://636c-cloud1-5gfii8jlc56b5045-1306536140.tcb.qcloud.la/wxfile/background.png?sign=6a62c2c13c024f0091a1f8034f4d6155&t=1627552695);
  background-repeat: no-repeat;
  background-size: contain;
  background-position: 0 -10vh;
}
.me-bd{
  height: 100vh;
}
// me.js文件
data: {
  opacity:1
  
},
scrollPage(e){
  let scrolltop = e.detail.scrollTop;
  
  
  if(scrolltop > 150){
    this.setData({
      opacity:0
    })
    return ;
  }
​
  this.setData({
    opacity: (150 - scrolltop) / 150
  })
},

4、测试

如上,对其测试后发现有点小BUG,当缓慢的上下滚动时,头部导航栏的背景图片会流畅的变化,但是当迅速下滚时页面主体已经到顶了,头部导航栏却无法恢复无透明度的状态,如下:

BUG.gif

经过测试,发现迅速下拉并在中间手指脱离触摸屏后,虽然滚动区域已经到顶部,但scroll-viewscroll 事件并没有跟随屏幕的滚动而发生;

如下测试每次 scroll 事件发生时的 scrollTop 参数,最后一次scroll事件发生时scrollTop的值为113,而并没有变为0;

image-20210802084308822

因为在我写的js中opacity是和scroll事件发生时返回的事件对象的scrollTop参数的大小有关的,只有scrollTop为 0 时opacity才为 1,所以此时 头部导航栏 的透明度并没有变为 1;

5、改进

发现BUG后我进行了如下多种尝试:

  1. 改用binddragging绑定滑动事件

    binddraggingbindscroll 的区别在于binddragging绑定的是滑动事件,bindscroll绑定的是滚动事件。而滑动事件和滚动事件的区别在于滑动事件只能在移动端生效,在PC端无法滑动;滚动事件可以在PC端滚动,并且滑动事件和滚动事件在移动端的表现效果一致;

    因此改用binddragging也无法解决问题;

  2. 在使用 bindscroll绑定滚动事件的基础上增加 bindtoupper 绑定滚动到顶事件

    没用,快速下拉时,bindtoupper 事件也不会被触发;

  1. 在使用slectorQuery对象的方法后

    使用slectorQuery对象的select方法获取节点距离屏幕顶部的高度,并根据高度动态改变头部导航栏的opacity,这依然需要滚动事件触发才能起作用;

  2. 将scroll-viewscroll-top使用数据双向绑定

    没用,scroll-view无法双向绑定;

  3. 有没有方法实时获取滚动的位置或者实时获取 me-hd 对应的view组件距离顶部的高度 的 方法

    没有找到,onLoad()nShow()onReady()等事件监听函数都只在整个页面生命周期执行一次;

  4. 关闭滚动惯性

    没用,只有在ios端才能关闭滚动惯性,况且如果关闭滚动惯性将极大影响用户体验,所以不能关闭滚动惯性;

  5. 通过bindscroll绑定的滚动事件的变化,在js中获取其变化趋势和逻辑,如果能判断其是迅速下拉,就将头部导航栏的opacity设置为1;

    此方法也是需要bindscroll事件发生后,记录每次事件对象返回的参数并统计它们的变化规律,这中方法依靠于事件对象返回的数据,数据的急剧变化并不一定和急剧向下滚动到顶的动作唯一对应;因此此方法也不够好;


最后结论:

经过长达一天的研究,BUG的根本原因还是在于 bindscroll事件在快速向下滚动时并没有被触发, 在微信开发者社区也看到了有人有同样的问题:bindscrolltolower快速滚动时偶尔会不触发?

一个猜想:

在小程序里捕获滚动事件被节流处理了,以至于我下拉滚动(scroll)的速度快过了捕获滚动的速度,所以导致视图回到了顶部,下拉滚动(scroll)事件却并没有被捕获;

可是我测试了京东购物小程序快速下拉滚动时,头部导航栏的背景图片可以恢复~_~;有没有京东的小程序开发人员来解个惑!

6、扩展资料

  1. scroll-view 的使用;
  2. slectorQuery对象;