最近我们的产品看上了 滴答app==>消息页面的头部滑动样式
然后 安卓有 CollapsingToolbarLayout 组件 只需要修改参数即可完成 ios 10以后也有对应的组件可以直接完成此功能
只有js没有 在百度搜索许久 未找到 一点有关的信息 都没有找到 最后只能自己写了
实现
思路1:最开始想到的就是css3的 transform里的translate 来达到位移 然后再利用scale完成缩放 ,然后当你在实现的过程中
你会发现 需要计算的值特别多 几个要计算的难点
a.滚动距离scrollTop 和 这个title 的 x轴及y轴的运算值的对应
b.如果用户没有滑到最后的位置 而 只滑动了一半 要求 判断是利用动画返回初始位置,还是继续利用动画滑动到最上部
c.一些阈值的计算及限定
最后 我们换了一个思路去实现
思路2:
a.上面title里的 ”消息“ 我们给1个div 然后给1个宽度,然后给text-algin:center; 然后利用改变div的宽度去做出 这个title向 右侧平移的效果,
b.向上的效果 其实就是利用 设置页面剩下的 高度 为 整个body的100%减去 滚动到最上节点的高度 也就是
calc(100% - 44px);
c.字体缩小就是改变字体的fontSize 去达到缩小字体的效果
d.当滚动到最上面后 我们利用v-if v-else 去切换显示提前准备好的一个高度44px的头部
下面上代码 :
由于我写的是vue项目 所以 我把这个头抽成了公共组建,一些特殊的阈值及变化 由外部传入 这样增强了扩展性
html 部分:
<template>
<div id="myHead" style="position: fixed;top:0px;left:0px;width: 100%;background-color: #ffffff;z-index: 100" >
<div v-if="showScroll" :style="{height: showHeight+'px'}">
<div style="height: 44px;line-height: 44px;display: inline-block;position: absolute;left: 15px;top: 0px;display: flex;justify-content: flex-start;align-items: center" @click="leftTextClick">
<img src="./img/5.9-left.png" width="14" >
</div>
<div :style="{width:showWidth+'px',fontSize:showFontSize+'px',color:centerTextColor}" style="height: 44px;line-height:44px;position: absolute;bottom: 0px;text-align: center;white-space: nowrap;">{{centerText}}</div>
<div :style="{color:rightTextColor,fontSize:minFontSize+'px'}" style="height: 44px;line-height: 44px;display: inline-block;position: absolute;right: 15px;top: 0px;" @click="rightTextClick">
{{rightText}}
</div>
</div>
<div v-else >
<div class=" col-xs-12 padding0 " :style="{fontSize:minFontSize+'px'}" style="height: 44px;line-height: 44px;position: fixed;background-color: #ffffff;z-index: 101;">
<div class="col-xs-2" style="height: 44px;line-height: 44px;padding-right: 0px;display: flex;justify-content: flex-start;align-items: center" @click="leftTextClick"><img src="./img/5.9-left.png" width="14" ></div>
<div class="col-xs-7"></div>
<div class="col-xs-3" style="text-align: right" :style="{color:rightTextColor}" @click="rightTextClick">{{rightText}}</div>
<div style="position: absolute;left: 0px;top:0px;z-index:-1;text-align: center" :style="{width:maxWidth+'px',color:centerTextColor}">
{{centerText}}
</div>
</div>
</div>
</div>
</template>
上面代码 我把css 直接放到了行内 方便大家查看。
js部分
其中 着重说一下js部分
data中 我声明的变量 为显示变量
showHeight:100, 是头部的显示高度
showWidth:77, 是上面说的 title 的宽度
showFontSize:25, 是头部文字的字号
showScroll:true, 是显示滚动的头还是fixed的头
自动滚动函数 用来判断 用户把头推到了什么位置 而 自动完成后续的动画
autoScroll:function (heightCha) {
let _this=this
document.addEventListener('touchend', function () {
let newV = _this.$getScrollTop();
if(newV>(heightCha/2) && newV<heightCha){ // 自动上去
$('html,body').animate({scrollTop: heightCha+'px'}, 150);
}
if(newV>0 && newV<=(heightCha/2)){ // 自动下去
$('html,body').animate({scrollTop: '0px'}, 150);
}
}, false);
},
计算向上 向下滚动的比率 因为 向上scrollTop从1开始增长 向下 scrollTop 是从最大值 开始减到1 所以jia jian 比率不同
let jiaBiLv=scrollTop/heightCha; //向上滑动比率
let jianBiLv=(heightCha-scrollTop)/heightCha; //向下滑动比率
重点 : 变化函数 字体 div宽度 头的height 都是这个函数判断
// 变化函数
if(scrollTop>=lastScroll && scrollTop<=heightCha){ // 向上滚动
_this.showFontSize=_this.maxFontSize-(_this.maxFontSize-_this.minFontSize)*jiaBiLv;
_this.showWidth=_this.minWidth+(_this.maxWidth-_this.minWidth)*jiaBiLv;
_this.showHeight=_this.maxHeight-(_this.maxHeight-_this.minHeight)*jiaBiLv;
}
if(scrollTop<=lastScroll && scrollTop<=heightCha){ // 向下滚动
_this.showFontSize=_this.minFontSize+(_this.maxFontSize-_this.minFontSize)*jianBiLv;
_this.showWidth=_this.maxWidth-(_this.maxWidth-_this.minWidth)*jianBiLv;
_this.showHeight=_this.minHeight+(_this.maxHeight-_this.minHeight)*jianBiLv;
}
约束函数 通过阈值判断 滑动到最上面后 不再缩小字体 增加div宽度 从scroll头切换到 fixed的头
// 约束函数1 约束宽
if(_this.showWidth>=_this.maxWidth){
_this.showWidth = _this.maxWidth
}else
if(_this.showWidth<=_this.minWidth){
_this.showWidth = _this.minWidth
}
// 约束函数2 约束字体
if(_this.showFontSize>=_this.maxFontSize){
_this.showFontSize = _this.maxFontSize
}else if(_this.showFontSize<=_this.minFontSize){
_this.showFontSize =_this.minFontSize
}
// 约束函数3 切换头
if(scrollTop>=heightCha){
_this.showScroll=false
}else{
_this.showScroll=true
}
这句很有用 通过它 将当前滚动距离赋值给一个变量 然后下次 取新的滚动距离 和这个变量比较 判断是向上还是向下滑动
lastScroll = scrollTop;
从父组件接收传进来的参数 并给默认值
props: {
centerText: {
default: '',
type: String
},
rightText: {
default: '',
type: String
},
minHeight: {
default: 44,
type: Number
},
maxHeight: {
default: 100,
type: Number
},
minWidth: {
default: 77,
type: Number
},
maxWidth: {
default: 160,
type: Number
},
minFontSize: {
default: 17,
type: Number
},
maxFontSize: {
default: 25,
type: Number
},
rightTextColor: {
default: '#8F8F8F',
type: String
},
centerTextColor: {
default: '#333333',
type: String
},
}
最后剩下的就是调用了
<navHeaderTransform
:centerText="centerText"
:rightText="rightText"
:minHeight="minHeight"
:maxHeight="maxHeight"
:minWidth="minWidth"
:maxWidth="maxWidth"
:minFontSize="minFontSize"
:maxFontSize="maxFontSize"
@rightTextClick="navRightTextClick"
@leftTextClick="navLeftTextClick"
></navHeaderTransform>
<div :style="{height: maxHeight+'px'}"></div>
里面的值我就不列举了 下面要有一个 可以占位的div 因为整个头都是fixed的 如果你要问我问什么 主要是因为 微信浏览器 自带 弹性拉动 如果你不是fixed定位 而里面某个元素是fiexd 就会出来 很搞笑的画面 欢迎尝试
好了 下面 奉上 组件全部代码
<script>
export default{
name:'navHeaderTransform',
created(){
},
data(){
return{
showHeight:100,
showWidth:77,
showFontSize:25,
showScroll:true,
}
},
created(){
this.showHeight=this.maxHeight;
this.showWidth=this.minWidth;
this.showFontSize=this.maxFontSize;
},
mounted(){
let _this=this
const heightCha=_this.maxHeight-44; // 44为固定头的高度
_this.autoScroll(heightCha); // 自动滚动函数
let lastScroll = 0;
window.onscroll= function(e){
let scrollTop = _this.$getScrollTop();
let jiaBiLv=scrollTop/heightCha; //向上滑动比率
let jianBiLv=(heightCha-scrollTop)/heightCha; //向下滑动比率
// 变化函数
if(scrollTop>=lastScroll && scrollTop<=heightCha){ // 向上滚动
_this.showFontSize=_this.maxFontSize-(_this.maxFontSize-_this.minFontSize)*jiaBiLv;
_this.showWidth=_this.minWidth+(_this.maxWidth-_this.minWidth)*jiaBiLv;
_this.showHeight=_this.maxHeight-(_this.maxHeight-_this.minHeight)*jiaBiLv;
}
if(scrollTop<=lastScroll && scrollTop<=heightCha){ // 向下滚动
_this.showFontSize=_this.minFontSize+(_this.maxFontSize-_this.minFontSize)*jianBiLv;
_this.showWidth=_this.maxWidth-(_this.maxWidth-_this.minWidth)*jianBiLv;
_this.showHeight=_this.minHeight+(_this.maxHeight-_this.minHeight)*jianBiLv;
}
// 约束函数1 约束宽
if(_this.showWidth>=_this.maxWidth){
_this.showWidth = _this.maxWidth
}else
if(_this.showWidth<=_this.minWidth){
_this.showWidth = _this.minWidth
}
// 约束函数2 约束字体
if(_this.showFontSize>=_this.maxFontSize){
_this.showFontSize = _this.maxFontSize
}else if(_this.showFontSize<=_this.minFontSize){
_this.showFontSize =_this.minFontSize
}
// 约束函数3 切换头
if(scrollTop>=heightCha){
_this.showScroll=false
}else{
_this.showScroll=true
}
lastScroll = scrollTop;
}
},
methods:{
autoScroll:function (heightCha) {
let _this=this
document.addEventListener('touchend', function () {
let newV = _this.$getScrollTop();
if(newV>(heightCha/2) && newV<heightCha){ // 自动上去
$('html,body').animate({scrollTop: heightCha+'px'}, 150);
}
if(newV>0 && newV<=(heightCha/2)){ // 自动下去
$('html,body').animate({scrollTop: '0px'}, 150);
}
}, false);
},
rightTextClick:function () {
this.$emit('rightTextClick')
},
leftTextClick:function () {
this.$emit('leftTextClick')
}
},
// props:['title','bgImg','bgColor','frontColor','height','minHeight','maxHeight','titleSize','maxTitleSize','minTitleSize']
props: {
centerText: {
default: '',
type: String
},
rightText: {
default: '',
type: String
},
minHeight: {
default: 44,
type: Number
},
maxHeight: {
default: 100,
type: Number
},
minWidth: {
default: 77,
type: Number
},
maxWidth: {
default: 160,
type: Number
},
minFontSize: {
default: 17,
type: Number
},
maxFontSize: {
default: 25,
type: Number
},
rightTextColor: {
default: '#8F8F8F',
type: String
},
centerTextColor: {
default: '#333333',
type: String
},
}
}
</script>
<style scoped="scoped" lang="less">
</style>
代码让我压缩后 传上了csdn 如果有需要的 下载即可,然后配合我上面的调用代码 一切轻松搞定
链接:https://download.csdn.net/download/gaoqiang1112/10966617