在很多APP上,我们经常能见到菜单滚动置顶、点击置顶的场景,说的通俗一点就是:起初页面初始化的时候,菜单处于文档流中,当我们向上滑动页面的时候,菜单脱离文档流,悬浮于页面顶端,且将一直处于顶端,而当我们向下滑动页面的至最初的位置时,菜单又回到文档流当中了。
滚动置顶的思路
要想让菜单固定在顶端,我们想到的当然是
position:fixed
啦~ 上代码来试试:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 <template><div class="mod-page js-body"><section class="mod-content js-mod-body index"><!--省略部分结构--><div class="mod-filter bc-main com-space" id="menuNav" :style="{'height':menuHeight + 'px'}"><div class="mod-filter bc-main" :class="{'fixed-menu-fixed': isFixedMenu}" id="topMenu"><nav class="filter-nav"><ul class="mod-list mod-flex com-border-bottom"><li class="mod-list-item mod-flex-item" v-for="item in filterData"><a href="javascript:;" class="mod-list-info">{{item}}</a></li></ul></nav></div></div><!--省略部分结构--></section></div></template><script>import scrollTo from 'scrollTo.js'export default {data () {return {filterData: ["区域", "职位类型", "职位排序", "筛选"],platform: Util.OS(),isFixedMenu: false, //是否Fixed menumenuTop: 48, // menu固定topmenuHeight: 44 // menu高度threshold: 100}},methods: {/*** 初始化* @method initFun* @public* @return {Null} void*/initFun() {let self = this;//注册滚动事件self.registerScrollEvent();},/*** 注册滚动事件,用以实现menu固定* @method registerScrollEvent* @public* @return {Null} void*/registerScrollEvent () {let self = this;$(document).on('scroll', function(e) {let scrolltop = document.body.scrollTop;if( scrolltop >= threshold) {self.isFixedMenu = true;} else if (scrolltop < threshold + menuHeight) {self.isFixedMenu = false;}});}},created() {let self = this;self.initFun();}}</script><!--省略部分样式--><style src="static/css/test.css"></style><style type="text/css">.fixed-menu-fixed {animation: fade-in .3s;position: fixed !important;}@keyframes fade-in {0% {opacity: 0;}10% {opacity: 0.1;}20% {opacity: 0.2;}30% {opacity: 0.3;}40% {opacity: 0.4;}50% {opacity: 0.5;}60% {opacity: 0.6;}70% {opacity: 0.7;}80% {opacity: 0.8;}90% {opacity: 0.9;}100% {opacity: 1;}}</style>我们通过监听页面的滚动
scroll
事件,在滚动事件回调中判断菜单滚动位置是否超出阀值threshold
,如果超出阀值则设置成固定布局position: fixed
(脱离文档流),如果小于阀值+菜单高度,则恢复原有布局(回到文档流),经过试验,安卓手机上是没问题的,需要注意的是:使用position: fixed
会使页面中菜单脱离文档流,那么将影响它下面的DOM结构,出现突然抖动上移的情况,对于这种情况,我们需要做占位
处理,即给菜单套一层有固定高度(menuHeight)的div。
12345678910111213141516171819 <template><div class="mod-page js-body"><section class="mod-content js-mod-body index"><!--省略部分结构--><div class="mod-filter bc-main com-space" id="menuNav" :style="{'height':menuHeight + 'px'}"><div class="mod-filter bc-main" :class="{'fixed-menu-fixed': isFixedMenu}" id="topMenu"><nav class="filter-nav"><ul class="mod-list mod-flex com-border-bottom"><li class="mod-list-item mod-flex-item" v-for="item in filterData"><a href="javascript:;" class="mod-list-info">{{item}}</a></li></ul></nav></div></div><!--省略部分结构--></section></div></template>但是对于ios来说,只能在滚动结束后才会触发
scroll
回调,即便监听’touchmove’实时触发滚动回调,对于菜单布局的渲染也是阻塞的,也就是会延迟很多。。。,多么尴尬的岁月~多么尴尬的日子~多么尴尬~好想改变一切~只是没有银子但有方法,哈哈!下面隆重介绍一下粘性布局position: sticky
。
粘性布局(position: sticky)
网络上关于粘性布局(position: sticky)的博客很多,这里引用一下地址(本人太懒,O(∩_∩)O):
使用 position:sticky 实现粘性布局
position:sticky实现iOS6+下的粘性布局好了,了解了粘性布局(position: sticky)之后,我们对代码进行了修改:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 <template><div class="mod-page js-body"><section class="mod-content js-mod-body index"><!--省略部分结构--><div class="mod-filter bc-main com-space" id="menuNav" :style="{'height':menuHeight + 'px'}" v-if="platform == 'android'"><div class="mod-filter bc-main" :class="{'fixed-menu-fixed': isFixedMenu}" id="topMenu"><nav class="filter-nav"><ul class="mod-list mod-flex com-border-bottom"><li class="mod-list-item mod-flex-item" v-for="item in filterData"><a href="javascript:;" class="mod-list-info">{{item}}</a></li></ul></nav></div></div><div class="mod-filter bc-main com-space" :class="{'fixed-menu-sticky': isStickyMenu, 'active': clickedMenuItem != -1}" id="menuNav" v-if="platform == 'ios'"><nav class="filter-nav"><ul class="mod-list mod-flex com-border-bottom"><li class="mod-list-item mod-flex-item" v-for="(item, index) in filterData" @click="filterMenuClicked()"><a href="javascript:;" class="mod-list-info">{{item}}</a></li></ul></nav></div><!--省略部分结构--></section></div></template><script>import scrollTo from 'scrollTo.js'export default {data () {return {filterData: ["区域", "职位类型", "职位排序", "筛选"],platform: Util.OS(),isFixedMenu: false, //是否Fixed menuisStickyMenu: false, //是否Sticky menumenuTop: 48, // menu固定topmenuHeight: 44 // menu高度threshold: 100}},methods: {/*** 初始化* @method initFun* @public* @return {Null} void*/initFun() {let self = this;//注册滚动事件self.registerScrollEvent();},/*** 注册滚动事件,用以实现menu固定* @method registerScrollEvent* @public* @return {Null} void*/registerScrollEvent () {let self = this;/*##############主要看这里###################*/let menuId = 'topMenu';if(Util.OS() == 'ios') {menuId = 'menuNav';}// 设置菜单的top位置$(`#${menuId}`).css('top', self.menuTop + 'px');if(/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)){self.isStickyMenu = true;} else if (/android/i.test(navigator.userAgent)) {$(document).on('scroll', function(e) {let scrolltop = document.body.scrollTop;if(/android/i.test(navigator.userAgent)){if( scrolltop >= threshold) {self.isFixedMenu = true;} else if (scrolltop < threshold + menuHeight) {self.isFixedMenu = false;}}});}},filterMenuClicked () {let self = this;$('body').scrollTo({toT: self.menuTop, durTime : 200});}},created() {let self = this;self.initFun();}}</script><!--省略部分样式--><style src="static/css/test.css"></style><style type="text/css">.fixed-menu-fixed {animation: fade-in .3s;position: fixed !important;}/*##############主要看这里###################*/.fixed-menu-sticky {position: -webkit-sticky !important;position: -moz-sticky !important;position: -ms-sticky !important;position: -o-sticky !important;position: sticky !important;}@keyframes fade-in {0% {opacity: 0;}10% {opacity: 0.1;}20% {opacity: 0.2;}30% {opacity: 0.3;}40% {opacity: 0.4;}50% {opacity: 0.5;}60% {opacity: 0.6;}70% {opacity: 0.7;}80% {opacity: 0.8;}90% {opacity: 0.9;}100% {opacity: 1;}}</style>因为安卓手机需要一个占位,而ios不需要,所以我们分开处理。在
registerScrollEvent
方法中,我们首先判断是ios设备还是安卓设备,如果是ios设备,我们直接使用position: sticky;
来将菜单设置成粘性布局,并给它一个top值,这个值表示当元素距离页面视口(Viewport,也就是fixed定位的参照)顶部距离大于 0px 时,元素以 relative 定位表现,而当元素距离页面视口小于 0px 时,元素表现为 fixed 定位,也就会固定在顶部。
实现点击滚动置顶
思路很简单,就是点击菜单项,将页面滚动至我们设定的位置。我们前端采用的是zepto这个轻库进行开发的,因此想采用animate({scrollTop:”100px”})函数进行页面的动画滚动,结果就是毫无效果。。。,发现zepto的animate()源码采用css3的方式进行,而scrollTop属性不在css3的动画属性中,所以没有生效。接下来的方法就是自己写一个滚动条上下滚动的方法。那么,我们来写一写:。
12345678910111213141516171819202122232425262728293031323334 /* scrollTo.js */$.fn.scrollTo =function(options){var defaults = {toT : 0, //滚动目标位置durTime : 500, //过渡动画时间delay : 30, //定时器时间callback:null //回调函数};var opts = $.extend(defaults,options),timer = null,_this = this,curTop = _this.scrollTop(),//滚动条当前的位置subTop = opts.toT - curTop, //滚动条目标位置和当前位置的差值index = 0,dur = Math.round(opts.durTime / opts.delay),smoothScroll = function(t){index++;var per = Math.round(subTop/dur);if(index >= dur){_this.scrollTop(t);window.clearInterval(timer);if(opts.callback && typeof opts.callback == 'function'){opts.callback();}return;}else{_this.scrollTop(curTop + index*per);}};timer = window.setInterval(function(){smoothScroll(opts.toT);}, opts.delay);return _this;};采用原型函数的方式,制做一个scrollTo方,效果还不错~~哈!然后,我们给菜单项绑定点击事件
filterMenuClicked
,在filterMenuClicked
中实现点击滚动置顶。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 <template><div class="mod-page js-body"><section class="mod-content js-mod-body index"><!--省略部分结构--><div class="mod-filter bc-main com-space" id="menuNav" :style="{'height':menuHeight + 'px'}" v-if="platform == 'android'"><div class="mod-filter bc-main" :class="{'fixed-menu-fixed': isFixedMenu}" id="topMenu"><nav class="filter-nav"><ul class="mod-list mod-flex com-border-bottom"><li class="mod-list-item mod-flex-item" v-for="item in filterData"><a href="javascript:;" class="mod-list-info">{{item}}</a></li></ul></nav></div></div><div class="mod-filter bc-main com-space" :class="{'fixed-menu-sticky': isStickyMenu, 'active': clickedMenuItem != -1}" id="menuNav" v-if="platform == 'ios'"><nav class="filter-nav"><ul class="mod-list mod-flex com-border-bottom"><li class="mod-list-item mod-flex-item" v-for="(item, index) in filterData" @click="filterMenuClicked()"><a href="javascript:;" class="mod-list-info">{{item}}</a></li></ul></nav></div><!--省略部分结构--></section></div></template><script>import scrollTo from 'scrollTo.js'export default {data () {return {filterData: ["区域", "职位类型", "职位排序", "筛选"],platform: Util.OS(),isFixedMenu: false, //是否Fixed menuisStickyMenu: false, //是否Sticky menumenuTop: 48, // menu固定topmenuHeight: 44 // menu高度threshold: 100}},methods: {/*** 初始化* @method initFun* @public* @return {Null} void*/initFun() {let self = this;//注册滚动事件self.registerScrollEvent();},/*** 注册滚动事件,用以实现menu固定* @method registerScrollEvent* @public* @return {Null} void*/registerScrollEvent () {let self = this;if(/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)){self.isStickyMenu = true;} else if (/android/i.test(navigator.userAgent)) {$(document).on('scroll', function(e) {let scrolltop = document.body.scrollTop;if(/android/i.test(navigator.userAgent)){if( scrolltop >= threshold) {self.isFixedMenu = true;} else if (scrolltop < threshold + menuHeight) {self.isFixedMenu = false;}}});}},filterMenuClicked () {let self = this;/*##############主要看这里###################*/$('body').scrollTo({toT: self.menuTop, durTime : 200});}},created() {let self = this;self.initFun();}}</script><!--省略部分样式--><style src="static/css/test.css"></style><style type="text/css">.fixed-menu-fixed {animation: fade-in .3s;position: fixed !important;}.fixed-menu-sticky {position: -webkit-sticky !important;position: -moz-sticky !important;position: -ms-sticky !important;position: -o-sticky !important;position: sticky !important;}@keyframes fade-in {0% {opacity: 0;}10% {opacity: 0.1;}20% {opacity: 0.2;}30% {opacity: 0.3;}40% {opacity: 0.4;}50% {opacity: 0.5;}60% {opacity: 0.6;}70% {opacity: 0.7;}80% {opacity: 0.8;}90% {opacity: 0.9;}100% {opacity: 1;}}</style>好啦,先记录到这儿吧