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 相关文章推荐
在Z-Blog中运行代码[html][/html](纯JS版)
Mar 25 Javascript
jQuery 源码分析笔记(7) Queue
Jun 19 Javascript
js Array操作的最简短最容易理解方法
Dec 09 Javascript
Jquery创建一个层当鼠标移动到层上面不消失效果
Dec 12 Javascript
IE下使用jQuery重置iframe地址时内存泄露问题解决办法
Feb 05 Javascript
Javascript封装id、class与元素选择器方法示例
Mar 13 Javascript
js实现自定义进度条效果
Mar 15 Javascript
Vue2 SSR渲染根据不同页面修改 meta
Nov 20 Javascript
利用jqprint插件打印页面内容的实现方法
Jan 09 Javascript
Vue中&quot;This dependency was not found&quot;问题的解决方法
Jun 19 Javascript
微信小程序云开发(数据库)详解
May 17 Javascript
canvas实现贪食蛇的实践
Feb 15 Javascript
如何用JS实现网页瀑布流布局
分享几个JavaScript运算符的使用技巧
Apr 24 #Javascript
JavaScript 防篡改对象的用法示例
Apr 24 #Javascript
jquery插件实现悬浮的菜单
jquery插件实现代码雨特效
Apr 24 #jQuery
jquery插件实现搜索历史
Apr 24 #jQuery
关于Javascript闭包与应用的详解
You might like
Laravel路由研究之domain解决多域名问题的方法示例
2019/04/04 PHP
Yii2框架控制器、路由、Url生成操作示例
2019/05/27 PHP
线路分流自动跳转代码;希望对大家有用!
2006/12/02 Javascript
jQuery 学习第五课 Ajax 使用说明
2010/05/17 Javascript
javascript读写XML实现广告轮换(兼容IE、FF)
2013/08/09 Javascript
屏蔽相应键盘按钮操作
2014/03/10 Javascript
JavaScript实现对下拉列表值进行排序的方法
2015/07/15 Javascript
JavaScript脚本判断蜘蛛来源的方法
2015/09/22 Javascript
jQuery常用的一些技巧汇总
2016/03/26 Javascript
JavaScript SHA1加密算法实现详细代码
2016/10/06 Javascript
微信小程序 MD5加密登录密码详解及实例代码
2017/01/12 Javascript
Bootstrap table 定制提示语的加载过程
2017/02/20 Javascript
关于使用js算总价的问题
2017/06/23 Javascript
探索浏览器页面关闭window.close()的使用详解
2020/08/21 Javascript
js实现缓动动画
2020/11/25 Javascript
[01:59]翻天覆地,因你而变,7.20版本地图更新速览
2018/11/24 DOTA
python实现井字棋游戏
2020/03/30 Python
python 统计数组中元素出现次数并进行排序的实例
2018/07/02 Python
python中单例常用的几种实现方法总结
2018/10/13 Python
[原创]Python入门教程1. 基本运算【四则运算、变量、math模块等】
2018/10/28 Python
docker-py 用Python调用Docker接口的方法
2019/08/30 Python
浅析Python3 pip换源问题
2020/01/06 Python
Python使用py2neo操作图数据库neo4j的方法详解
2020/01/13 Python
next在python中返回迭代器的实例方法
2020/12/15 Python
Python爬虫实现selenium处理iframe作用域问题
2021/01/27 Python
销售工作岗位职责
2013/12/24 职场文书
电气自动化个人求职信范文
2014/02/03 职场文书
GMP办公室主任岗位职责
2014/03/14 职场文书
《生命的药方》教学反思
2014/04/08 职场文书
初三毕业评语
2014/12/26 职场文书
2016年重阳节慰问信
2015/12/01 职场文书
2016小学教师读书心得体会
2016/01/13 职场文书
公司致全体员工的感谢信
2019/06/24 职场文书
python批量创建变量并赋值操作
2021/06/03 Python
Redis如何实现验证码发送 以及限制每日发送次数
2022/04/18 Redis
Win10鼠标宏怎么设置?win10系统鼠标宏的设置方法
2022/08/14 数码科技