图片碎片化渲染特效实现

写在开头

最近,小编从玩了两年多的游戏中退游了😔,本来以为会一直就这么玩下去,和队友们相处很融洽,收获了很多开心快乐的时光😭。可惜,游戏的一波更新......准备要开始收割韭菜了,只能无奈选择弃坑了。

85af570680543599ddcbb0094dabbfb8.jpeg

小编属于贫民玩家,靠着硬肝与白嫖也将游戏号整得还不错,这两天把号给卖了💰。玩了两年多,竟然还能赚一点小钱,很开心😛。只是...多少有点舍不得的一起组队的队友们,唉。😔

记录一下,希望未来还有重逢一日吧,也希望各位一切安好!😆

好,回到正题,本文将分享一个图片碎片化展示的效果,具体效果如下,请诸君按需食用。

37c708a8ec25104cf01191e4e59a0e69.gif

原理

这种特效早在几年前就已经出现,属于老演员了😪,它最早是经常在轮播图(banner)上应用的,那会追求各种花里胡哨的特效,而现在感觉有点返璞归真了,简洁实用就行。

今天咱们来看看它的具体实现原理是如何的,且看图:

f78822e87dc20fa629787aa4d47f2c0f.jpeg

一图胜千言,不知道聪明的你是否看明白了?😉

大概原理是:通过容器/图片大小生成一定数量的小块,然后每个小块背景也使用相同图片,再使用 background-sizebackground-position 属性调整背景图片的大小与位置,使小块又合成一整张大图片,这操作和使用"精灵图"的操作是一样的,最后,我们再给每个小块增加动画效果,就大功告成。

简单朴实😁,你可以根据这个原理自个尝试一下,应该能整出来吧。👻

具体实现

布局与样式:

<!DOCTYPE html>
<html>
<head>
  <style>
    body{
      width: 100%;
      height: 100vh;
      padding: 0;
      margin: 0;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .box {
      width: var(--width);
      height: var(--height);
      display: flex;
      /* 小块自动换行排列 */
      flex-wrap: wrap;
      justify-content: center;
    }
    .small-box {
      background-image: url('https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99b070fcb1de471d9af4f4d5d3f71909~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1120&h=1680&s=2088096&e=png&b=d098d0');
      box-sizing: border-box;
      background-repeat: no-repeat;
    }
  </style>
</head>
<body>
  <div id="box" class="box"></div>
</body>
</html>

生成无数小块填充:

<script>
document.addEventListener('DOMContentLoaded', () => {
  const box = document.getElementById('box');
  const { width, height } = box.getBoundingClientRect();
  // 定义多少个小块,由多少行和列决定
  const row = 14;
  const col = 10;
  // 计算小块的宽高
  const smallBoxWidth = width / col;
  const smallBoxHeight = height / row;
  /** @name 创建小块 **/  
  function createSmallBox() {
    for (let i = 0; i < row; i++) {
      for (let j = 0; j < col; j++) {
        const smallBox = document.createElement('div');
        smallBox.classList.add('small-box');
        smallBox.style.width = smallBoxWidth + 'px';
        smallBox.style.height = smallBoxHeight + 'px';
        smallBox.style.border = '1px solid red';
        // 插入小块
        box.appendChild(smallBox);
      }
    }
  }
  createSmallBox();
});
</script>

上面,生成多少个小块是由人为规定行(row)与列(col)来决定。可能有的场景想用小块固定的宽高来决定个数,这也是可以的,只是需要注意处理一下"边界"的情况。😶

9535acedeeab308b7391eb6aee982ae7.jpeg

调整小块背景图片的大小与位置:

<script>
document.addEventListener('DOMContentLoaded', () => {
  // ...
  function createSmallBox() {
    for (let i = 0; i < row; i++) {
      for (let j = 0; j < col; j++) {
        // ...
        smallBox.style.border = '1px solid red';
        
        // 设置背景偏移量,让小块的背景显示对应图片的位置,和以前那种精灵图一样
        const offsetX = j * smallBoxWidth * -1;
        const offsetY = i * smallBoxHeight * -1;
        smallBox.style.backgroundPosition = `${offsetX}px ${offsetY}px`;
        smallBox.style.backgroundSize = `${width}px ${height}px`;
        
        box.appendChild(smallBox);
      }
    }
  }
  createSmallBox();
});
</script>

女神拼接成功,到这里就已经完成一大步了,是不是没什么难度!😋

e1bd77feb75445df3642f8c09253a5bc.jpeg

小块样式整好后,接下来,我们需要来给小块增加动画,让它们动起来,并且是有规律的动起来。

先来整个简单的透明度动画,且看:

<!DOCTYPE html>
<html>
<head>
  <style>
    /* ... */
    .small-box {
      /* ... */
      opacity: 0;
      animation: smallBoxAnimate 2000ms linear forwards;
    }
    @keyframes smallBoxAnimate {
      0% {
        opacity: 0;
      }
      40% {
        opacity: 0;
      }
      70% {
        opacity: 1;
      }
      100% {
        opacity: 1;
      }
    }
  </style>
</head>
<body>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      // ...
      function createSmallBox() {
        for (let i = 0; i < row; i++) {
          for (let j = 0; j < col; j++) {
            // ...
            // smallBox.style.border = '1px solid red';
            
            // 给每个小块增加不同的延时,让动画不同时间执行
            const delay = i * 100; // 延迟时间为毫秒(ms),注意不要太小了
            smallBox.style.animationDelay = `${delay}ms`;

            box.appendChild(smallBox);
          }
        }
      }
      createSmallBox();
    });
  </script>
</body>
</html>

嘿嘿😃,稍微有点意思了吧?


f6ff648a91966781f7de3fadb5ec2a17.gif5e8efd76078fc44a77316df8ad6ddeb4.jpeg

