JavaScript 实现页面滚动动画


Posted in Javascript onApril 24, 2021

在做前端 UI 效果时,让元素根据滚动位置实现动画效果是一个非常流行的设计,通常我们会使用第三方插件或库来实现。在本教程中,我将教大家使用纯 JavaScript 和 CSS 来实现。

先预览一下实现的效果:

JavaScript 实现页面滚动动画

我们使用 CSS 来实现动画,用 JavaScript 来处理触发所需的样式。我们先来创建布局。

创建布局

我们先使用 HTML 创建页面布局,然后为需要实现动画的元素分配一个通用类名,后面的 JavaScript 通过此类名定位这些元素。这里我们给需要根据滚动实现动画的元素指定为类名 js-scroll,HTML 代码如下:

<section class="scroll-container">
  <div class="scroll-element js-scroll"></div>
  <div class="scroll-caption">This animation fades in from the top.</div>
</section>

添加 CSS 样式

先来一个简单的淡入动画效果:

.js-scroll {
  opacity: 0;
  transition: opacity 500ms;
}

.js-scroll.scrolled {
  opacity: 1;
}

页面上的所有 js-scroll 元素都会被隐藏,不透明度为 0。当滚动到该元素区域时,给它加上 .scrolled 类名让它显现出来。

用 JavaScript 操作元素

有了布局和样式,现在我们需要编写一个 JavaScript 函数,当元素滚动到视图中时,为它们分配类名。

我们来简单分解一下逻辑:

  1. 获取页面上所有 js-scroll 元素
  2. 使这些元素默认淡出不可见
  3. 检测元素是否在视窗内
  4. 如果元素在视窗内则分配 scrolled 类名

获取目标元素

获取页面上所有 js-scroll 元素,使用 document.querySelectorAll() 即可:

const scrollElements = document.querySelectorAll('.js-scroll')

默认淡出所有目标元素

遍历这些元素,使其全部淡出不可见:

scrollElements.forEach((el) => {
  el.style.opacity = 0
})

检测元素是否在视窗内

我们可以通过判断元素距离页面顶部的间距是否小于页面可见部分的高度,来检测元素是否在用户视窗中。

在 JavaScript 中,我们使用 getBoundingClientRect().top 方法来获取元素与页面顶部的距离,使用 window.innerHeight document.documentElement.clientHeight 来获取视窗的高度。

JavaScript 实现页面滚动动画

我们将使用上述逻辑创建一个 elementInView 函数:

const elementInView = (el) => {
  const elementTop = el.getBoundingClientRect().top

  return (
    elementTop <= (window.innerHeight || document.documentElement.clientHeight)
  )
}

我们可以修改这个函数来检测元素是否向页面滚动了 x 个像素,或者检测页面滚动的百分比。

const elementInView = (el, scrollOffset = 0) => {
  const elementTop = el.getBoundingClientRect().top

  return (
    elementTop <=
    (window.innerHeight || document.documentElement.clientHeight) - scrollOffset
  )
}

在这种情况下,如果元素已经按 scrollOffset 的数量滚动到页面中,该函数返回 true。我们再稍作修改,把参数 scrollOffset 变成百分比:

const elementInView = (el, percentageScroll = 100) => {
  const elementTop = el.getBoundingClientRect().top

  return (
    elementTop <=
    (window.innerHeight || document.documentElement.clientHeight) *
      (percentageScroll / 100)
  )
}

这部分可以根据自己的特定需求来定义逻辑。

注意:可以使用 Intersection Observer API[2] 来实现同样的效果,但它不支持 IE。

给元素添加类名

现在我们已经能够检测到元素是否已经滚动到页面中,我们需要定义一个函数来处理该元素的显示--本例中我们通过分配 scrolled 类名来显示该元素。

const displayScrollElement = (element) => {
  element.classList.add('scrolled')
}

然后,再把我们前面的逻辑与 displayScrollElement 函数结合起来,并使用 forEach 方法在所有 js-scroll 元素上调用该函数。

