js+css 实现类似安卓Android CollapsingToolbarLayout 的折叠Toolbar 类似滴答消息页面title折叠效果

35 篇文章 0 订阅
29 篇文章 0 订阅

最近我们的产品看上了 滴答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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值