Em...等等,你发现没有?怎么有一些小白条?这可不是小编添加的,小块的边框(border)已经是注释了的。😓

一开始小编以为是常见的"图片底部白边"问题,直接设置一下 display: block 或者 vertical-align : middle 就能解决,结果还不是,折腾了很久都没有搞掉这个小白条。😤

最后,竟然通过设置 will-change 属性能解决这个问题❗我所知道的 will-change 应该是应用在性能优化上,解决动画流畅度问题上的,想不到这里竟然也能用。

看来得去深度学习一下💪 will-change 属性的原理过程才行,这里也推荐倔友写得一篇文章:传送门。

解决相邻背景图片白条/白边间隙问题:

<script>
  document.addEventListener('DOMContentLoaded', () => {
    // ...
    function createSmallBox() {
      for (let i = 0; i < row; i++) {
        for (let j = 0; j < col; j++) {
          // ...

          smallBox.style.willChange = 'transform';
          // 在动画执行后,需要重置will-change
          const timer = setTimeout(() => {
            smallBox.style.willChange = 'initial';
            clearTimeout(timer);
          }, 2000);
          
          box.appendChild(smallBox);
        }
      }
    }
    createSmallBox();
  });
</script>

一定要注意 will-change 不可能被滥用,注意重置回来❗

这下女神在动画执行后,也清晰可见了,这是全部小块拼接组成的图片。

752bac319a9b3e11437618cefb425c22.gif

在上述代码中,咱们看到,通过 animation-delay 去延迟动画的执行,就能制造一个从上到下的渐变效果。

那么,咱们再改改延迟时间,如:

// const delay = i * 100; 
// 改成 ⤵
const delay = j * 100;

效果:

3fe79a79f2db794bd4c556ad0521f4a9.gif

这...好像有那么点意思吧。。。

17d9ef0eff883eabb93d3c95d2cd2007.jpeg

但是,这渐变...好像还达不到我们开头 gif 的碎片化效果吧?

那么,碎片化安排上:

.small-box {
  /* ... */
  --rotateX: rotateX(0);
  --rotateY: rotateY(0);
  transform: var(--rotateX) var(--rotateY) scale(0.8);
}
@keyframes smallBoxAnimate {
  0% {
    opacity: 0;
    transform: var(--rotateX) var(--rotateY) scale(0.8);
  }
  40% {
    opacity: 0;
    transform: var(--rotateX) var(--rotateY) scale(0.8);
  }
  70% {
    opacity: 1;
    transform: rotateX(0) rotateY(0) scale(0.8);
  }
  100% {
    opacity: 1;
    transform: rotateX(0) rotateY(0) scale(1);
  }
}

其实就是增加小块的样式动画而已,再加点旋转,再加点缩放,都整上,整上。😆

效果:

d5b7f4cb5cfde76f5d1353933204e4d1.gif

是不是稍微高级一点?有那味了?😁

看到上面旋转所用的"样式变量"没有?

--rotateX: rotateX(0);
--rotateY: rotateY(0);

不可能无缘无故突然使用,必然是有深意啦。😁

现在效果还不够炫,咱们将样式变量利用起来,让"相邻两个小块旋转相反":

<script>
  document.addEventListener('DOMContentLoaded', () => {
    // ...
    function createSmallBox() {
      for (let i = 0; i < row; i++) {
        for (let j = 0; j < col; j++) {
          // ...

          // 相邻两个小块旋转相反
          const contrary = (i + j) % 2 === 0;
          smallBox.style.setProperty('--rotateX', `rotateX(${contrary ? -180 : 0}deg)`);
          smallBox.style.setProperty('--rotateY', `rotateY(${contrary ? 0 : -180}deg)`);
          
          box.appendChild(smallBox);
        }
      }
    }
    createSmallBox();
  });
</script>

效果:

e1bf67a8f0e36b2ef5f1cd248a873a00.gif

这下对味了。😃

总的来说,我们可以通过"延迟"执行动画与改变"旋转"行为,让小块们呈现不同的动画效果,或者你只要有足够多的设想,你可以给小块添加不同的动画效果,相信也能制造出不错的整体效果。

更多效果

下面列举一些通过"延迟"执行动画产生的效果,可以瞧瞧哈。

随机:

const getRandom = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
const delay = getRandom(0, col + row) * 100;

ee6ec39cb1fd0e21ba2da7739ab4bba9.gif

从左上角到右下角:

const delay = (i + j) * 100;

8f9363936a39a13ccb35d4674cc22e77.gif

其他的从"右上角到左下角"或者"左下角到右上角"等等的,只要反向调整一下变量就行了,就靠你自己悟啦,Come On!👻

从中心向四周扩散:

const delay = ((Math.abs(col / 2 - j) + Math.abs(row / 2 - i))) * 100;

fe9400887879a63331e1619f6ddc58e5.gif

从四周向中心聚齐:

const delay = (col / 2 - Math.abs(col / 2 - j) + (col / 2 - Math.abs(row / 2 - i))) * 100;

05e3df387c40c5df3fe2c37256e9d7c8.gif

那么,到这里就差不多了❗

但还有最后一个问题,那就是图片的大量使用与加载时长的情况可能会导致效果展示不佳,这里你最好进行一些防范措施,如:

  • 图片链接设置缓存,让浏览器缓存到内存或硬盘中。

  • 通过 JS 手动将图片缓存到内存,主要就是创建 Image 对象。

  • 将图片转成 base64 使用。

  • 直接将图片放到代码本地使用。

  • ...

以上等等吧,反正最好就是要等图片完整加载后再进行效果展示。

至此,本篇文章就写完啦,撒花撒花。

原文链接:https://juejin.cn/post/7379856289487831074

作者:橙某人

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值