昨天在仿写京东购物小程序,它的“我的”界面头部的下拉渐变效果很酷,于是研究了一下,向大家分享一下我的研究历程~~
1、京东购物小程序之我的界面
具体的效果是这样的(要稍微等等加载,下面的是动图):
2、整体布局
看动图就知道,京东购物小程序的头部导航栏肯定不是小程序默认的头部,通过在小程序的.json配置文件的window项中加入 "navigationStyle":"custom"
就可以自定义小程序顶部导航栏啦;
分析一下“我的”界面,可以看出头部导航栏的背景图片和下面的图片肯定是同一张图片,只不过是分割了一下,一部分放上面,一部分放下面;
我们通过css的background
放置背景图片和对背景图片进行定位,可以轻松的将图片放在两个不同的盒子里而拼成一张完整的图片;
并且我们在动图里可以看到界面主体是一个可以滚动的区域,而实现滚动使用小程序的scroll-view
组件就可以了~
于是有如下布局:
3、渐变方案
基本框架搭起来了,那怎样实现让头部导航栏跟随主体滚动发生渐变呢。只要监听界面主体的滚动事件,获取当前的滚动位置,然后根据位置改变头部导航栏的背景的状态就行了;
实际上小程序的scroll-view
提供了监听滚动事件的方法,我们只需要写一个js函数就就可以实时获得界面主体当前的位置;
获取界面主体的当前滚动位置后,怎样让导航栏的背景发生改变呢?我首先想到的是下面的方案:
-
一个背景图片,一个背景颜色,然后背景颜色逐渐显现,逐渐遮挡背景图片;
(实际上此方案不行,因为css的
background
属性的background-color
和background-image
之间无法自定义层次,背景颜色在背景图片的下方,无法让背景颜色跑到上面来;)
后面我想到了使用css的opacity
属性,我可以通过设置opacity:0
来让背景图片逐渐消失;并且小程序使用Mustache方法提供了页面和js的数据绑定方法;所以最后可以将 头部导航栏的背景状态 和 界面主体的当前滚动位置 绑定起来;
效果如下(要稍微等等加载,下面的是动图):
以下是代码:
<!--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,当缓慢的上下滚动时,头部导航栏的背景图片会流畅的变化,但是当迅速下滚时页面主体已经到顶了,头部导航栏却无法恢复无透明度的状态,如下:
经过测试,发现迅速下拉并在中间手指脱离触摸屏后,虽然滚动区域已经到顶部,但scroll-view
的 scroll
事件并没有跟随屏幕的滚动而发生;
如下测试每次 scroll
事件发生时的 scrollTop
参数,最后一次scroll
事件发生时scrollTop
的值为113,而并没有变为0;
因为在我写的js中opacity
是和scroll
事件发生时返回的事件对象的scrollTop
参数的大小有关的,只有scrollTop
为 0 时opacity
才为 1,所以此时 头部导航栏 的透明度并没有变为 1;
5、改进
发现BUG后我进行了如下多种尝试:
-
改用
binddragging
绑定滑动事件binddragging
和bindscroll
的区别在于binddragging
绑定的是滑动事件,bindscroll
绑定的是滚动事件。而滑动事件和滚动事件的区别在于滑动事件只能在移动端生效,在PC端无法滑动;滚动事件可以在PC端滚动,并且滑动事件和滚动事件在移动端的表现效果一致;因此改用
binddragging
也无法解决问题; -
在使用
bindscroll
绑定滚动事件的基础上增加bindtoupper
绑定滚动到顶事件没用,快速下拉时,
bindtoupper
事件也不会被触发;
-
在使用
slectorQuery
对象的方法后使用
slectorQuery
对象的select方法获取节点距离屏幕顶部的高度,并根据高度动态改变头部导航栏的opacity
,这依然需要滚动事件触发才能起作用; -
将s
croll-view
的scroll-top
使用数据双向绑定没用,
scroll-view
无法双向绑定; -
有没有方法实时获取滚动的位置或者实时获取
me-hd
对应的view组件距离顶部的高度 的 方法没有找到,
onLoad()
,nShow()
,onReady()
等事件监听函数都只在整个页面生命周期执行一次; -
关闭滚动惯性
没用,只有在ios端才能关闭滚动惯性,况且如果关闭滚动惯性将极大影响用户体验,所以不能关闭滚动惯性;
-
通过
bindscroll
绑定的滚动事件的变化,在js中获取其变化趋势和逻辑,如果能判断其是迅速下拉,就将头部导航栏的opacity
设置为1;此方法也是需要
bindscroll
事件发生后,记录每次事件对象返回的参数并统计它们的变化规律,这中方法依靠于事件对象返回的数据,数据的急剧变化并不一定和急剧向下滚动到顶的动作唯一对应;因此此方法也不够好;
最后结论:
经过长达一天的研究,BUG的根本原因还是在于 bindscroll事件在快速向下滚动时并没有被触发, 在微信开发者社区也看到了有人有同样的问题:bindscrolltolower快速滚动时偶尔会不触发?
一个猜想:
在小程序里捕获滚动事件被节流处理了,以至于我下拉滚动(scroll)的速度快过了捕获滚动的速度,所以导致视图回到了顶部,下拉滚动(scroll)事件却并没有被捕获;
可是我测试了京东购物小程序快速下拉滚动时,头部导航栏的背景图片可以恢复~_~;有没有京东的小程序开发人员来解个惑!