实现元素进入界面的平滑效果

news2024/12/29 9:51:35

先看效果:
在这里插入图片描述
实现思路:获取页面中需要加载动画的节点,用元素的animate()方法创建一个动画对象,并传入两个关键帧,接着使用IntersectionObserverAPI创建观察对象,用于观察元素进入页面。当元素进入界面时,执行动画,最后取消元素的观察即可。

具体实现:

第一步:

<div class="container">
   <div class="module">模块1</div>
   <div class="module">模块2</div>
   <div class="module">模块3</div>
   <div class="module">模块4</div>
   <div class="module">模块5</div>
   <div class="module">模块6</div>
   <div class="module">模块7</div>
   <div class="module">模块8</div>
   <div class="module">模块9</div>
   <div class="module">模块10</div>
 </div>


<script>
const DISTANCE = 100;
onload = function() {
// 页面加载完毕,获取到所有需要加载动画的节点
   document.querySelectorAll('.module').forEach(module => {
   // 循环调用每个模块的animate方法。用法:animate(keyframes, options)
   // keyframes 关键帧对象数组,或一个关键帧对象(其属性为可迭代值的数组)
   // options 代表动画持续时间的整数(以毫秒为单位),或者一个包含一个或多个时间属性的对象
     let animation = module.animate([
       {
         transform: `translateY(${DISTANCE}px)`,
         opacity: 0
       },
       {
         transform: 'translateY(0)',
         opacity: 1
       }
     ], {
       duration: 1000,
       easing: 'ease-in-out'
     })
     animation.pause() // 创建好animation对象后,首先暂停动画的执行,待会儿用监听器监听元素进入界面后开启动画
   })
 }
</script>

第二步:

// 创建一个WeakMap对象,用于存储节点及动画对象,WeakMap是为了防止内存泄漏,当元素消失时,元素会自动销毁在对象中的存储
let map = new WeakMap()
// 创建一个监视器
let observer = new IntersectionObserver((entries, observer) => {
// entries是获取到的所有监听对象
  entries.forEach(entry => {
  // 判断当元素进入到界面
    if (entry.isIntersecting) {
    // 获取每个元素上的动画对象
      const animation = map.get(entry.target)
      // 当动画存在时,执行动画,并取消元素进入界面的观察
      if(animation) {
        animation.play()
        observer.unobserve(entry.target)
      }
    }
  });
});

onload = function() {
   document.querySelectorAll('.module').forEach(module => {
     let animation = module.animate([
       {
         transform: `translateY(${DISTANCE}px)`,
         opacity: 0
       },
       {
         transform: 'translateY(0)',
         opacity: 1
       }
     ], {
       duration: 1000,
       easing: 'ease-in-out'
     })
     animation.pause()
     // 用上方创建的监听器 观察每一个模块节点
     observer.observe(module)
     // 将每个节点及动画存储对象中
     map.set(module, animation)
   })
 }

最后一步:

解决关键性问题,当滚动条滚动到界面中间时,刷新界面后,不论是往上滚还是往下滚,再次进入界面的元素都会执行动画。当然我们所需要的是往下滚动时下面的元素进入界面需要加载动画,而上方的元素进入界面不需要动画。那么我们就可以这样写:

let map = new WeakMap()

    let observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const animation = map.get(entry.target)
          if(animation) {
            animation.play()
            observer.unobserve(entry.target)
          }
        }
      });
    });

    const DISTANCE = 100
    function isViewport(element) {
      const rect = element.getBoundingClientRect();
      return rect.top - DISTANCE > window.innerHeight
    }

    onload = function() {
      document.querySelectorAll('.module').forEach(module => {
        // 判断节点是否在视口内
        if(!isViewport(module)) return;
        let animation = module.animate([
          {
            transform: `translateY(${DISTANCE}px)`,
            opacity: 0
          },
          {
            transform: 'translateY(0)',
            opacity: 1
          }
        ], {
          duration: 1000,
          easing: 'ease-in-out'
        })
        animation.pause()
        observer.observe(module)
        map.set(module, animation)
      })
    }

结尾:上面的示例是在html文件中完成的,当然你也可以在Vue中实现,在Vue中,你可以自定义指令,通过获取到的el对象绑定动画、观察,甚至是通过绑定的指令传入动画的参数,来动态执行动画。

