这个标题有点夺人眼球,哈啊哈~骗点击率的。
“原神”官网当真的做的很漂亮,虽然我没玩过这个游戏,但是禁不住喜欢这个网站啊。
最近居家教学上网课。除了上课,实在不想做学校安排的其它任务,太烦了。果然在家没的工作动力啊~ 但是学习、coding、追番的动力还是有的。
今天来做一个模仿原神官网的全屏滚动效果页面。 以前这种页面用的是 fullpage 插件,网上的文章很多,就不再提它了。今天用原生的JS来写一个,结合了CSS3动画。
本demo页面制作设计了5个板块。读者大大可以根据自己的需要任意增加板块。
- <div class="wrap" id="wrap">
- <div class="section"><span>01span>div>
- <div class="section"><span>02span>div>
- <div class="section"><span>03span>div>
- <div class="section"><span>04span>div>
- <div class="section"><span>05span>div>
- div>
- <div class="ctrlBtns" id="ctrlBtns">
-
- div>
给 span 标签写了个小动画,这么做的目的是扩展后期更复杂的动画需要。
注意:.section.show 是给 section 类设定了一个动画显示类 show,让有这个类的时候,其下的span标签执行一个过渡动画。
以下CSS可以屏蔽掉浏览器的滚动条:
- body{
- overflow: hidden;
- }
为了让 wrap 能丝滑运动,我让它绝对定位了。
完整CSS代码。
- body{
- overflow: hidden;
- }
- .wrap{
- position: absolute;
- width: 100%;
- }
- .section{
- width: 100%;
- height: 100vh;
- font-size: 60px;
- }
- .section:nth-child(1){
- background: #ffdddd;
- }
- .section:nth-child(2){
- background: #ddffe4;
- }
- .section:nth-child(3){
- background: #d7dcfd;
- }
- .section:nth-child(4){
- background: #ffddf1;
- }
- .section:nth-child(5){
- background: #ddf8ff;
- }
-
- .ctrlBtns{
- position: fixed;
- right:20px;
- top:50%;
- width: 20px;
- }
- .ctrlBtns a{
- display: block;
- width: 20px;
- height: 20px;
- background: #fff;
- opacity: 0.8;
- margin-top: 10px;
- margin-bottom: 10px;
- }
- .ctrlBtns a.current{
- background: #f90;
- opacity: 1;
- }
-
- /* 做一个小动画作为示例。可以根据项目需要设计更复杂的动画 */
- .section span{
- transition: all 0.5s;
- display: inline-block; /* 因为 inline 标签变形 transform 无效*/
- }
- .section.show span{
- transform: translateX(300px);
- }
这次代码有几个关键点:
如果浏览器不是强制刷新(Ctrl + F5),而是普通刷新(点击刷新按钮,或者按下 F5 刷新),则页面重新载入完毕后会调到之前访问的位置。
这对全屏页面简直就是个灾难。因为,刷新后一些数据都初始化了。必须让页面回到初始的位置,初始的样子。借用以下代码可以实现 。在页面的任意位置执行下面几行 JS 代码就可以了:
- // 防止浏览器刷新后,停留在当前位置。
- if (history.scrollRestoration) {
- history.scrollRestoration = 'manual';
- }
history.scrollRestoration 有两个属性值:
可以参考我这篇文章:鼠标滚轮事件_stones4zd的博客-CSDN博客
可以参考我这篇文章:https://blog.csdn.net/weixin_42703239/article/details/88764774
let wh = window.innerHeight ;
之前我写过一篇文章,写了帧动画的结束事件,JavaScript 练手小技巧:animationend 事件及其应用小案例_stones4zd的博客-CSDN博客
其实过渡动画也有类似事件:transitionend
切换板块其实就是移动 wrap 标签。移动它,更改的就是它的 top 属性值。
定义一个全局的索引变量 index,top 值就是 index * 窗口高度。
要借用鼠标滚轮事件 wheel 。当滚动鼠标的时候,让 index 加1 或者 减 1 ,进而更改 wrap 的top属性。
重点:当正在滚动的时候,不能再次驱动滚动事件。防止一次性滚动过多。必须等这次滚动动画(本质执行transition过渡动画)执行完毕后,才能开始下一次滚动。
因此,使用了一个开关变量 isScrolling,默认值 true,默认允许滚动。当滚动开始的时候,就要让它为false。滚动结束,就要变为 true。
完整 JavaScript 代码,里面写好了注释:
- let index = 0;
- let wrap = document.getElementById("wrap");
- let ctrlBtns = document.getElementById("ctrlBtns");
- let sections = wrap.getElementsByClassName("section");
- let secNum = sections.length ; // 页面板块个数
- let isScrolling = true; // 滚动开关。true,允许滚动;false,不允许滚动
- /*
- * 开关的目的是,当页面正在滚动的时候,让滚动事件不再执行代码。
- * */
- // 防止浏览器刷新后,停留在当前位置。
- if (history.scrollRestoration) {
- history.scrollRestoration = 'manual';
- }
-
- /*
- * 工具方法:找兄弟标签
- * 参数:目标标签
- * 返回值:目标标签的兄弟标签数组
- * */
- function findSiblings(tag){
- let parent = tag.parentNode;
- let children = parent.children;
- let siblings = [];
- for(let i=0; i<=children.length-1; i++){
- if( children[i] !== tag ){
- siblings.push( children[i] );
- }
- }
- return siblings;
- }
-
- /* 板块切换函数
- * 参数:window.innerHeight 窗口高度
- * index:当前显示的板块的索引值,全局变量
- * */
- function scrollWrap(wh,index){
- isScrolling = false;
- wrap.style.transition ="all 0.5s";
- // 板块切换
- wrap.style.top = `-${wh*index}px`;
- // 控制块切换
- let btnIndex = ctrlBtns.children[index]; // index 对应的控制块
- let sibilingsA = findSiblings(btnIndex); // 它的兄弟们
- // btnIndex 要添加 current,用以突出显示
- btnIndex.classList.add("current");
- // 兄弟们要去掉类 current
- sibilingsA.forEach(function (v,i) {
- v.classList.remove("current");
- })
- }
-
- /*
- * 初始化函数:
- * 初始索引变量 index 为 0
- * 初始 wrap 的 top 值为 0
- * 生成控制块
- * 默认第一个 section 执行动画。
- * */
- function init(){
- index = 0;
- wrap.style.top = 0;
- // 生成控制块
- for(let i=0; i <= secNum-1; i++){
- let a = document.createElement("a");
- a.href = "javascript:void(0)";
- ctrlBtns.appendChild(a);
- }
- let ctrlHeight = ctrlBtns.offsetHeight ;
- // 调整控制块垂直方向的位置。
- ctrlBtns.style.marginTop = - ctrlHeight/2 + "px";
- // 默认控制块第一个 a 为突出显示。
- ctrlBtns.children[0].classList.add("current");
- // 点击控制块,实现板块切换
- for(let i=0; i <= secNum-1; i++){
- ctrlBtns.children[i].addEventListener("click",function(){
- index = i ;
- // 切换板块
- scrollWrap(window.innerHeight,index);
- });
- }
- // 默认第一个标签执行动画
- sections[index].classList.add("show");
- }
- function doScroll(e){
- // 开关为 true:才执行滚动处理函数
- if(isScrolling ){
- let evt = e || window.event;
- let wh = window.innerHeight ;
- evt.preventDefault(); // 阻止浏览器默认事件
- if( evt.deltaY>0 ){
- console.info("页面向上滚动");
- index++;
- if( index >= secNum ){ // 当在最后一页的时候,还要滚动鼠标
- index = secNum-1 ;
- isScrolling = true ;
- wrap.style.transition ="none"; // 去掉过渡效果,防止用户拖拉窗口,画面异常
- }else{
- scrollWrap(wh,index);
- }
- }else{
- console.info("页面向下滚动");
- index--;
- if(index<0){ // 当在第一页的时候,用户还要滚动鼠标
- index = 0; // 保持 index 为 0
- isScrolling = true ; // 允许滚动
- wrap.style.transition ="none"; // 去掉过渡效果,防止用户拖拉窗口,画面异常
- }else{
- scrollWrap(wh,index);
- }
-
- }
- console.info( evt.type,evt.deltaY, index);
- }
-
- }
-
- window.addEventListener("wheel",doScroll,{ passive: false });
- // 让过渡动画执行完毕的时候,就开启开关,允许滚动。
- wrap.addEventListener("transitionend",function(){
- isScrolling = true; // 允许滚动
- wrap.style.transition ="none"; // 去掉过渡效果,防止用户拖拉窗口,画面异常
- // 对应板块显示动画
- let secIndex = sections[index]; // 对应板块
- let secSiblings = findSiblings(secIndex); // 它的兄弟们
- secIndex.classList.add("show");
- // 其它版权要去掉动画效果
- for(let i = 0 ; i<=secSiblings.length-1; i++){
- secSiblings[i].classList.remove("show");
- }
- });
-
- window.addEventListener("resize",function(){
- wrap.style.top = `-${index*window.innerHeight}px`;
- })
-
- init(); // 初始化
哦对了,长安汽车的官网也是全屏滚动的页面。https://www.changan.com.cn/