1、前言
想起之前一次在写一个小程序商城时候,详情页的类似锚点的跳转花了我不少时间,因为刚写,对小程序滚动,滚动距离那套不熟悉加之本身对什么滚动高度,元素距离顶部距离不是很了解,花了挺长时间的,这几天有空,就研究了下。
2、先看效果
额,tab点击没有加效果,看着不明显,你懂我的意思就行 哈哈,电商里面最常见了
3、页面搭建
先搞一个 tab,然后下面就是 几个对应的div盒子
1// 标题 2 <div class="tab" > 3 <span v-for="item in 4" 4 :key="item" @click="toView(item)" > 5 {{`滚到${item}去`}} 6 </span> 7</div> 8//内容 9// 这是采用动态绑定ref 方便获取对应 dom 距离顶部的距离 10// 同时也动态绑定元素的背景色和高度,便于区分 11<div v-for="item in 4" :key="item" 12 :ref="'scrollView'+item" :id="'tab'+item" 13 class="item_box" :style="getStyle(item)" > 14 {{item}} 15</div> 16
4、动态绑定如下:
1// getRandomArbitrary 用于生产指定区间的随机数 2 computed:{ 3 getStyle(){ 4 return function (index){ 5 // 高度我就 *对应的 index来递增了 6 let height = this.getRandomArbitrary(110,300)*index 7 return { 8 'background': 9 `linear-gradient(rgb(240,255,240),rgb(${this.getRandomArbitrary(230,255)}, ${this.getRandomArbitrary(230,240)}, ${this.getRandomArbitrary(240,255)}))`, 10 height:`${height}px` 11 } 12 } 13 } 14},
5、开始实现:
目前用了三种方式:
- 锚点跳转
- ele.scrollIntoView() api跳转
- 老老实实用 window.scrollTo
5、1 锚点
1// 锚点简单,搞个a标签,href指向对应盒子的 id就可以了,但是有个毛病 2//:8080/anchor#tab1,路由也变成了这样,而且还添加到了页面历史记录中了 3anchorWay(){ 4 let link = document.createElement("a") 5 link.href = '#'+'tab'+index 6 link.click() 7 link.removeChild() 8},
5.2、scrollIntoView
1//这个也简单,想要跳转到哪个元素,就在这个元素上调用此api就行了 2//还支持动画,不过ios不支持 3// 这个有个弊端就是,他只能让元素滚动到页面顶部,或者元素底部在页面底部 4// 一般我们上面还有个导航的 所以跳转后还需要动态去改变下距离顶部距离 5scrIntoView(ele){ 6 // ele.scrollIntoView({behavior: "smooth", block: "start"}) 7 ele.scrollIntoView(true) 8},
5.3、window.scrollTo
这个最方便了,直接找好需要滚动的位置,传入即可window.scrollTo(x,y)</br>我想来个动画呢 </br> window.requestAnimationFrame
这个api之前有写过,有兴趣可看看之前写的,简单说下,这个api是请求开启一个动画,接收一个函数,会自动以 浏览器刷新的频率去执行,也就是说 如果我们,不断的改变 传入 window.scrollTo(x,y)中的 y值,这样就可以呈现动画的效果了。 好比 1+10+9+8+7+....+0.1 总有一天会加到100
分成几个小步骤
5.3.1、点击tab时
主要目的,获取需要的目标盒子距离顶部的距离(对应元素距离屏幕顶部),同时,当该元素距离顶部的距离 == 顶部导航高度(一般可以已知)时,不进行滚动
1// 点击 tab 2toView(index){ 3 let refV = `scrollView${index}` 4 let scrView = this.$refs[refV][0] 5 // 目标盒子 距离顶部距离 6 let viewTop = parseInt( scrView.getBoundingClientRect().top ) 7 // 31 为顶部导航高度,可灵活变更 已在导航下方 return 8 if( Math.abs(viewTop) < 32 ) return 9 this.winScrllToView(viewTop - 31) 10 },
5.3.2、获取当前window已滚动距离和需要滚动的目标位置
1// y 目标元素距离顶部的距离(已减去导航高度) 2 winScrllToView(y){ 3 // 页面当前滚动距离 4 let sTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop 5 // 页面滚动目标位置 6 this.sTarget = parseInt (sTop + y) 7 // 页面滚动当前位置 8 this.nowLocation = parseInt(sTop) 9 10 this.animaScrll() 11 },
5.3.3、动画函数
已经知道了滚动的目标位置,和当前滚动位置,接下来就是实现滚动距离的累加(累减,上滚动时)
1 // 动画函数 2 animaScrll(){ 3 // 累加值 为 目标位置 - 已经滚动距离 / 8 呈现累加值递减 4 let addDis = (this.sTarget - this.nowLocation) / 8 5 6 this.nowLocation += addDis 7 window.scrollTo(0,this.nowLocation) 8 // 类似定时器 会返回一个id 用来清除 9 this.aniTimer = window.requestAnimationFrame(this.animaScrll) 10 // 理想状态是 目标值 == 累加滚动的值 实际上不肯的,边界值可以自行判断 11 if( Math.abs(this.sTarget - this.nowLocation) <= 2 ) { 12 window.cancelAnimationFrame(this.aniTimer) 13 this.nowLocation = 0 14 this.aniTimer = null 15 } 16}
好了,整个过程就完了,总的来说还是 window.scrollTo 来的直接暴力。requestAnimationFrame也存在部分兼容问题
