JS实现图片飞入特效

JS实现图片飞入特效

技术杂谈小彩虹2021-08-23 12:51:07150A+A-

前言

前段时间在折腾自己的服务器的时候,无意中翻到了自己以前开始学习JS的时候写过的小案例,不知不觉2020年只剩下不到7天了,回想起当时自学前端的日子真是感受颇多;其中我找到一个用JS实现图片飞入的小案例,觉得挺有意思的,今天拿出来跟大家分享下。

效果图

没错,文章开头的图片就是我们本次的素材了,下面是最终的效果图:

原理分析

从效果图中可以看出来,最外层是放了一个大盒子,最终的大图会铺满这个大盒子,而整个一张大图是被分割成了许多的小图,将它们打乱顺序,然后在一个一个的推入到大盒子中。
这里其实是用了100个"小盒子",每个小盒子中装了大图的一部分,通过给它们加上背景图片然后设置对应的定位值,最后把这100个小盒子打乱顺序挨个放到大盒子里就能实现上述的效果了。

  • 如何创建"小盒子"

    我们第一步要做的就是先创建出这些"小盒子",在这里我们可以把整张图片按照10x10来分割,总共100个小盒子,这里我们可以用数组实现:

//快速生成一个length为100的数组
const list = [...new Array(100).keys()];
  • 如何打乱顺序

    上面list的值是从0~99按顺序来的,现在我们需要将这个数组打乱顺序;数组排序毋庸置疑可以用数组的sort方法,sort方法接收一个回调函数作为参数,回调函数的返回值如果小于0就会对数组升序排序,大于0就会进行降序排序;然后我们这里还要的一个随机的效果,Math.random()刚好能返回一个0到1之间的随机数,再接合上数组的sort方法,用Math.random()减去0.5就能实现对数组的值随机排序了,当然这个随机完全意义上来说是不准确,也可以说是不公平的,因为Math.random()永远也取不到1,不过在这里相对来说已经很随机了。

    //打乱数组的顺序
    list.sort((a, b) => Math.random() - 0.5);
    
  • 如何计算每个小盒子的lefttop

    我们先画个草图,如下图所示(偷个懒没写的部分大家自行脑补哈),100个小盒子的最终排列效果就像这样,可以明显的看出每个小盒子的定位值是不一样的,但是貌似它们又遵循着某种规律,我们怎么获取到每一个小盒子的left和top值呢?

    由于我们的素材图片是600px x 400px的,我们把它分割成10x10等份,通过计算可以得出,每一个小盒子的width=60pxheight=40px。所以从图中可以看出:
    第一个的left值=0
    第二个的left值=60
    第三个的left值=120
    第四个的left值=180
    ...以此类推

    而刚好我们这100个盒子是放在数组中,数组的下标从0开始,第一个盒子的索引为0,第二个盒子的索引为1,第三个盒子的索引为2,第四个盒子的索引为3,我们在x轴方向一排只放10个盒子,所以我们得出规律:第几个小盒子的left值=(它的索引 % 10 )x60,也就是用每一个盒子的索引模上10来取余,再乘以一等份的width就能得到它的left值。
    我们来验证一下:
    第一个的left值 = (0 % 10) * 60 = 0
    第二个的left值 = (1 % 10) * 60 = 60
    第三个的left值 = (2 % 10) * 60 = 120
    第四个的left值 = (3 % 10) * 60 = 180

    没毛病,跟我们上面完全对的上,就是这么nice,每个盒子的left值已经得到了,现在我们获取top值,既然每个小盒子的left值是由规律的,那么top值是不是也有规律呢?答案是肯定的,我们来找一下。
    通过上面的网格图可以得到:
    第1个的top值=0
    第11个的top值=40
    第21个的top值=80
    第31个的top值=120
    第41个的top值=160
    ...

    不难发现,每一个小盒子的top值=Math.floor(它的索引 / 10) x 40,这里我们只需要得到每个小盒子的十位部分,所以不需要小数。接着我们再来验证一下:
    第1个的top值 = Math.floor(0 / 10) * 40 = 0
    第11个的top值 = Math.floor(10 / 10) * 40 = 40
    第21个的top值 = Math.floor(20 / 10) * 40 = 80
    第31个的top值 = Math.floor(30 / 10) * 40 = 120
    第41个的top值 = Math.floor(40 / 10) * 40 = 160

    同样没毛病,也完全对的上,一样的nice,到这里我们就已经得到了每一个小盒子的left和top值。

  • 如何计算每个小盒子背景图片的left和top值

    现在我们还需要给每一个小盒子加上露娜的背景图片并设置好对应的background-position,经过上面的分析到这里不难看出,每个小盒子的background-position跟它们的left值top值是相反的,下面我们还需要准备一点东西。

  • 如何实现飞入特效

    这里我们需要引用一个JS动画库,叫做anime.js,大家可以去github搜一下就能找到了;点击这里可以查看它的文档,了解相关用法。我们这里就是传入了一些配置,将我们上面计算到的定位值传入进去,最终来实现飞入的动画效果,下面就可以开始撸代码了。

代码实现

我们先创建最外层的容器(class为wrapper的div)并初始化样式,并且提前给里面的每个小盒子(class为box的div)设置好相应的样式

  • html部分
