原神启动!!用 Vue 实现原神官网的全屏滚动效果
点击关注公众号,技术干货及时送达先看一下官网效果:https://ys.mihoyo.com/main/map[1]
概念每次当滚动鼠标滚轮的时候,页面会进行一整页的滚动,这就是全屏滚动。
要求当窗口大小变化时,全屏滚动效果不会发生变化,即需要做到自适应,不受到高度宽度的影响。
当点击指示器也可以进行页面切换。
原理「最外层容器」:就是我们的窗口,视角能看到的。
「内层容器:」 用来存放我们需要滚动内容的容器。
「滚动元素:」 就是我们看到的一张张照片。
首先对最外侧容器的overflow:hidden;这样保证不会溢出。
其次对内层容器进行滚动,每次滚动的高度就是窗口的高度,但是需要动态计算窗口的高度。
最后只需要判断上一页还是下一页来计算index,通过index ? 页面高度,来进行滚动即可。
代码实现HTML代码大家可以结合html结构,去理解 js 代码
!--最外层容器--
divclass="outer-box"ref="fullPage"
!--内层容器--
div
ref="element"
:class="{activeTranstion:isCloseTranstion}"
class="inner-box"
@mousewheel="mousewheel"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
@touchmove="handleTouchMove"
!--滚动显示的元素--
div
v-for="iteminysImage"
:style="{backgroundImage:`url(${item.backgroundImage})`,height:windowHeight+'px'}"
class="scroll-element"
/div
/div
!--指示器--
ulclass="aside"
liv-for="(item,index)inasideData"@click="changeBac(index)"
span:class="{active:index===$index}"/span
divv-show="index===$index"class="show-dec"{{item.title}}/div
/li
/ul
/div
JS代码给内层容器添加鼠标滚动事件当滚动事件触发时判断是向上还是向下滚动,以此来控制滚动方向。
另外由于鼠标滚动事件触发过于频繁,我们需要增加「节流」,即保证在单位时间内只执行一次即可。
设置canRun为节流标志,每500毫秒执行一次。
functionmousewheel(e){
isTranstion.value=false
if(canRun.value){
canRun.value=false
goScroll(e)
setTimeout(()={
canRun.value=true
},1100)
}
}
开始滚动元素通过上一页下一页增加减少$index,通过索引 ? 页面高度,来决定页面滚动的距离,也就是当前要显示的页面。
functiongoScroll(e){
//e.wheelDelta用来判断上一个下一个0下一个0上一个
if(e.wheelDelta0){
next()
}else{
last()
}
}
//$INDEX
const$index=ref(0)//索引控制第几个显示
//下一个
functionnext(){
if($index.value4){
$index.value++
}
}
//上一个
functionlast(){
if($index.value1||$index.value===1){
$index.value--
}
}
动态计算页面高度由于页面高度发生变化时,我们需要动态响应高度的变化。
使用 VueUse 库提供的 useWindowSize() 函数计算https://vueuse.org/core/useWindowSize/[2]
当页面高度变化时height会相应变化
const{height}=useWindowSize()
值得注意的是,当页面高度变化时,我们需要关闭动画效果。
constwindowHeight=computed(()={
isTranstion.value=true
returnheight.value
})
「通过索引计算滚动高度」
consttransformScroll=computed(()={
return`translateY(-${$index.value*windowHeight.value}px)`
})
「将滚动高度应用到滚动容器上面」
「watchEffect:」 会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。官方:https://cn.vuejs.org/guide/essentials/watchers.html#watcheffect[3]
//ELEMENT
constelement=ref('element')
watchEffect(()={
if(element.value.style){
element.value.style.transform=transformScroll.value
}
})
点击指示器点击指示器的时候,跳转到相应页面,只需设置相应的index即可。
//点击切换
functionchangeBac(index){
//点击切换时需要开启动画
isTranstion.value=false
$index.value=index
}
移动端//#region移动端
conststartY=ref(0)//记录开始位置
constendY=ref(0)//记录结束位置
constmoveDistance=ref(0)//滑动距离
//触摸开始
functionhandleTouchStart(e){
startY.value=e.touches[0].pageY||e.changedTouches[0].pageY
}
//触摸移动
functionhandleTouchMove(e){
e.preventDefault()//wx上拉默认事件
isCloseTranstion.value=true//开始移动关闭动画
moveDistance.value=(e.changedTouches[0].pageY||e.touches[0].pageY)-startY.value//计算移动距离
//判断临界点
constisCriticalPoint=
($index.value===4moveDistance.value0)||($index.value===0moveDistance.value0)
//如果是临界点就直接返回
if(isCriticalPoint){
return
}
//否则直接对内层容器应用随之移动
element.value.style.transform=`translateY(-${$index.value*windowHeight.value+moveDistance.value*-1}px)`
//触摸抬起
functionhandleTouchEnd(e){
//抬起时开启动画
isCloseTranstion.value=false
//计算结束距离
endY.value=e.changedTouches[0].pageY||e.touches[0].pageY
//计算移动距离,判断应该上一页还是下一页,直接更改index即可在原先基础上整页移动
moveDistance.value=endY.value-startY.value
//这里我做了一个最小值大于50才翻页
if(Math.abs(moveDistance.value)=60){
if($index.value4moveDistance.value0){
$index.value++
}
if($index.value0moveDistance.value0){
$index.value--
}
}else{
//当临界值小于60意味着不需要翻页就恢复原来的位置即可
element.value.style.transform=`translateY(-${$index.value*windowHeight.value}px)`
}
}
}
效果想看效果得可以进入看一下:https://chenyajun.fun/#/fullPage[4]
总结全屏滚动的核心还是判断上一页还是下一页,动态计算页面高度,对内层容器进行滚动,从而显示我们需要展示的元素即可。不过在一些细节处理上还是需要注意,尤其是动画的开关。移动端的触摸同步也需要动画的配合,看似简单的一个应用,其实里面值得学习的内容还是比较多的。
写作不易,你的赞就是我最大的动力,觉得写的不错的,可以给点个赞呢~
全部源码GitHub地址:https://github.com/chenyajun-create/ysFullPage
所有源码已经同步提交GitHub,觉得对你有帮助的话,记得帮作者点下star?~非常感谢呢
scriptsetup
//IMAGEDATA
constysImage=ref([
{
backgroundImage:'https://ys.mihoyo.com/main/_nuxt/img/5c125a1.png',
},
{
backgroundImage:'https://uploadstatic.mihoyo.com/contentweb/20200319/2020031921550320292.jpg',
},
{
backgroundImage:'https://uploadstatic.mihoyo.com/contentweb/20200319/2020031921552395638.jpg',
},
{
backgroundImage:'https://uploadstatic.mihoyo.com/contentweb/20210719/2021071918001232800.jpg',
},
{
backgroundImage:
'https://webstatic.mihoyo.com/upload/contentweb/2022/08/15/8969f683b92839ac427c875d0d742be2_4825576482548821743.jpg',
},
])
constasideData=ref([
{
title:'蒙德',
},
{
title:'璃月',
},
{
title:'稻妻',
},
{
title:'须弥',
},
{
title:'枫丹',
},
])
//ELEMENT
constelement=ref('element')
watchEffect(()={
if(element.value.style){
element.value.style.transform=transformScroll.value
}
})
//HEIGHT
const{height}=useWindowSize()
constwindowHeight=computed(()={
//高度变化时需要关闭动画
isCloseTranstion.value=true
returnheight.value
})
consttransformScroll=computed(()={
return`translateY(-${$index.value*windowHeight.value}px)`
})
constisCloseTranstion=ref(false)//控制是否显示动画效果
constcanRun=ref(true)//节流控制器
functionmousewheel(e){
isCloseTranstion.value=false
if(canRun.value){
canRun.value=false
goScroll(e)
setTimeout(()={
canRun.value=true
},1100)
}
}
functiongoScroll(e){
//e.wheelDelta用来判断上一个下一个0下一个0上一个
if(e.wheelDelta0){
next()
}else{
last()
}
}
//$INDEX
const$index=ref(0)//索引控制第几个显示
//下一个
functionnext(){
if($index.value4){
$index.value++
}
}
//上一个
functionlast(){
if($index.value1||$index.value===1){
$index.value--
}
}
//点击切换
functionchangeBac(index){
//点击切换时需要开启动画
isCloseTranstion.value=false
$index.value=index
}
//#region移动端
conststartY=ref(0)//记录开始位置
constendY=ref(0)//记录结束位置
constmoveDistance=ref(0)//滑动距离
//触摸开始
functionhandleTouchStart(e){
startY.value=e.touches[0].pageY||e.changedTouches[0].pageY
}
//触摸抬起
functionhandleTouchEnd(e){
//抬起时开启动画
isCloseTranstion.value=false
//计算结束距离
endY.value=e.changedTouches[0].pageY||e.touches[0].pageY
//计算移动距离,判断应该上一页还是下一页,直接更改index即可在原先基础上整页移动
moveDistance.value=endY.value-startY.value
//这里我做了一个最小值大于50才翻页
if(Math.abs(moveDistance.value)=60){
if($index.value4moveDistance.value0){
$index.value++
}
if($index.value0moveDistance.value0){
$index.value--
}
}else{
//当临界值小于60意味着不需要翻页就恢复原来的位置即可
element.value.style.transform=`translateY(-${$index.value*windowHeight.value}px)`
}
}
//触摸移动
functionhandleTouchMove(e){
e.preventDefault()
isCloseTranstion.value=true//开始移动关闭动画
moveDistance.value=(e.changedTouches[0].pageY||e.touches[0].pageY)-startY.value//计算移动距离\
//判断临界点
constisCriticalPoint=
($index.value===4moveDistance.value0)||($index.value===0moveDistance.value0)
//如果是临界点就直接返回
if(isCriticalPoint){
return
}
//否则直接对内层容器应用随之移动
element.value.style.transform=`translateY(-${$index.value*windowHeight.value+moveDistance.value*-1}px)`
}
//#endregion
/script
template
!--最外层容器--
divclass="outer-box"ref="fullPage"
!--内层容器--
div
ref="element"
:class="{activeTranstion:isCloseTranstion}"
class="inner-box"
@mousewheel="mousewheel"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
@touchmove="handleTouchMove"
!--滚动显示的元素--
div
v-for="iteminysImage"
:style="{backgroundImage:`url(${item.backgroundImage})`,height:windowHeight+'px'}"
class="scroll-element"
/div
/div
!--指示器--
ulclass="aside"
liv-for="(item,index)inasideData"@click="changeBac(index)"
span:class="{active:index===$index}"/span
divv-show="index===$index"class="show-dec"{{item.title}}/div
/li
/ul
/div
/template
stylelang="scss"scoped
.activeTranstion{
transition:all0msease0s!important;
}
.active{
display:inline-block;
width:12px!important;
height:12px!important;
}
.outer-box{
width:100%;
height:100%;
overflow:hidden;
position:relative;
.inner-box{
width:100%;
transition:allease-in-out0.5s;
.scroll-element{
background-size:cover!important;
background-position:center;
background-repeat:no-repeat;
}
}
.aside{
list-style:none;
position:fixed;
right:20px;
top:
transform:translateY(-50%);
li{
height:14px;
width:14px;
margin:
display:flex;
align-items:center;
justify-content:center;
position:relative;
.show-dec{
text-align:right;
position:absolute;
width:70px;
right:20px;
padding:
//opacity:
color:#000;
transition:alllinear0.1s;
font-size:12px;
background-color:#fff;
}
span {
border-radius:100%;
border:#fffsolid
width:
height:
display:inline-block;
background-color:#fff;
transition:allease-in-out0.2s;
}
&:hoverspan {
width:10px;
height:10px;
background-color:#fff;
cursor:pointer;
}
}
}
}
/style
如果文章对你有帮助的话欢迎
「关注+点赞+收藏」
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线