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各类型的关系图解
Oct 16 Javascript
详解Javascript ES6中的箭头函数(Arrow Functions)
Aug 24 Javascript
JS获取多维数组中相同键的值实现方法示例
Jan 06 Javascript
使用jQuery实现动态添加小广告
Jul 11 jQuery
Angular 4.0学习教程之架构详解
Sep 12 Javascript
详解利用 Vue.js 实现前后端分离的RBAC角色权限管理
Sep 15 Javascript
angular 服务的单例模式(依赖注入模式下)详解
Oct 22 Javascript
vue2中引用及使用 better-scroll的方法详解
Nov 15 Javascript
学前端,css与javascript重难点浅析
Jun 11 Javascript
解决Vue router-link绑定事件不生效的问题
Jul 22 Javascript
使用vue引入maptalks地图及聚合效果的实现
Aug 10 Javascript
原生js中运算符及流程控制示例详解
Jan 05 Javascript
如何用JS实现网页瀑布流布局
分享几个JavaScript运算符的使用技巧
Apr 24 #Javascript
JavaScript 防篡改对象的用法示例
Apr 24 #Javascript
jquery插件实现悬浮的菜单
jquery插件实现代码雨特效
Apr 24 #jQuery
jquery插件实现搜索历史
Apr 24 #jQuery
关于Javascript闭包与应用的详解
You might like
php 数组动态添加实现代码(最土团购系统的价格排序)
2011/12/30 PHP
PHP基于SimpleXML生成和解析xml的方法示例
2017/07/17 PHP
jquery 常用操作整理 基础入门篇
2009/10/14 Javascript
方便实用的jQuery checkbox复选框全选功能简单实例
2013/10/09 Javascript
轻松创建nodejs服务器(10):处理上传图片
2014/12/18 NodeJs
jQuery过滤HTML标签并高亮显示关键字的方法
2015/08/07 Javascript
Node.js+Express配置入门教程
2016/05/19 Javascript
onmouseover事件和onmouseout事件全面理解
2016/08/15 Javascript
AngularJS实现单一页面内设置跳转路由的方法
2017/06/28 Javascript
Vue学习笔记进阶篇之单元素过度
2017/07/19 Javascript
详解.vue文件解析的实现
2018/06/11 Javascript
详解JSON和JSONP劫持以及解决方法
2019/03/08 Javascript
微信小程序云开发如何使用npm安装依赖
2019/05/18 Javascript
小程序实现搜索界面 小程序实现推荐搜索列表效果
2019/05/18 Javascript
html+jQuery实现拖动滑块图片拼图验证码插件【移动端适用】
2019/09/10 jQuery
解决React在安装antd之后出现的Can't resolve './locale'问题(推荐)
2020/05/03 Javascript
[42:39]老党炸弹人试玩视频
2014/09/03 DOTA
零基础写python爬虫之爬虫框架Scrapy安装配置
2014/11/06 Python
python获取目录下所有文件的方法
2015/06/01 Python
Saltstack快速入门简单汇总
2016/03/01 Python
Numpy数组的保存与读取方法
2018/04/04 Python
Python全栈之列表数据类型详解
2019/10/01 Python
Python切图九宫格的实现方法
2019/10/10 Python
pyinstaller打包单文件时--uac-admin选项不起作用怎么办
2020/04/15 Python
python将unicode和str互相转化的实现
2020/05/11 Python
HTML5边玩边学(3)像素和颜色
2010/09/21 HTML / CSS
中国领先的汽车保养服务平台:途虎养车
2019/10/18 全球购物
智能钱包:Ekster
2019/11/21 全球购物
简历自我评价模版
2014/01/31 职场文书
面试后的英文感谢信
2014/02/01 职场文书
优乐美广告词
2014/03/14 职场文书
餐厅筹备计划书
2014/04/25 职场文书
幼儿发展评估方案
2014/06/11 职场文书
酒店工程部岗位职责
2015/02/12 职场文书
幼儿园辞职信
2015/05/13 职场文书
分析JVM源码之Thread.interrupt系统级别线程打断
2021/06/29 Java/Android