一、需求说明
使用微信小程序原生代码,实现如上图的交互效果。具体要求如下:
- 当手指触发(tap / touch) Tab区块时,页面自动向上滚动,滚动至视窗的顶部。
- Tab区块,最多只能滚动到视窗的顶部,不可以继续向上滚动。
- 当用户向下滑动时,Tab区块正常向下滚动。
- 当Tab区块里的内容超出一屏的高度时,只能在 Tab区块的内部进行滚动。
二、封装自定义组件
根据以上需求,使用原生小程序组件和代码,封装一个
- 当Tab区块置顶时,
组件的高度必须等于屏幕视窗的高度,因此将使用 wx.getSystemInfo
来动态获取屏幕尺寸(单位是px
),通过计算后,手动地设置自定义组件和 原生组件
的高度,分别是h1
和h2
。详细代码见下方的myswiper.js
。 - 小程序原生组件
是有默认高度的,必须手动设置其高度h2
。 -
默认是position:absolute;overflow:hidden;
的,我们必须更改其默认样式(见下方的myswiper.wxss
文件),否则当
内部内容较多时会被隐藏掉。另外,为了让
中的内容滚动更流畅,还要为其加上-webkit-overflow-scrolling:touch;
属性。 - Tab项可能多于3个,或者是 4个,因此建议使用
flex
来动态布局,以确保 Tabs的数量是可变的。 - 使用
slot
插槽来动态渲染 Tab区块中的内容,在组合使用
来定义插槽。 - 为
组件绑定 tap``touch
事件,并判断当用户点击、上滑时,让页面滚动的位置。详细逻辑见下方的 myswiper.js
文件。
# myswiper.wxml
{{item}}
# myswiper.wxss
.myswiper {
position: relative;
/* height: 1200rpx; */
background: white;
}
.tabs{
width: 100%;
height: 100rpx;
background-color: #eeeeee;
display: flex;
}
.tab{
flex: 1;
font-size:28rpx;
font-family:PingFangSC;
font-weight:400;
color:rgba(102,102,102,1);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-weight: bold;
}
.tab>text{
display: block;
}
.on .tab-line{
width:90rpx;
height:4rpx;
background:rgba(255,117,56,1);
border-radius:3rpx;
margin-top: 5rpx;
}
.on .tab-title{
font-size:32rpx;
font-family:PingFangSC;
font-weight:500;
color:rgba(255,117,56,1);
}
.swiper swiper-item {
/* 开启swiper-item滚动,touch流畅滚动 */
overflow: scroll;
-webkit-overflow-scrolling:touch;
}
# myswiper.js
let startY = 0;
Component({
options: {
multipleSlots: true // 启用多slot支持
},
properties: {
tabArr: { /* Tab项的名称列表 */
type: Array,
value: []
}
},
data: {
h1: '', // my-swiper 组件的高度
h2: '', // swiper 组件的高度
curIdx: 0 // tab 索引号
},
attached: function() {
const that = this
// 获取信息,动态设置组件高度
wx.getSystemInfo({
success: function(e) {
const w = e.windowWidth
const h = e.windowHeight
// 100 是Tab的默认高度,单位rpx
// 把 rpx 转化为 px
const tabH = 100 * w / h
that.setData({ h1: h, h2: h - tabH })
}
});
},
methods: {
/* my-swiper 组件的系列事件 */
tabTouch: function(e) {
// 当用户点击my-swiper组件时,将其置顶
const top = e.currentTarget.offsetTop
// 滚动到顶部
wx.pageScrollTo({scrollTop: top});
},
touchStart: function(e) {
startY = e.changedTouches[0].clientY
console.log('start', startY)
},
touchEnd: function(e) {
const endY = e.changedTouches[0].clientY
// 当手势向上滑动时,把my-swiper组件置顶
if (startY - endY > 10) {
const top = e.currentTarget.offsetTop
wx.pageScrollTo({scrollTop: top});
}
console.log('end', endY)
},
// 点击标签事件
tabClick:function(e){
let data = e.currentTarget.dataset;
this.setData({ curIdx: data.index })
},
// swiper切换事件
swiperChange:function(e){
this.setData({ curIdx:e.detail.current })
},
}
})
# myswiper.json
{ "component": true }
三、使用
组件,进行测试
组件封装好了,测试代码如下。我们只需要传递一个 tabArr 标签列表进去即可。再使用 slot
插槽动态地插入多个区块的内容。
# demo.json
{
"navigationBarTitleText": "自定义Swiper组件及特效",
"usingComponents": {
"my-swiper": "/component/myswiper/myswiper"
}
}
# demo.wxml
人物
自然
汽车
美女
# demo.wxss
.other image {
display: block;
width: 100%;
}
.content {
font-size: 80rpx;
text-align: center;
line-height: 200rpx;
}
# demo.js
Page({
data: {},
onLoad: function() {},
})
四、总结
组件的封装,填平了一些坑,比如 swiper
默认的样式问题。通过这个组件,我们至少学到了如下前端知识:
- 什么是组件?如何自定义组件?
- 如何使用小程序的原生组件,并修改其默认的样式?
- 学会使用 slot 插槽,实现组件内容的差异化
- 学会使用小程序原生 api 获取手机信息,用 js 改变组件样式
- 学会使用 touch 事件,区分
e.touches
和e.changedTouches
等 - 学会
px
和rpx
单位之间的区别和相互转化 - 学会使用小程序 api 来手动地滚动页面,等。
以上完整代码,复制黏贴即可实现这个效果,相关要点已经在代码中进行了注释。事实上,要总结的内容还有很多,时间有限就不啰嗦了。好了,本篇就总结到这里。
往期 相关的小程序demo笔记:
- 手动实现 Swiper 视窗滑动效果
- 微信小程序 模拟打电话 实践
- 微信小程序 Animation 动画实践
- 小程序Canvas画布保存至相册
- CSS动画|JavaScript动画|小程序动画
本篇结束 2019-08-27