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下function声明一些小结
Dec 28 Javascript
复选框全选与全不选操作实现思路
Aug 18 Javascript
分享一则javascript 调试技巧
Jan 02 Javascript
jQuery插件formValidator自定义函数扩展功能实例详解
Nov 25 Javascript
创建一般js对象的几种方式
Jan 19 Javascript
关于使用js算总价的问题
Jun 23 Javascript
Vue.js实现的计算器功能完整示例
Jul 11 Javascript
JS实现textarea通过换行或者回车把多行数字分割成数组并且去掉数组中空的值
Oct 29 Javascript
Node.js之readline模块的使用详解
Mar 25 Javascript
浅析vue-cli3配置webpack-bundle-analyzer插件【推荐】
Oct 23 Javascript
微信小程序获取公众号文章列表及显示文章的示例代码
Mar 10 Javascript
JavaScript如何实现防止重复的网络请求的示例
Jan 28 Javascript
如何用JS实现网页瀑布流布局
分享几个JavaScript运算符的使用技巧
Apr 24 #Javascript
JavaScript 防篡改对象的用法示例
Apr 24 #Javascript
jquery插件实现悬浮的菜单
jquery插件实现代码雨特效
Apr 24 #jQuery
jquery插件实现搜索历史
Apr 24 #jQuery
关于Javascript闭包与应用的详解
You might like
php项目打包方法
2008/02/18 PHP
Window下PHP三种运行方式图文详解
2013/06/11 PHP
双冒号 ::在PHP中的使用情况
2015/11/05 PHP
php的单例模式及应用场景详解
2021/02/27 PHP
JavaScript 中的replace方法说明
2007/04/13 Javascript
如何在一个页面显示多个百度地图
2013/04/07 Javascript
div+css+js实现无缝滚动类似marquee无缝滚动兼容firefox
2013/08/29 Javascript
jQuery学习笔记之jQuery中的$
2015/01/19 Javascript
javascript实现获取字符串hash值
2015/05/10 Javascript
Javascript highcharts 饼图显示数量和百分比实例代码
2016/12/06 Javascript
浅谈react前后端同构渲染
2017/09/20 Javascript
微信小程序动画组件使用解析,类似vue,且更强大
2019/08/01 Javascript
解决layui 三级联动下拉框更新时回显的问题
2019/09/03 Javascript
使用axios发送post请求,将JSON数据改为form类型的示例
2019/10/31 Javascript
vue中的过滤器及其时间格式化问题
2020/04/09 Javascript
[00:44]华丽开场!DOTA2勇士令状带来全新对阵画面
2019/05/15 DOTA
Python使用MONGODB入门实例
2015/05/11 Python
在Python中操作列表之list.extend()方法的使用
2015/05/20 Python
python实现给数组按片赋值的方法
2015/07/28 Python
Python for Informatics 第11章之正则表达式(四)
2016/04/21 Python
Python多层嵌套list的递归处理方法(推荐)
2016/06/08 Python
Python实现购物车功能的方法分析
2017/11/10 Python
TensorFlow 实战之实现卷积神经网络的实例讲解
2018/02/26 Python
Python3.6通过自带的urllib通过get或post方法请求url的实例
2018/05/10 Python
Jacobi迭代算法的Python实现详解
2019/06/29 Python
pycharm 关掉syntax检查操作
2020/06/09 Python
Python定时任务框架APScheduler原理及常用代码
2020/10/05 Python
利用python查看数组中的所有元素是否相同
2021/01/08 Python
美国在线肉类和海鲜配送:Crowd Cow
2020/10/02 全球购物
社团2014年植树节活动总结
2014/03/11 职场文书
党风廉政建设责任书
2014/04/14 职场文书
项目合作协议书
2014/04/16 职场文书
成功的商业计划书这样写才最靠谱
2019/07/12 职场文书
关于感恩的素材句子(38句)
2019/11/11 职场文书
如何利用golang运用mysql数据库
2022/03/13 Golang
利用Python将list列表写入文件并读取的方法汇总
2022/03/25 Python