为什么需要使用animate方法,而不是在节点的style中添加动画?

因为我们是要封装公用指令、组件,如果我们继续使用style,向其添加动画,有可能和节点原有的动画冲突,为了不对元素本身的动画照成影响,我们可以使用AnimationAPI,它的好处就在于 不会改变元素的DOM树,不会改变元素本身的属性,这样就可以避免两者的冲突。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1418279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sqli.labs靶场(第18~22关)

18、第十八关 经过测试发现User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0加引号报错 这里我们闭合一下试试 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0,127.0.0.1,adm…

漏洞原理linux操作系统的SqlMap工具的使用

漏洞原理linux操作系统的SqlMap工具的使用 Linux操作系统基础操作链接: 1024一篇通俗易懂的liunx命令操作总结(第十课)-CSDN博客 kali的IP地址:192.168.56.1 实操 # kali中使用sqlmap http://192.168.56.1/ sqlmap -u http://192.168.56.1/news/show.php?id46 sqlmap -u …

线程锁多线程的复习

线程 实现方式3种乐观锁&悲观锁线程池线程池总结 进程:是正在运行的程序 线程:是进程中的单个顺序控制流,是一条执行路径 实现方式3种 1.Thread //步骤一:定义一个继承Thread的类 //步骤二:再定义的类中重写run()方法 //步骤三:创建定义类对象 //步骤四:启动线程 class M…

轮廓测长|轮廓仪高精度测量粗糙度

现代工业制造领域中&#xff0c;为什么测量粗糙度至关重要&#xff1f;在现代工业制造领域中&#xff0c;测量粗糙度是一项非常重要的工作。因为粗糙度能够影响到工件的功能性能和外观质量。当我们制造机器零件或工具时&#xff0c;需要确保表面的光洁度能够满足设计要求。过高…

iOS 自动打包 命令之如何获取PROVISIONING_PROFILE

获取描述文件UUID&#xff1a; 首先在Finder中前往/Users/用户名/Library/MobileDevice/Provisioning Profiles路径 然后在Provisioning Profiles文件夹中选择打包需要的描述文件&#xff0c;一般描述文件的文件名都是由UUID组成的&#xff0c;如果文件名不是由UUID组成的&…

STM32入门教程-2023版【5-1】EXTI外部中断

关注 点赞 不错过精彩内容 大家好&#xff0c;我是硬核王同学&#xff0c;最近在做免费的嵌入式知识分享&#xff0c;帮助对嵌入式感兴趣的同学学习嵌入式、做项目、找工作! 本节主要讲的是&#xff0c;中断系统和外部中断&#xff0c;依靠外部中断来学习中断系统 中断系统&…

idea docker 内网应用实践

文章目录 前言一、服务器端1.1 离线安装docker1.2 开启docker远程访问1.3 制作对应jdk镜像1.3.1 下载jdk171.3.2 Dockerfile 制作jdk17镜像1.3.3 镜像导出1.3.4 服务器引入镜像 二、Idea 配置2.1 Dockerfile2.2 pom 引入docker插件2.3 idea docker插件配置2.4 打包镜像上传2.5 …

基于logisim的MIPS单周期硬布线24条指令CPU

目录 1 前言 2 思路分享 2.1 下载资源 2.2 观看视频 2.3 连接相关电路 2.4 撰写报告 2.5 答辩准备 3 云盘链接 1 前言 该项目为本人大三上学期的实训成果&#xff0c;在实训过程中&#xff0c;纯靠自己摸索&#xff0c;因此迷茫了一段时间。为了不让后来的同学重蹈覆辙…

快速入门JavaScript基础

JavaScript认知 序言 JavaScript发展历史(JS) 简称JS 1.Netscape(网景) 公司的这种浏览器脚本语言&#xff0c;最初名字叫做 Mocha2.1995年9月改为 LiveScript3.1995年12月&#xff0c;Netscape 公司与 Sun 公司&#xff08;Java 语言的发明者和所有者&#xff09;达成协议&am…

Layui + Echarts 5.0

