仿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>




posted @ 2023-04-11 11:38  yang_nick  阅读(33)  评论(0编辑  收藏  举报