仿fullpage效果
做这个的原因是因为使用fullpage在mac上滑动有一丢丢bug,可能会出现多滑动一次的情况,比如,鼠标就往下滚了一下,但是页面滚动了两页,这就很烦了,正好B站上看到有人讲了下这个效果的实现,发现其实也不难。
特别是如果在vue里面的话,相对会更简单,好了,贴一下具体实现吧,有两个版本,原生和vue3,相对来说,vue3的版本更完善一些
*{
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
//vue3 app.vue
<template>
<div class="container" ref="container" @transitionrun="runHandle" @transitionend="endHandle">
<div class="page" v-for="item in len" :key="item">{{ item }}</div>
</div>
<ul class="controls">
<li
:class="{ active: index === currentIndex }"
v-for="(item, index) in len"
:key="item"
@click="chooseHandle(index)"
>
{{ item }}
</li>
</ul>
</template>
<script setup lang="ts">
import { onMounted, ref, watch, watchEffect } from 'vue'
const len = ref(6)
// 当前所处的编号
const currentIndex = ref<number>(3)
//获取页面高度
const viewHeight = document.body.clientHeight
//是否滚动完成
const isScrolled = ref<boolean>(true)
const container = ref<HTMLDivElement | null>(null)
onMounted(() => {
init()
f()
})
function init() {
chooseHandle(currentIndex.value)
}
function runHandle() {
console.log('runHandle')
isScrolled.value = false
}
function endHandle() {
console.log('endHandle')
isScrolled.value = true
}
function chooseHandle(index: number) {
currentIndex.value = index
// ;(container.value as HTMLDivElement).style.top = -currentIndex.value * viewHeight + 'px'
transformY()
}
function setContainerHeight(flag: number): void {
//这个函数的作用就是通过最终判断的上或者下,确定当前的index的值,然后移动对应的距离
/*
* 需要判断滚动方向
* */
if (
(currentIndex.value === len.value - 1 && flag > 0) ||
(currentIndex.value === 0 && flag < 0)
) {
return
}
console.log(2222)
currentIndex.value += flag
console.log(currentIndex.value)
if (currentIndex.value > len.value - 1) {
currentIndex.value = len.value - 1
} else if (currentIndex.value < 0) {
currentIndex.value = 0
}
console.log(currentIndex.value, '----currentIndex.value---')
// if (container.value) {
// ;(container.value as HTMLDivElement).style.top = -currentIndex.value * viewHeight + 'px'
// }
transformY()
}
function transformY() {
if (container.value) {
// ;(container.value as HTMLDivElement).style.top = -currentIndex.value * viewHeight + 'px'
// transform: translate3d(0,100px,0);
;(container.value as HTMLDivElement).style.transform = `translate3d(0,${-currentIndex.value * viewHeight}px,0)`
}
}
//鼠标滚轮
document.addEventListener('wheel', (e: WheelEvent) => {
//如果在滚动过程中,那么不接收鼠标滚动操作
if (!isScrolled.value) {
return
}
// console.log(e.deltaY, 'e.deltaY') //负值,向上;正值,向下。
let flag = e.deltaY > 0 ? 1 : -1
setContainerHeight(flag)
})
//键盘上下键
document.addEventListener('keydown',(e)=>{
// console.log(e);
// console.log(e,'键盘事件');
let key=e.key
switch (key){
case 'ArrowUp':
console.log('ArrowUp');
setContainerHeight(-1)
break;
case 'ArrowDown':
setContainerHeight(1)
break;
}
})
//还需要适配手机端
let startDistanceY=ref(0)
let endDistanceY=ref(0)
let moveDistanceY=ref(0)
document.addEventListener('touchstart',(e:TouchEvent)=>{
// console.log(e);
startDistanceY.value=e.touches[0].clientY
})
/*document.addEventListener('touchmove',(e)=>{
// console.log(e);
//TODO 让滑动能够跟手
if(currentIndex.value<=0||currentIndex.value>=len.value-1){
return
}
let dis=e.changedTouches[0].clientY- startDistanceY.value
console.log(dis,'move--');
if (container.value) {
;(container.value as HTMLDivElement).style.top = -(currentIndex.value * viewHeight-dis) + 'px'
}
})*/
document.addEventListener('touchend',(e:TouchEvent)=>{
console.log(e,'end');
endDistanceY.value=e.changedTouches[0].clientY
moveDistanceY.value = startDistanceY.value - endDistanceY.value
console.log(moveDistanceY.value);
//上划为正,下滑为负
let flag = moveDistanceY.value > 0 ? 1 : -1
setContainerHeight(flag)
})
window.addEventListener('resize',()=>{
//需要刷新一下
console.log('resize');
// location.reload()
})
function f() {
var rAF = function () {
return (
window.requestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
}
);
}();
var frame = 0;
var allFrameCount = 0;
var lastTime = Date.now();
var lastFameTime = Date.now();
var loop = function () {
var now = Date.now();
var fs = (now - lastFameTime);
var fps = Math.round(1000 / fs);
lastFameTime = now;
// 不置 0,在动画的开头及结尾记录此值的差值算出 FPS
allFrameCount++;
frame++;
if (now > 1000 + lastTime) {
fps = Math.round((frame * 1000) / (now - lastTime));
console.log(`${new Date()} 1S内 FPS:`, fps);
frame = 0;
lastTime = now;
};
rAF(loop);
}
loop();
}
</script>
<style scoped lang="scss">
.container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
transition: all 0.25s ease-in-out;
.page {
height: 100%;
&:nth-of-type(1) {
background-color: #502c3c;
}
&:nth-of-type(2) {
background-color: #307aa8;
}
&:nth-of-type(3) {
background-color: #7ac024;
}
&:nth-of-type(4) {
background-color: #d9186e;
}
&:nth-of-type(5) {
background-color: #68170f;
}
&:nth-of-type(6) {
background-color: #30a844;
}
}
}
.controls {
position: absolute;
top: 50%;
right: 20px;
transform: translateY(-50%);
list-style: none;
}
.controls li {
cursor: pointer;
width: 50px;
height: 50px;
text-align: center;
background-color: #000;
color: #fff;
font: bold 22px/50px '宋体'; /*字号22px 行高50px*/
}
.controls li + li {
margin-top: 5px;
}
.controls li.active {
background-color: #fff;
color: red;
}
</style>
----------------------下面是原生实现------------------------------
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
/*line-height: 1;*/
}
html,body{
width: 100%;
height: 100%;
overflow: hidden;
}
.container{
width: 100%;
height: 500%;
position: absolute;
top: 0;
transition: all 0.5s ease;
}
.section{
width: 100%;
height: 20%;
display: flex;
justify-content: center;
align-items: center;
}
.section1{
background-color: red;
}
.section2{
background-color: #d9186e;
}
.section3{
background-color: #7ac024;
}
.section4{
background-color: #307aa8;
}
.section5{
background-color: #502c3c;
}
.controls{
position: absolute;
top: 50%;
right: 20px;
transform: translateY(-50%);
list-style: none;
}
.controls li{
width: 50px;
height: 50px;
text-align: center;
background-color: #000;
color: #fff;
font: bold 22px/50px "宋体";/*字号22px 行高50px*/
}
.controls li+li{
margin-top: 5px;
}
.controls li.active{
background-color: #fff;
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="section section1" ><h1>第一屏</h1> </div>
<div class="section section2" ><h1>第2屏</h1> </div>
<div class="section section3" ><h1>第3屏</h1> </div>
<div class="section section4" ><h1>第4屏</h1> </div>
<div class="section section5" ><h1>第5屏</h1> </div>
</div>
<ul class="controls">
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
const containerEl=document.querySelector('.container')
const controlsEl=document.querySelector('.controls')
const lis=document.querySelectorAll('.controls li')
console.log(lis);
const clientHeight=document.body.clientHeight
console.log(clientHeight,'clientHeight----');
let currentIndex=0
lis.forEach((item,index)=>{
item.setAttribute('tag',index)
})
let flag=true
containerEl.addEventListener('transition',()=>{
console.log('transition');
// flag=true
})
containerEl.addEventListener('transitionend',()=>{
console.log('transitionend');
flag=true
})
controlsEl.addEventListener('click',(e)=>{
removeControlsStyle()
if(e.target.nodeName.toLowerCase() === 'li'){
e.target.classList.add('active')
//还要滑动页面
let index=e.target.getAttribute('tag')*1
console.log(e.target,index);
currentIndex=index
setContainerStyle(index)
}
})
function removeControlsStyle() {
lis.forEach((item)=>{
item.classList.remove('active')
})
}
function setContainerStyle(index) {
containerEl.style.top=-clientHeight*index+'px'
}
// function debounce(fn, wait) {
// var timeout = null;
// return function () {
// if (timeout !== null) clearTimeout(timeout);
// timeout = setTimeout(fn, wait);
// }
// }
function debounce(fn, delay, isInit = false) {
let timer = null;
//用户是否首次输入
let initInt = true;
return function (...args) {
// 如果用户传入了true并且是首次输入
if (isInit && initInt) {
// 如果是首次输入,则立即执行
fn.apply(this, args);
// 将立即执行变量置为false,以便进入到防抖的代码中
initInt = false;
} else {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
// 将立即执行变量为true,以便下次进入到立即执行的代码里
initInt = true;
}, delay);
}
};
}
document.addEventListener('mousewheel',(e)=>{
console.log(e.target);
console.log(e.wheelDelta);
let delta=e.wheelDelta
if(!flag) return
wheelHandle(delta)
})
/*
* keyCode 38 = Up
* keyCode 40 = Down
* */
document.addEventListener('keydown',(e)=>{
console.log(e,'键盘事件');
let key=e.key
switch (key){
case 'ArrowUp':
currentIndex--
if(currentIndex>4){
currentIndex=4
}
if(currentIndex<0){
currentIndex=0
}
break;
case 'ArrowDown':
currentIndex++
if(currentIndex>4){
currentIndex=4
}
if(currentIndex<0){
currentIndex=0
}
break;
}
setContainerStyle(currentIndex)
removeControlsStyle()
lis[currentIndex].classList.add('active')
})
function wheelHandle(delta) {
flag=false
// let retio= delta/clientHeight
// console.log(retio,'retio');
let radio=delta>0?1:-1
console.log(radio,'radio');
//currentIndex 0-4
currentIndex-=radio
if(currentIndex>4){
currentIndex=4
flag=true
}
if(currentIndex<0){
currentIndex=0
flag=true
}
setContainerStyle(currentIndex)
removeControlsStyle()
lis[currentIndex].classList.add('active')
// for (let i=0;i<lis.length;i++){
//
// }
}
</script>
</body>
</html>