• JavaScript练手小技巧:我破解了原神官网全屏滚动的秘密


    这个标题有点夺人眼球,哈啊哈~骗点击率的。

    “原神”官网当真的做的很漂亮,虽然我没玩过这个游戏,但是禁不住喜欢这个网站啊。

    https://ys.mihoyo.com/

    最近居家教学上网课。除了上课,实在不想做学校安排的其它任务,太烦了。果然在家没的工作动力啊~ 但是学习、coding、追番的动力还是有的。

    今天来做一个模仿原神官网的全屏滚动效果页面。 以前这种页面用的是 fullpage 插件,网上的文章很多,就不再提它了。今天用原生的JS来写一个,结合了CSS3动画

    一、HTML结构

    本demo页面制作设计了5个板块。读者大大可以根据自己的需要任意增加板块。

    1. <div class="wrap" id="wrap">
    2. <div class="section"><span>01span>div>
    3. <div class="section"><span>02span>div>
    4. <div class="section"><span>03span>div>
    5. <div class="section"><span>04span>div>
    6. <div class="section"><span>05span>div>
    7. div>
    8. <div class="ctrlBtns" id="ctrlBtns">
    9. div>

    二、CSS样式

    给 span 标签写了个小动画,这么做的目的是扩展后期更复杂的动画需要。

    注意.section.show 是给 section 类设定了一个动画显示类 show让有这个类的时候,其下的span标签执行一个过渡动画。

    以下CSS可以屏蔽掉浏览器的滚动条:

    1. body{
    2. overflow: hidden;
    3. }

    为了让 wrap 能丝滑运动,我让它绝对定位了。

    完整CSS代码。 

    1. body{
    2. overflow: hidden;
    3. }
    4. .wrap{
    5. position: absolute;
    6. width: 100%;
    7. }
    8. .section{
    9. width: 100%;
    10. height: 100vh;
    11. font-size: 60px;
    12. }
    13. .section:nth-child(1){
    14. background: #ffdddd;
    15. }
    16. .section:nth-child(2){
    17. background: #ddffe4;
    18. }
    19. .section:nth-child(3){
    20. background: #d7dcfd;
    21. }
    22. .section:nth-child(4){
    23. background: #ffddf1;
    24. }
    25. .section:nth-child(5){
    26. background: #ddf8ff;
    27. }
    28. .ctrlBtns{
    29. position: fixed;
    30. right:20px;
    31. top:50%;
    32. width: 20px;
    33. }
    34. .ctrlBtns a{
    35. display: block;
    36. width: 20px;
    37. height: 20px;
    38. background: #fff;
    39. opacity: 0.8;
    40. margin-top: 10px;
    41. margin-bottom: 10px;
    42. }
    43. .ctrlBtns a.current{
    44. background: #f90;
    45. opacity: 1;
    46. }
    47. /* 做一个小动画作为示例。可以根据项目需要设计更复杂的动画 */
    48. .section span{
    49. transition: all 0.5s;
    50. display: inline-block; /* 因为 inline 标签变形 transform 无效*/
    51. }
    52. .section.show span{
    53. transform: translateX(300px);
    54. }

     三、JavaScript代码

    这次代码有几个关键点:

    1. 防止浏览器刷新后,停留在当前位置

    如果浏览器不是强制刷新(Ctrl + F5),而是普通刷新(点击刷新按钮,或者按下 F5 刷新),则页面重新载入完毕后会调到之前访问的位置。

    这对全屏页面简直就是个灾难。因为,刷新后一些数据都初始化了。必须让页面回到初始的位置,初始的样子。借用以下代码可以实现 。在页面的任意位置执行下面几行 JS 代码就可以了:

    1. // 防止浏览器刷新后,停留在当前位置。
    2. if (history.scrollRestoration) {
    3. history.scrollRestoration = 'manual';
    4. }

    history.scrollRestoration 有两个属性值:

    • auto:默认值,表示滚动位置会被存储。
    • manual:单词的意思就是手动。表示,滚动的位置不会被存储。

    2. 鼠标滚轮事件

    可以参考我这篇文章:鼠标滚轮事件_stones4zd的博客-CSDN博客

    3. 获取兄弟标签

     可以参考我这篇文章:https://blog.csdn.net/weixin_42703239/article/details/88764774

    4. 获取窗口高度

    let wh = window.innerHeight ;

     5. 过渡动画结束事件

    之前我写过一篇文章,写了帧动画的结束事件,JavaScript 练手小技巧:animationend 事件及其应用小案例_stones4zd的博客-CSDN博客
    

    其实过渡动画也有类似事件:transitionend

     6. 思路

    • 整个效果重点是切换板块

    切换板块其实就是移动 wrap 标签。移动它,更改的就是它的 top 属性值。

    定义一个全局的索引变量 index,top 值就是 index * 窗口高度

    • 整个效果是通过滚动鼠标滚轮驱动

    要借用鼠标滚轮事件 wheel 。当滚动鼠标的时候,让 index 加1 或者 减 1 ,进而更改 wrap 的top属性。

    重点:当正在滚动的时候,不能再次驱动滚动事件。防止一次性滚动过多。必须等这次滚动动画(本质执行transition过渡动画)执行完毕后,才能开始下一次滚动。

    因此,使用了一个开关变量 isScrolling,默认值 true,默认允许滚动。当滚动开始的时候,就要让它为false。滚动结束,就要变为 true。

    完整 JavaScript 代码,里面写好了注释:

    1. let index = 0;
    2. let wrap = document.getElementById("wrap");
    3. let ctrlBtns = document.getElementById("ctrlBtns");
    4. let sections = wrap.getElementsByClassName("section");
    5. let secNum = sections.length ; // 页面板块个数
    6. let isScrolling = true; // 滚动开关。true,允许滚动;false,不允许滚动
    7. /*
    8. * 开关的目的是,当页面正在滚动的时候,让滚动事件不再执行代码。
    9. * */
    10. // 防止浏览器刷新后,停留在当前位置。
    11. if (history.scrollRestoration) {
    12. history.scrollRestoration = 'manual';
    13. }
    14. /*
    15. * 工具方法:找兄弟标签
    16. * 参数:目标标签
    17. * 返回值:目标标签的兄弟标签数组
    18. * */
    19. function findSiblings(tag){
    20. let parent = tag.parentNode;
    21. let children = parent.children;
    22. let siblings = [];
    23. for(let i=0; i<=children.length-1; i++){
    24. if( children[i] !== tag ){
    25. siblings.push( children[i] );
    26. }
    27. }
    28. return siblings;
    29. }
    30. /* 板块切换函数
    31. * 参数:window.innerHeight 窗口高度
    32. * index:当前显示的板块的索引值,全局变量
    33. * */
    34. function scrollWrap(wh,index){
    35. isScrolling = false;
    36. wrap.style.transition ="all 0.5s";
    37. // 板块切换
    38. wrap.style.top = `-${wh*index}px`;
    39. // 控制块切换
    40. let btnIndex = ctrlBtns.children[index]; // index 对应的控制块
    41. let sibilingsA = findSiblings(btnIndex); // 它的兄弟们
    42. // btnIndex 要添加 current,用以突出显示
    43. btnIndex.classList.add("current");
    44. // 兄弟们要去掉类 current
    45. sibilingsA.forEach(function (v,i) {
    46. v.classList.remove("current");
    47. })
    48. }
    49. /*
    50. * 初始化函数:
    51. * 初始索引变量 index 为 0
    52. * 初始 wrap 的 top 值为 0
    53. * 生成控制块
    54. * 默认第一个 section 执行动画。
    55. * */
    56. function init(){
    57. index = 0;
    58. wrap.style.top = 0;
    59. // 生成控制块
    60. for(let i=0; i <= secNum-1; i++){
    61. let a = document.createElement("a");
    62. a.href = "javascript:void(0)";
    63. ctrlBtns.appendChild(a);
    64. }
    65. let ctrlHeight = ctrlBtns.offsetHeight ;
    66. // 调整控制块垂直方向的位置。
    67. ctrlBtns.style.marginTop = - ctrlHeight/2 + "px";
    68. // 默认控制块第一个 a 为突出显示。
    69. ctrlBtns.children[0].classList.add("current");
    70. // 点击控制块,实现板块切换
    71. for(let i=0; i <= secNum-1; i++){
    72. ctrlBtns.children[i].addEventListener("click",function(){
    73. index = i ;
    74. // 切换板块
    75. scrollWrap(window.innerHeight,index);
    76. });
    77. }
    78. // 默认第一个标签执行动画
    79. sections[index].classList.add("show");
    80. }
    81. function doScroll(e){
    82. // 开关为 true:才执行滚动处理函数
    83. if(isScrolling ){
    84. let evt = e || window.event;
    85. let wh = window.innerHeight ;
    86. evt.preventDefault(); // 阻止浏览器默认事件
    87. if( evt.deltaY>0 ){
    88. console.info("页面向上滚动");
    89. index++;
    90. if( index >= secNum ){ // 当在最后一页的时候,还要滚动鼠标
    91. index = secNum-1 ;
    92. isScrolling = true ;
    93. wrap.style.transition ="none"; // 去掉过渡效果,防止用户拖拉窗口,画面异常
    94. }else{
    95. scrollWrap(wh,index);
    96. }
    97. }else{
    98. console.info("页面向下滚动");
    99. index--;
    100. if(index<0){ // 当在第一页的时候,用户还要滚动鼠标
    101. index = 0; // 保持 index 为 0
    102. isScrolling = true ; // 允许滚动
    103. wrap.style.transition ="none"; // 去掉过渡效果,防止用户拖拉窗口,画面异常
    104. }else{
    105. scrollWrap(wh,index);
    106. }
    107. }
    108. console.info( evt.type,evt.deltaY, index);
    109. }
    110. }
    111. window.addEventListener("wheel",doScroll,{ passive: false });
    112. // 让过渡动画执行完毕的时候,就开启开关,允许滚动。
    113. wrap.addEventListener("transitionend",function(){
    114. isScrolling = true; // 允许滚动
    115. wrap.style.transition ="none"; // 去掉过渡效果,防止用户拖拉窗口,画面异常
    116. // 对应板块显示动画
    117. let secIndex = sections[index]; // 对应板块
    118. let secSiblings = findSiblings(secIndex); // 它的兄弟们
    119. secIndex.classList.add("show");
    120. // 其它版权要去掉动画效果
    121. for(let i = 0 ; i<=secSiblings.length-1; i++){
    122. secSiblings[i].classList.remove("show");
    123. }
    124. });
    125. window.addEventListener("resize",function(){
    126. wrap.style.top = `-${index*window.innerHeight}px`;
    127. })
    128. init(); // 初始化

    哦对了,长安汽车的官网也是全屏滚动的页面。https://www.changan.com.cn/

  • 相关阅读:
    数据库基本概念
    Unity3D 关于过大的UI帧动画如何处理详解
    河北工业大学数据挖掘实验四 贝叶斯决策分类算法
    打造安全的Open RAN
    什么是平台即服务 (PaaS)?定义、示例、组件和最佳实践
    1113: 递归调用的次数统计(函数专题)
    EraseNet:End-to-End Text Removal in the wild
    NVMe SSD 学习总结:04 为什么NVME的SSD越来越受欢迎?
    java-php-net-python-绥化市北林区房屋拆迁管理信息管理系统计算机毕业设计程序
    Nginx网络服务三-----(三方模块和内置变量)
  • 原文地址:https://blog.csdn.net/weixin_42703239/article/details/127956702