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 document.referrer 用法
Apr 30 Javascript
FileUpload上传图片(图片不变形)
Aug 05 Javascript
imgAreaSelect 中文文档帮助说明
Oct 08 Javascript
jquery插件制作 自增长输入框实现代码
Aug 17 jQuery
JS遍历数组及打印数组实例分析
Jan 21 Javascript
深入理解js generator数据类型
Aug 16 Javascript
jQuery Validate 数组 全部验证问题
Jan 12 Javascript
详解给Vue2路由导航钩子和axios拦截器做个封装
Apr 10 Javascript
vue.js的vue-cli脚手架中使用百度地图API的实例
Jan 21 Javascript
vue路由--网站导航功能详解
Mar 29 Javascript
js获取对象,数组所有属性键值(key)和对应值(value)的方法示例
Jun 19 Javascript
javascript Canvas动态粒子连线
Jan 01 Javascript
如何用JS实现网页瀑布流布局
分享几个JavaScript运算符的使用技巧
Apr 24 #Javascript
JavaScript 防篡改对象的用法示例
Apr 24 #Javascript
jquery插件实现悬浮的菜单
jquery插件实现代码雨特效
Apr 24 #jQuery
jquery插件实现搜索历史
Apr 24 #jQuery
关于Javascript闭包与应用的详解
You might like
输入值/表单提交参数过滤有效防止sql注入的方法
2013/12/25 PHP
微信公众平台天气预报功能开发
2014/07/06 PHP
ThinkPHP中使用ajax接收json数据的方法
2014/12/18 PHP
PHP获取文本框、密码域、按钮的值实例代码
2017/04/19 PHP
PHP使用Nginx实现反向代理
2017/09/20 PHP
goto语法在PHP中的使用教程
2020/09/17 PHP
分享27款非常棒的jQuery 表单插件
2011/03/28 Javascript
ajax 同步请求和异步请求的差异分析
2011/07/04 Javascript
js unicode 编码解析关于数据转换为中文的两种方法
2014/04/21 Javascript
Sortable.js拖拽排序使用方法解析
2016/11/04 Javascript
vue获取DOM元素并设置属性的两种实现方法
2017/09/30 Javascript
Vue.js通用应用框架-Nuxt.js的上手教程
2017/12/25 Javascript
在Vue项目中引入腾讯验证码服务的教程
2018/04/03 Javascript
详解webpack-dev-server使用方法
2018/09/14 Javascript
对Layer UI 模块化的用法详解
2019/09/26 Javascript
Javascript如何实现双指控制图片功能
2020/02/25 Javascript
微信小程序开发搜索功能实现(前端+后端+数据库)
2020/03/04 Javascript
vue 通过base64实现图片下载功能
2020/12/19 Vue.js
[00:02]DOTA2新版本使用PA至宝后暴击展示
2014/11/19 DOTA
Python文件右键找不到IDLE打开项解决办法
2015/06/08 Python
Scrapy的简单使用教程
2017/10/24 Python
Python数据拟合与广义线性回归算法学习
2017/12/22 Python
python中的set实现不重复的排序原理
2018/01/24 Python
python开启摄像头以及深度学习实现目标检测方法
2018/08/03 Python
Python中的取模运算方法
2018/11/10 Python
Python多线程原理与用法实例剖析
2019/01/22 Python
python绘制已知点的坐标的直线实例
2019/07/04 Python
Pytorch 实现冻结指定卷积层的参数
2020/01/06 Python
Python基础之字符串操作常用函数集合
2020/02/09 Python
Python3打包exe代码2种方法实例解析
2020/02/17 Python
python 图像判断,清晰度(明暗),彩色与黑白实例
2020/06/04 Python
文化与传播毕业生求职信
2014/03/09 职场文书
中秋节主持词
2014/04/02 职场文书
休假证明书
2015/06/24 职场文书
教师节主题班会教案
2015/08/17 职场文书
超详细教你怎么升级Mysql的版本
2021/05/19 MySQL