先看一下官网效果:ys.mihoyo.com/main/map
概念
每次当滚动鼠标滚轮的时候,页面会进行一整页的滚动,这就是全屏滚动。
要求
当窗口大小变化时,全屏滚动效果不会发生变化,即需要做到自适应,不受到高度宽度的影响。
当点击指示器也可以进行页面切换。
原理
最外层容器:就是我们的窗口,视角能看到的。
内层容器: 用来存放我们需要滚动内容的容器。
滚动元素: 就是我们看到的一张张照片。
首先对最外侧容器的overflow:hidden;这样保证不会溢出。
其次对内层容器进行滚动,每次滚动的高度就是窗口的高度,但是需要动态计算窗口的高度。
最后只需要判断上一页还是下一页来计算index,通过index ✖ 页面高度,来进行滚动即可。
代码实现
HTML代码
大家可以结合html结构,去理解 js 代码 ` ` ## JS代码
给内层容器添加鼠标滚动事件
当滚动事件触发时判断是向上还是向下滚动,以此来控制滚动方向。
另外由于鼠标滚动事件触发过于频繁,我们需要增加节流,即保证在单位时间内只执行一次即可。
设置canRun为节流标志,每500毫秒执行一次。
function mousewheel(e) {
isTranstion.value = false
if (canRun.value) {
canRun.value = false
goScroll(e)
setTimeout(() => {
canRun.value = true
}, 500)
}
}
开始滚动元素
通过上一页下一页增加减少$index,通过索引 ✖ 页面高度,来决定页面滚动的距离,也就是当前要显示的页面。 ` function goScroll(e) { //e.wheelDelta 用来判断上一个下一个 <0 下一个 >0上一个 if (e.wheelDelta < 0) { next() } else { last() } }
//$INDEX
const $index = ref(0) //索引控制第几个显示
// 下一个
function next() {
if ($index.value < 4) {
$index.value++
}
}
// 上一个
function last() {
if ($index.value > 1 || $index.value === 1) {
$index.value--
}
}
`
动态计算页面高度
由于页面高度发生变化时,我们需要动态响应高度的变化。
使用 VueUse 库提供的 useWindowSize() 函数计算vueuse.org/core/useWin…
当页面高度变化时height会相应变化
const { height } = useWindowSize()
值得注意的是,当页面高度变化时,我们需要关闭动画效果。
const windowHeight = computed(() => {
isTranstion.value = true
return height.value
})
通过索引计算滚动高度
const transformScroll = computed(() => {
return `translateY(-${$index.value * windowHeight.value}px)`
})
将滚动高度应用到滚动容器上面
watchEffect: 会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。官方:cn.vuejs.org/guide/essen…
// ELEMENT
const element = ref('element')
watchEffect(() => {
if (element.value.style) {
element.value.style.transform = transformScroll.value
}
})
点击指示器
点击指示器的时候,跳转到相应页面,只需设置相应的index即可。
// 点击切换
function changeBac(index) {
// 点击切换时需要开启动画
isTranstion.value = false
$index.value = index
}
全部源码
<script setup>
// IMAGE DATA
const ysImage = 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',
},
])
const asideData = ref([
{
title: '蒙德',
},
{
title: '璃月',
},
{
title: '稻妻',
},
{
title: '须弥',
},
{
title: '枫丹',
},
])
// ELEMENT
const element = ref('element')
watchEffect(() => {
if (element.value.style) {
element.value.style.transform = transformScroll.value
}
})
//HEIGHT
const { height } = useWindowSize()
const windowHeight = computed(() => {
// 高度变化时需要关闭动画
isTranstion.value = true
return height.value
})
const transformScroll = computed(() => {
return `translateY(-${$index.value * windowHeight.value}px)`
})
// ISTRANSTION CANRUN
const isTranstion = ref(false) //控制是否显示动画效果
const canRun = ref(true) //节流控制器
function mousewheel(e) {
isTranstion.value = false
if (canRun.value) {
canRun.value = false
goScroll(e)
setTimeout(() => {
canRun.value = true
}, 500)
}
}
function goScroll(e) {
//e.wheelDelta 用来判断上一个下一个 <0 下一个 >0上一个
if (e.wheelDelta < 0) {
next()
} else {
last()
}
}
//$INDEX
const $index = ref(0) //索引控制第几个显示
// 下一个
function next() {
if ($index.value < 4) {
$index.value++
}
}
// 上一个
function last() {
if ($index.value > 1 || $index.value === 1) {
$index.value--
}
}
// 点击切换
function changeBac(index) {
// 点击切换时需要开启动画
isTranstion.value = false
$index.value = index
}
</script>
<template>
<!-- 最外层容器 -->
<div class="outer-box" ref="fullPage">
<!-- 内层容器 -->
<div ref="element" :class="{ activeTranstion: isTranstion }" class="inner-box" @mousewheel="mousewheel">
<!-- 滚动显示的元素 -->
<div
v-for="item in ysImage"
:style="{ backgroundImage: `url(${item.backgroundImage})` }"
class="scroll-element"
></div>
</div>
<!-- 指示器 -->
<ul class="aside">
<li v-for="(item, index) in asideData" @click="changeBac(index)">
<span :class="{ active: index === $index }"></span>
<div class="show-dec">{{ item.title }}</div>
</li>
</ul>
</div>
</template>
<style lang="scss" scoped>
.activeTranstion {
transition: all 0ms ease 0s !important;
}
.active {
width: 12px !important;
height: 12px !important;
}
.outer-box {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.inner-box {
width: 100%;
height: 100%;
transition: all ease-in-out 1s;
.scroll-element {
height: 100%;
background-size: cover !important;
background-position: center;
background-repeat: no-repeat;
}
}
.aside {
list-style: none;
position: fixed;
right: 20px;
top: 50%;
transform: translateY(-50%);
li {
height: 14px;
width: 14px;
margin: 7px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.show-dec {
position: absolute;
font-size: 10px;
width: 45px;
right: 20px;
opacity: 0;
color: #fff;
transition: all linear 0.1s;
}
span {
border-radius: 100%;
border: #fff solid 1px;
width: 4px;
height: 4px;
display: inline-block;
background-color: #fff;
transition: all ease-in-out 0.2s;
}
&:hover span {
width: 10px;
height: 10px;
background-color: #fff;
cursor: pointer;
}
&:hover .show-dec {
opacity: 1;
}
}
}
}
</style>