微信小程序--导航吸顶+view锚点
导航栏滑动定位+view锚点
我们在做小程序的时候,难免会遇到页面里面有很长的数据,页面下面又有到导航栏部分;用户希望页面滑动到导航栏的位置,导航栏固定在页面的最上面,同时里面的数变化,也能改变导航栏的高亮。
效果图
图1:
图2:
图3:
实现原理
通过对scroll的监听获取滚动条的scrollTop值;
在导航的下子集class判断距离整个页面Top;
切换position:fixed与position:relative。
WXML
我是使用自定义组件来写的
<rui-sticky scroll="{{curNodes}}" bindstickyscroll="getScrollTop">
<view slot="stickychild">
<view class='flex-row border_b mar-t bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list1</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<view class='flex-row border_b bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list2</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<view class='flex-row border_b bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list3</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<view class='flex-row border_b bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list4</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<view class='flex-row border_b bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list5</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<view class='flex-row border_b bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list6</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<view class='flex-row border_b bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list7</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<view class='flex-row border_b bg_white uinn'>
<view class='flex1 flex-row'>
<view class='s_width1 colorTitle'>list8</view>
<input type='text' class='flex1' value='' disabled='true' />
</view>
</view>
<!-- 导航栏组件 -->
<rui-content navcur='{{navcur}}' bind:compontpass="compontpass" bind:myevent='myevent'> </rui-content>
</view>
</rui-sticky>
WXSS主页面
//所有的css
.rui-flex-content {
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.rui-nav-flex {
background-color: #fff;
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 1000;
}
.rui-goods-content {
padding-top: 4vw;
}
.rui-nav-li text {
display: -webkit-flex;
display: flex;
justify-content: center;
align-items: center;
color: #969799;
border-bottom: 4px solid #fff;
}
.rui-nav-li {
font-size: 30rpx;
display: -webkit-flex;
display: flex;
justify-content: center;
flex: 0 0 auto;
width: 25%;
height: 88rpx;
}
.rui-nav-li.rui-active text {
font-size: 30rpx;
color: #323233;
border-bottom: 4px solid #7a6cf9;
}
.rui-flex-li {
width: 100%;
margin: 0 0 4vw 0;
}
.rui-flex-li:last-child {
width: 100%;
margin: 0 0 128rpx 0;
}
.imgline {
width: 100%;
height: 400rpx;
}
.imgline image {
width: 100%;
height: 100%;
}
/* .rui-flex-li view {
height: 45rpx;
line-height: 45rpx;
color: #000;
} */
.bacred {
padding: 20rpx 0;
background: #fff;
border-radius: 4px;
}
.vertical {
float: left;
display: block;
width: 8rpx;
height: 30rpx;
margin: 10rpx auto;
background: linear-gradient(360deg, rgba(139, 115, 250, 1) 0%, rgba(122, 108, 249, 1) 100%);
border-radius: 4px;
}
.vertical_tit {
font-weight: 500;
font-size: 32rpx;
color: #9970ed;
margin-left: 10rpx;
height: 45rpx;
line-height: 45rpx;
float: left;
display: block;
}
.ovh {
overflow: hidden;
}
.liucheng {
position: relative;
width: 100%;
border: 1px solid #969799;
}
.liucheng image {
width: 100%;
height: 100%;
}
.fullscreen {
text-align: center;
position: absolute;
bottom: 20rpx;
right: 20rpx;
width: 104rpx;
height: 52rpx;
line-height: 52rpx;
background: rgba(255, 255, 255, 1);
border-radius: 26px;
border: 1px solid rgba(220, 222, 227, 1);
color: #333;
font-size: 28rpx;
}
.uinn {
padding: 20rpx;
}
.footsubmit {
z-index: 999999;
background-color: #fff;
width: 100%;
position: fixed;
bottom: 0px;
box-shadow:0px -2px 10px 0px rgba(239,239,239,0.5);
}
.succ_btn {
margin-top: 0rpx;
margin-bottom: 20rpx;
background-color: #9970ed;
color: white;
font-size: 36rpx;
}
.notavailable{
color: #969799;
font-size: 30rpx;
}
/* upload */
.flex25{
width: 25%;
word-wrap:break-word;
word-break:break-all;
}
.border-radius{
border-radius: 6rpx;
}
.bg_eb{
background-color: #EBEBEB;
}
.lineh{
line-height: 50rpx;
}
.exceptionimgbox{
width: 140rpx;
height: 140rpx;
margin: auto;
vertical-align: middle;
position: relative;
}
.exceptionimgbox .add{
width: 30%;
height: 100%;
display: block;
margin: auto;
}
.exceptionimgbox image,.exceptionimgbox video{
width: 100%;
height: 100%;
display: block;
}
.exceptionimgbox image.delimg{
position: absolute;
right: -10rpx;
top: -10rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.3);
z-index: 999;
}
.dingwei{
width: 40rpx;
height: 40rpx;
}
.xline{
text-decoration: underline;
}
.exceptionimgbox icon{
position: absolute;
top: 36%;
left: 36%;
}
.flex-rows{
-webkit-display:flex;
display:flex;
flex-direction: row;
flex-wrap: wrap;
-webkit-align-items:center;
align-items:center;
/* -webkit-justify-content:center;
justify-content:center; */
}
/* sticky */
.rui-sticky-content{
height: 100%;
}
JS-主页面
var main = require('../../utils/util.js');
Page({
/**
* 页面的初始数据
*/
data: {
// scroll_height:0,
navcur: 0,
curNodes: 0,
// navlist: ['历史意见', '协同意见', '相关附件', '流程图'],
},
//父组件接收子组件传值
compontpass(val) {
var self=this
self.setData({
navcur: val.detail.navcur,
curNodes: val.detail.curNodes
})
},
//父组件接收子组件传值
myevent(res){
var self=this
self.setData({
tops: res.detail
})
},
getScrollTop(e) {
// console.log('getScrollTop----',e)
var self = this;
let scrollTop = e.detail.current.detail.scrollTop;
let navcur = parseInt(self.data.navcur);
let min = self.data.tops[navcur].top;
let max = self.data.tops[navcur + 1] ? self.data.tops[navcur + 1].top : self.data.tops[navcur].top;
if (max - 56 < scrollTop && navcur < self.data.tops.length - 1) {
navcur++
} else if (min - 56 > scrollTop && navcur > 0) {
navcur--
}
self.setData({
navcur: navcur
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage(opts) {
},
})
JS-主页面
{
"usingComponents": {
"rui-sticky": "../../../component/sticky/sticky",
"rui-content": "../../../component/newcontent/newcontent"
}
}
子页面-newcontent
wxml:
<!-- 导航栏 -->
<view class="rui-flex-content rui-nav-flex">
<view bindtap="getNavIndex" data-index="{{index}}" class='rui-nav-li {{navcur==index ? "rui-active" : ""}} ' wx:for="{{navlist}}" wx:key="navlist">
<text>{{item}}</text>
</view>
</view>
<view class=" rui-flex-content rui-goods-content uinn">
<view class='rui-flex-li rui-goods-nodes bacred '>
<view class="ovh">
<text class="vertical"></text>
<text class="vertical_tit">示例1</text>
</view>
<view class="uinn">
<view class="notavailable">示例1-暂无数据</view>
</view>
</view>
<view class='rui-flex-li rui-goods-nodes bacred '>
<view class="ovh">
<text class="vertical"></text>
<text class="vertical_tit">示例2</text>
</view>
<view class="uinn">
<view class="notavailable">示例2-暂无数据</view>
</view>
</view>
<view class='rui-flex-li rui-goods-nodes bacred '>
<view class="ovh">
<text class="vertical"></text>
<text class="vertical_tit">上传图片</text>
</view>
<view class="uinn">
<rui-uploadfile bind:onload='onload'> 上传图片</rui-uploadfile>
</view>
</view>
<view class='rui-flex-li rui-goods-nodes bacred '>
<view class="ovh">
<text class="vertical"></text>
<text class="vertical_tit">效果图</text>
</view>
<view class="uinn">
<view class="liucheng" style='height:{{scroll_height}}rpx; ' data-scroll_height="{{scroll_height}}">
<image src="{{imgUrl}}"></image>
<!-- <text class="fullscreen" catchtap='previewphoto'>全屏</text> -->
</view>
</view>
</view>
</view>
wxss:
@import "../../utils/pulic.wxss";
js:
var app = getApp().globalData;
Component({
externalClasses: ['rui-class'],
options: {
multipleSlots: true
},
properties: {
//导航index值
navcur: {
type: Number,
value: 0
},
//效果图的高度
scroll_height: {
type: Number,
value: 0
},
},
data: {
navcur: 0,
curNodes: 0,
navlist: ['示例1', '示例2', '上传图片', '效果图'],
imgUrl:'../../src/images/liu.jpg'
},
methods: {
onload(rect){
var self = this
const query = wx.createSelectorQuery().in(this);
query.selectAll('.rui-goods-nodes').boundingClientRect((res) => {
// console.log(res)
self.setData({
tops: res,
});
self.triggerEvent('myevent', res)
}).exec()
},
getNavIndex(e) {
var self = this;
let index = e.currentTarget.dataset.index;
if (self.data.navcur == index) {
return false
}
self.setData({
navcur: index,
curNodes: self.data.tops[index].top - 55
})
//nav--index;curNodes---距离页面的top
var curNodesNum = {
navcur: self.data.navcur,
curNodes: self.data.curNodes
}
self.triggerEvent("compontpass", curNodesNum)
},
},
ready: function () {
var self = this
const query = wx.createSelectorQuery().in(this);
query.selectAll('.rui-goods-nodes').boundingClientRect((res) => {
self.setData({
tops: res,
});
self.triggerEvent('myevent', res)
}).exec()
let windowHeight = wx.getSystemInfoSync().windowHeight
let windowWidth = wx.getSystemInfoSync().windowWidth
self.setData({
scroll_height: (windowHeight * 750 / windowWidth) - 280
})
}
})
json:
{
"component": true,
"usingComponents": {}
}
子页面-sticky
wxml:
<scroll-view class="rui-sticky-content rui-class" bindscroll="getScrollTop" scroll-y="true" scroll-top="{{scroll}}">
<slot name="stickychild"></slot>
</scroll-view>
wxss:
@import "../../utils/pulic.wxss";
js:
Component({
externalClasses: ['rui-class'],
options: {
multipleSlots: true
},
properties: {
scroll: {
type: Number,
value: 0
}
},
methods: {
getScrollTop(e){
this.triggerEvent('stickyscroll', { current: e }, {});
}
}
})
json:
{
"component": true,
"usingComponents": {}
}
子页面-uploadfile
wxml:
<view class='bg_white border-a border-radius mar-t'>
<view class='uinn'>
<text>上传图片</text>
<text class='color_red'>(最少上传一张图片,最多十张)</text>
</view>
<view class='uinn flex-rows border_b border_t'>
<view class='flex25 mar-b' wx:for='{{photoList}}' wx:for-index='idx' wx:for-item='items' wx:key='index'>
<view class='exceptionimgbox bg_eb' hover-class='opacity' data-index="{{idx}}" catchtap='previewphoto'>
<image src='{{items}}'></image>
<image src='../../src/images/del.png' class='delimg' catchtap='delphoto' data-index="{{idx}}"></image>
</view>
</view>
<view class='flex25 mar-b'>
<view class='exceptionimgbox bg_eb' hover-class='opacity' bindtap='chooseimg'>
<image src='../../src/images/add.png' mode="aspectFit" class='add'></image>
</view>
</view>
</view>
</view>
wxss:
@import "../../utils/pulic.wxss";
js:
var main = require("../../utils/util.js");
var app = getApp().globalData;
Component({
options: {
multipleSlots: true
},
properties: {},
data: {
code: '',
photoNum: 10, //照片数量
photoList: [], //照片数组
photoindex: 'none', //照片当前位置
},
methods: {
chooseimg: function(e) {
var self = this;
if (self.data.photoNum <= 0) {
main.showToast('还能选择' + self.data.photoNum + '张照片');
return;
}
wx.chooseImage({
count: self.data.photoNum,
success(res) {
const tempFilePaths = res.tempFilePaths
if (res.tempFiles[0].size >= 10000000) {
main.showToast('图片大小超过10M');
return;
}
var len = res.tempFilePaths.length
self.setData({
photoNum: self.data.photoNum - len,
photoList: self.data.photoList.concat(tempFilePaths)
})
self.triggerEvent("onload", {flag:true})
}
})
},
delphoto: function(e) {
var self = this;
var photolist = self.data.photoList;
var photindex = Number(e.currentTarget.dataset.index);
main.showModal('提示', '是否确认删除?', true, function(res) {
if (res.confirm) {
photolist.splice(photindex, 1);
self.setData({
photoList: photolist,
photoNum: 10 - photolist.length,
photoindex: 'none'
});
} else {
self.setData({
photoindex: 'none'
})
}
})
},
}
})
methods: {
getScrollTop(e){
this.triggerEvent('stickyscroll', { current: e }, {});
}
}
})
json:
{
"component": true,
"usingComponents": {}
}
如果有感兴趣,可以去我的 https://download.csdn.net/my. 下载
这里注意一下:在上传功能上有改变导航栏里面的高度,这时候要重新获取一次导航栏下子元素的高度。——这是我遇到的坑,与大家共勉一下,希望大家代码之路都能避坑,一路顺畅