<div class="wrapper"></div>
  • css部分
.wrapper{
  position: relative;
  width: 600px;
  height: 400px;
  border: 1px solid #ccc;
  margin: 50px auto;
}
.wrapper .box{
  position: absolute;
  width: 60px;
  height: 40px;
}
  • js部分

我们先获取最外层容器然后生成一个值为0-99的数组并打乱顺序,同时要知道我们每一张小盒子飞入容器中都是一次函数执行,所以我们需要一个索引值来记录当前飞入的是第几个小盒子

//获取容器盒子
const wrapper = document.querySelector('.wrapper');
//快速生成一个值为0-99、length为100的数组
const list = [...new Array(100).keys()];
//打乱数组的顺序
list.sort((a, b) => Math.random() - 0.5);
//初始化索引值
let index = 0;

然后我们需要定义一个函数,每一次执行这个函数就往容器中飞入一个小盒子,我们的index就+1,当index到达99的时候,说明就是最后一个小盒子,本次函数执行结束后下一次就不需要执行了,所以我们每一次执行函数我们先进行判断,如果index === 100,那么我们就终止函数执行

然后我们挨个从数组中取出一个小盒子,结合上面的分析,获取到它的left值和top值,然后我们创建一个div给它设置好相应的样式然后append到容器中就好了,最后再由anime来生成飞入的动画,来看看具体代码:

function fly() {
  //index=100时直接return
  if (index === 100) return;
  //取出当前小盒子的位置,取完后index需要自增1
  let currentVal = list[index++];
  //获取当前盒子的left和top值
  const left = currentVal % 10 * 60;
  const top = Math.floor(currentVal / 10) * 40

  //生成一个盒子
  const box = document.createElement('div');
  //加上我们前面定义的类名
  box.classList.add('box');
  //设置背景图片的定位值和其他属性,这里./timg.jpg就是我们的素材图片
  box.style.cssText = `background:url('./timg.jpg') ${-left}px ${-top}px no-repeat; background-size: 600px 400px;`;
  //插入到容器中
  wrapper.appendChild(box);
  //生成飞入动画,需要注意在此之前我们需要先引入anime.js
  let animation = anime({
    targets: box, //目标元素,这里指当前飞入的小盒子
    left: left + 'px', //每一个小盒子的left值
    top: top + 'px', //每一个小盒子的top值
    duration: 80, //每一个小盒子飞入的执行时间,单位是毫秒
    easing: 'linear', //这里可以设置很多不一样的参数来控制动画如何执行
    //执行完当前动画后的延迟回调,因为我们需要执行100次,所以进行递归调用
    complete: function () {
      fly();
    }
  })
}

最后我们直接在外层调用fly()就可以了,到这里代码实现差不多就结束了,打开浏览器可以看到跟效果图一样的飞入动画了,其实这个案例分析明白想透彻了还是比较简单的,可能我上面有些地方描述的不是特别好,还望大家多多指教,喜欢的朋友可以点个赞支持一下哈,感谢各位大佬。

  • 下面附上完整代码,需要的小伙伴可以参考下:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片飞入</title>
  <style>
    .wrapper {
      position: relative;
      width: 600px;
      height: 400px;
      border: 1px solid #ccc;
      margin: 50px auto;
    }

    .wrapper .box {
      position: absolute;
      width: 60px;
      height: 40px;
    }
  </style>
</head>
<script src="./anime.js"></script>

<body>
  <div class="wrapper"></div>
  <script>
    //获取容器盒子
    const wrapper = document.querySelector('.wrapper');
    //快速生成一个length为100的数组
    const list = [...new Array(100).keys()];
    //打乱数组的顺序
    list.sort((a, b) => Math.random() - 0.5);
    //初始化索引值
    let index = 0;

    function fly() {
      //index=100时直接return
      if (index === 100) return;
      //取出当前小盒子的位置,取完后index需要自增1
      let currentVal = list[index++];
      //获取当前盒子的left和top值
      const left = currentVal % 10 * 60;
      const top = Math.floor(currentVal / 10) * 40

      //生成一个盒子
      const box = document.createElement('div');
      //加上我们前面定义的类名
      box.classList.add('box');
      //设置背景图片的定位值和其他属性,这里./timg.jpg就是我们的素材图片
      box.style.cssText = `background:url('./timg.jpg') ${-left}px ${-top}px no-repeat;
                       background-size: 600px 400px;`;
      //插入到容器中
      wrapper.appendChild(box);
      //生成飞入动画,需要注意在此之前我们需要先引入anime.js
      let animation = anime({
        targets: box, //目标元素,这里指当前飞入的小盒子
        left: left + 'px', //每一个小盒子的left值
        top: top + 'px', //每一个小盒子的top值
        duration: 80, //每一个小盒子飞入的执行时间,单位是毫秒
        easing: 'linear', //这里可以设置很多不一样的参数来控制动画如何执行
        //执行完当前动画后的延迟回调,因为我们需要执行100次,所以进行递归调用
        complete: function () {
          fly();
        }
      })
    }
    
    //开始执行
    fly();
  </script>
</body>

</html>

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们