Layui 怎么整合最新版本的 Echarts 5.0&#xff0c;Echarts 4 升级到 5后&#xff0c;有了很大改变&#xff0c;新的配置项4是无法兼容的&#xff0c;所以想要使用新的功能&#xff0c;都需要升级&#xff01; 新建一个echarts.js文件 layui.define(function (exports) {// 这…

C#,广义斐波那契数(Generalised Fibonacci Numbers)的算法

广义斐波那契序列(generalized Fibonacci sequence)是斐波那契数的推广。由递推关系F₁F₂…Fm-10&#xff0c;Fₘ1&#xff0c;FmnFₙFn1…Fnm1&#xff0c;n≥1所产生的序列&#xff0c;称为m级广义斐波那契序列。 计算结果&#xff1a; 源代码&#xff1a; 1 文本格式 …

AF647 二苯并环辛炔,AF647-DBCO,一种明亮且可感光的远红色染料

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;AF647 二苯并环辛炔&#xff0c;AF647 DBCO&#xff0c;Alexa Fluor 647 DBCO&#xff0c;AF647-二苯并环辛炔&#xff0c;AF647-DBCO 一、基本信息 产品简介&#xff1a;Alexa Fluor 647是一种独特的远红色染料&am…

第14章 File类与IO流

第14章 File类与IO流 主要内容 File类字节流字符流文件流缓冲流转换流对象流打印流Scanner、System与IO流 教学目标 使用File类对象表示文件或目录 能够辨别相对路径和绝对路径 能够说出IO流的分类和功能 能够使用FileOutputStream写出数据到文件 能够使用FileInputStream读…

uniapp-v3组合式语法-data实现

在vue选项式风格中,data数据是放置在export default 导出的对象中的一个data方法里面返回 export default {// data() 返回的属性将会成为响应式的状态// 并且暴露在 this 上data() {return {count: 0}},}但这样写,很明显一点都不优雅,代码缩进太多了,还没开始写呢,就已经嵌套…

蓝牙简学(四)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 HID协议一、摘要二、设备实现1. 大概要写的数据解释2. 代码例子3. 代码例子&#xff08;c&#xff09; HID协议 一、摘要 HID&#xff08;Human Interface Device…

Qt SQLite3数据库加密 QtCipherSqlitePlugin

在客户端软件开发过程中&#xff0c;基本都会涉及到数据库的开发。QT支持的数据库也有好几种&#xff08;QSQLITE, QODBC, QODBC3, QPSQL, QPSQL7&#xff09;&#xff0c;SQLite就是其中之一&#xff0c;但这个 SQLite 是官方提供的开源版本&#xff0c;没有加密功能的。如果对…

【leetcode】01背包总结

01 背包 关键点 容器容量固定每件物品只有两种状态&#xff1a;不选、选 1 件求最大价值 代码 int N, W; // N件物品&#xff0c;容量为W int w[N], v[N]; // w为大小&#xff0c;v为容量/* 数组定义 */ int[][] dp new int[N][W 1]; // 注意是W 1, 因为重量会取到W dp[…

win11玩游戏找不到d3dx9如何解决?5种方法轻松搞定dll问题

在Windows 11操作系统环境下&#xff0c;如果用户在运行游戏时发现系统无法找到d3dx9组件&#xff0c;这一问题可能会引发一系列不良影响和限制&#xff0c;严重影响到用户的正常游戏体验。首先&#xff0c;由于d3dx9是DirectX 9.0c的一部分&#xff0c;负责处理图形渲染等关键…

Nodejs前端学习Day5

苦其心志&#xff0c;劳其筋骨 文章目录 前言一、处理路径问题二、path路径模块总结 前言 继续fs 一、处理路径问题 在使用fs模块操作文件时&#xff0c;如果提供的操作路径是以./或…/开头的相对路径时&#xff0c;很容易出现路径动态拼接错误的问题 原因&#xff1a;代码在…

选择合适的CRM管理系统,需要满足以下条件

随着数据时代的发展和企业业务的不断扩大&#xff0c;数据的比例开始增加&#xff0c;传统的数据计算方法不再适合现代企业。客户管理已成为企业最重要的组成部分之一&#xff0c;越来越多的企业开始关注客户管理。在crm管理系统上&#xff0c;企业希望通过crm管理系统&#xf…