const handleScrollAnimation = () => {
  scrollElements.forEach((el) => {
    if (elementInView(el, 100)) {
      displayScrollElement(el)
    }
  })
}

另外,当元素不再在视图中时,需要将其重置为默认状态,我们可以通过定义一个 hideScrollElement 来实现:

const hideScrollElement = (element) => {
  element.classList.remove("scrolled");
};

const handleScrollAnimation = () => {
  scrollElements.forEach((el) => {
    if (elementInView(el, 100)) {
      displayScrollElement(el);
    } else {
      hideScrollElement(el);
    }
  }

最后,我们将把上面的方法传递到窗口的滚动事件监听中,这样每当用户滚动时它就会运行。

window.addEventListener('scroll', () => {
  handleScrollAnimation()
})

我们已经实现了滚动动画的所有功能。

完善示例

请大家回到文章开头看看效果图。看到,这些元素以不同的动画出现。这是通过给类名分配不同的 CSS 动画来实现的。这个示例的 HTML 是这样的:

<section class="scroll-container">
  <div class="scroll-element js-scroll fade-in"></div>
  <div class="scroll-caption">淡入动效</div>
</section>
<section class="scroll-container">
  <div class="scroll-element js-scroll fade-in-bottom"></div>
  <div class="scroll-caption">切入顶部动效</div>
</section>
<section class="scroll-container">
  <div class="scroll-element js-scroll slide-left"></div>
  <div class="scroll-caption">从左边切入动效</div>
</section>
<section class="scroll-container">
  <div class="scroll-element js-scroll slide-right"></div>
  <div class="scroll-caption">从右边切入动效</div>
</section>

这里我们给不同动效的元素分配了不同的 CSS 类名,下面是这些类对应的 CSS 代码:

.scrolled.fade-in {
  animation: fade-in 1s ease-in-out both;
}

.scrolled.fade-in-bottom {
  animation: fade-in-bottom 1s ease-in-out both;
}

.scrolled.slide-left {
  animation: slide-in-left 1s ease-in-out both;
}

.scrolled.slide-right {
  animation: slide-in-right 1s ease-in-out both;
}

@keyframes slide-in-left {
  0% {
    transform: translateX(-100px);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes slide-in-right {
  0% {
    transform: translateX(100px);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes fade-in-bottom {
  0% {
    transform: translateY(50px);
    opacity: 0;
  }
  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

@keyframes fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

虽然加了不同动画元素,但我们不需要修改 JavaScript 代码,因为逻辑保持不变。这意味着我们可以在页面添加任何数量的不同动画,而无需编写新的函数。

利用节流阀提高性能

每当我们在滚动监听器中绑定一个函数时,每次用户滚动页面,该函数都会被调用。滚动一个 500px 的页面会导致一个函数被调用至少 50 次。如果我们试图在页面上包含很多元素,这会导致我们的页面速度明显变慢。

我们可以通过使用“节流函数(Throttle Function)”来减少函数的调用次数。节流函数是一个高阶函数,它在指定的时间间隔内只调用传入的函数一次。

它对于滚动事件特别有用,因为我们不需要检测用户滚动的每个像素。例如,如果我们有一个定时器为 100ms 的节流函数,那么用户每滚动 100ms,该函数将只被调用一次。

节流函数在 JavaScript 中可以这样实现:

let throttleTimer = false

const throttle = (callback, time) => {
  if (throttleTimer) return

  // 这里标记一下,以使函数不会重复执行
  throttleTimer = true

  setTimeout(() => {
    // 到了指定的时间,调用传入的回调函数
    callback()
    throttleTimer = false
  }, time)
}

然后我们可以修改 window 对象上的 scroll 事件监听:

window.addEventListener('scroll', () => {
  throttle(handleScrollAnimation, 250)
})

现在我们的 handleScrollAnimation 函数在用户滚动时每隔 250ms 就会被调用一次:

JavaScript 实现页面滚动动画

以上就是JavaScript 实现页面滚动动画的详细内容,更多关于JavaScript 页面滚动的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
Javascript写了一个清除“logo1_.exe”的杀毒工具(可扫描目录)
Feb 09 Javascript
JS解密入门 最终变量劫持
Jun 25 Javascript
原生javascript兼容性测试实例
Jul 01 Javascript
Jquery的hover方法让鼠标经过li时背景变色
Sep 06 Javascript
js+html5实现canvas绘制镂空字体文本的方法
Jun 05 Javascript
Jquery全选与反选点击执行一次的解决方案
Aug 14 Javascript
创建自己的jquery表格插件
Nov 25 Javascript
AngularJS入门教程之XHR和依赖注入详解
Aug 18 Javascript
详解Angular中$cacheFactory缓存的使用
Aug 19 Javascript
JavaScript中Object基础内部方法图
Feb 05 Javascript
Angularjs实现页面模板清除的方法
Jul 20 Javascript
JavaScript实现网页跨年倒计时
Dec 02 Javascript
如何用JS实现网页瀑布流布局
分享几个JavaScript运算符的使用技巧
Apr 24 #Javascript
JavaScript 防篡改对象的用法示例
Apr 24 #Javascript
jquery插件实现悬浮的菜单
jquery插件实现代码雨特效
Apr 24 #jQuery
jquery插件实现搜索历史
Apr 24 #jQuery
关于Javascript闭包与应用的详解
You might like
PHP多线程抓取网页实现代码
2010/07/22 PHP
ucenter通信原理分析
2015/01/09 PHP
PHP查看当前变量类型的方法
2015/07/31 PHP
php 使用expat方式解析xml文件操作示例
2019/11/26 PHP
JavaScript的目的分析
2007/01/05 Javascript
JS Timing
2007/04/21 Javascript
用jscript实现列出安装的软件列表
2007/06/18 Javascript
js对象之JS入门之Array对象操作小结
2011/01/09 Javascript
基于Jquery与WebMethod投票功能实现代码
2011/01/19 Javascript
使用JS读秒使用示例
2013/09/21 Javascript
JavaScript获取当前页面上的指定对象示例代码
2014/02/28 Javascript
基于JavaScript实现鼠标悬浮弹出跟随鼠标移动的带箭头的信息层
2016/01/18 Javascript
基于javascript实现句子翻牌网页版小游戏
2016/03/23 Javascript
Bootstrap CDN和本地化环境搭建
2016/10/26 Javascript
基于复选框demo(分享)
2017/09/27 Javascript
从源码里了解vue中的nextTick的使用
2018/11/22 Javascript
Node.js中console.log()输出彩色字体的方法示例
2019/12/01 Javascript
15分钟学会vue项目改造成SSR(小白教程)
2019/12/17 Javascript
vue v-on:click传递动态参数的步骤
2020/09/11 Javascript
Vue父子组件传值的一些坑
2020/09/16 Javascript
[00:43]DOTA2小紫本全民票选福利PA至宝全方位展示
2014/11/25 DOTA
[05:24]TI9采访——教练
2019/08/24 DOTA
使用相同的Apache实例来运行Django和Media文件
2015/07/22 Python
Python实现八大排序算法
2016/08/13 Python
Python拼接微信好友头像大图的实现方法
2018/08/01 Python
Python延时操作实现方法示例
2018/08/14 Python
wxPython电子表格功能wx.grid实例教程
2019/11/19 Python
python实现在线翻译功能
2020/03/03 Python
python 输入字符串生成所有有效的IP地址(LeetCode 93号题)
2020/10/15 Python
美国最佳选择产品网站:Best Choice Products
2019/05/27 全球购物
技术人员面试提纲
2013/11/28 职场文书
升职感谢信
2015/01/22 职场文书
2016年小学端午节活动总结
2016/04/01 职场文书
如何用Navicat操作MySQL
2021/05/12 MySQL
分享Python获取本机IP地址的几种方法
2022/03/17 Python
对讲机知识
2022/04/07 无线电