如何制作一个动画,从四面八方聚集在一起的相同尺寸的碎片构建最终图像?

How to make an animation that constructs a final image from pieces of equal dimensions that come together from all sides?

动画类似于 PowerPoint 中的幻灯片切换。我会使用 skewtranslate(从页面边缘的随机位置)转换来完成。我开始使用 CSS sprites 和 CSS 网格。我遇到的第一个问题是网格之间的间距,Chrome DevTools 中没有解释间距。我可以使用 anime.js 或其他库。

下面的问题是碎片之间的间距,它们没有显示所需的图像的完整部分。原图为here.

/* The photo resolution: 2400 x 1600 */
.slideshow {
    display: grid;
    grid-template-rows: auto auto auto;
    grid-template-columns: auto auto auto;
}

.tile {
    background-image: url(https://unsplash.com/photos/hzgs56Ze49s/download?force=true&w=2400);
    /* width: 800px;
    height: 533px; */
    width: calc(800px / 2);
    height: calc(533px / 2);
    transform: scale(0.5, 0.5);
}
 
.piece1 {
    background-position: 0 0;
    grid-row: 1/1;
    grid-column: 1/1;
}
.piece2 {
    background-position: -800px 0;
    grid-row: 1/1;
    grid-column: 2/2;
}
.piece3 {
    background-position: -1600px 0;
    grid-row: 1/1;
    grid-column: 3/3;
}

.piece4 {
    background-position: 0 -533px;
    grid-row: 2/2;
    grid-column: 1/1;
}
.piece5 {
    background-position: -800px -533px;
    grid-row: 2/2;
    grid-column: 2/2;
}
.piece6 {
    background-position: -1600px -533px;
    grid-row: 2/2;
    grid-column: 3/3;
}

.piece7 {
    background-position: 0 -1066px;
    grid-row: 3/3;
    grid-column: 1/1;
}
.piece8 {
    background-position: -800px -1066px;
    grid-row: 3/3;
    grid-column: 2/2;
}
.piece9 {
    background-position: -1600px -1066px;
    grid-row: 3/3;
    grid-column: 3/3;
}
<div class="slideshow">
  <div class="tile piece1"></div>
  <div class="tile piece2"></div>
  <div class="tile piece3"></div>

  <div class="tile piece4"></div>
  <div class="tile piece5"></div>
  <div class="tile piece6"></div>

  <div class="tile piece7"></div>
  <div class="tile piece8"></div>
  <div class="tile piece9"></div>
</div>

我还不确定我开始走的路是否行得通。

谢谢。

一种方法,只需进行一些更改:

1) background-position, 应为"left top", "top", "right top", "left", "center", "right", "bottom left"、"bottom center" 和 "bottom right" 将图像切成 9 块。
https://www.w3schools.com/cssref/pr_background-position.asp

2) 由于原图尺寸为2400 x 1600,将"background-size"设置小一点,然后根据较小的尺寸用calc计算,比如:

width: calc(600px / 3);
height: calc(600px / 3);

3) 将图块位置设置为绝对位置。

4) 使用顶部和左侧指定每个图块的绝对位置。

5) 为每个图块指定唯一的 ID。

6) 使用javascript 的setInterval 和if else 语句来改变元素的绝对位置,使用每个元素的id 来决定它们的top 和left 属性应该如何超时改变。

7) 在示例中,我使用按钮 "Click Me" 来执行脚本,但您可以使用 onload 或任何您想调用 moveElements() 函数来执行此操作。

<style>
    /* The photo resolution: 2400 x 1600 */
.slideshow {
    display: grid;
    grid-template-rows: auto auto auto;
    grid-template-columns: auto auto auto;
}

.tile {
    background-image: url(https://unsplash.com/photos/hzgs56Ze49s/download?force=true&w=2400);
    background-size: 600px 600px;
    /* width: 800px;
    height: 533px; */
    width: calc(600px / 3);
    height: calc(600px / 3);
    transform: scale(0.5, 0.5);
    position:absolute;
}

.piece1 {
    background-position: left top;
    grid-row: 1/1;
    grid-column: 1/1;
    top:0;
    left:0;
}
.piece2 {
    background-position: top;
    grid-row: 1/1;
    grid-column: 2/2;
    top:0;
    left:250;
}
.piece3 {
    background-position: right top;
    grid-row: 1/1;
    grid-column: 3/3;
    top:0;
    left:500;
}

.piece4 {
    background-position: left;
    grid-row: 2/2;
    grid-column: 1/1;
    top:250;
    left:0;
}
.piece5 {
    background-position: center;
    grid-row: 2/2;
    grid-column: 2/2;
    top:250;
    left: 250;
}
.piece6 {
    background-position: right;
    grid-row: 2/2;
    grid-column: 3/3;
    top:250;
    left:500;
}

.piece7 {
    background-position: bottom left;
    grid-row: 3/3;
    grid-column: 1/1;
    top:500;
    left:0;
}
.piece8 {
    background-position: bottom center;
    grid-row: 3/3;
    grid-column: 2/2;
    top:500;
    left:250;
}
.piece9 {
    background-position: bottom right;
    grid-row: 3/3;
    grid-column: 3/3;
    top:500;
    left:500;
}
</style>
<p><button onclick="moveElements()">Click Me</button></p> 
<div class="slideshow">
  <div id="topleft" class="tile piece1"></div>
  <div id="top" class="tile piece2"></div>
  <div id="topright" class="tile piece3"></div>

  <div id="left" class="tile piece4"></div>
  <div id="center" class="tile piece5"></div>
  <div id="right" class="tile piece6"></div>

  <div id="bottomleft" class="tile piece7"></div>
  <div id="bottomcenter" class="tile piece8"></div>
  <div id="bottomright" class="tile piece9"></div>
</div>


<script>
function moveElements() {
  var elem1 = document.getElementById("topleft");   
  var pos1 = 0;
  var id1 = setInterval(frame1, 5);
  function frame1() {
    if (pos1 == 151) {
      clearInterval(id1);
    } else {
      pos1++; 
      elem1.style.top = pos1 + "px"; 
      elem1.style.left = pos1 + "px"; 
    }
  }

  var elem2 = document.getElementById("top");   
  var pos2 = 0;
  var id2 = setInterval(frame2, 5);
  function frame2() {
    if (pos2 == 151) {
      clearInterval(id2);
    } else {
      pos2++; 
      elem2.style.top = pos2 + "px"; 

    }
  }

  var elem3 = document.getElementById("topright");   
  var pos3t = 0;
  var pos3l = 500;
  var id3 = setInterval(frame3, 5);
  function frame3() {
    if (pos3t == 151) {
      clearInterval(id3);
    } else {
      pos3t++; 
      pos3l--;
      elem3.style.top = pos3t + "px"; 
      elem3.style.left = pos3l + "px"; 
    }
  }

  var elem4 = document.getElementById("left");   
  var pos4 = 0;
  var id4 = setInterval(frame4, 5);
  function frame4() {
    if (pos4 == 151) {
      clearInterval(id4);
    } else {
      pos4++; 
      elem4.style.left = pos4 + "px"; 
    }
  }

  // element5, the center tile stays still

  var elem6 = document.getElementById("right");   
  var pos6 = 500;
  var id6 = setInterval(frame6, 5);
  function frame6() {
    if (pos6 == 349) {
      clearInterval(id6);
    } else {
      pos6--; 
      elem6.style.left = pos6 + "px"; 
    }
  }

  var elem7 = document.getElementById("bottomleft");   
  var pos7t = 500;
  var pos7l = 0;
  var id7 = setInterval(frame7, 5);
  function frame7() {
    if (pos7t == 349) {
      clearInterval(id7);
    } else {
      pos7t--;
      pos7l++; 
      elem7.style.top = pos7t + "px"; 
      elem7.style.left = pos7l + "px"; 
    }
  }

  var elem8 = document.getElementById("bottomcenter");   
  var pos8 = 500;
  var id8 = setInterval(frame8, 5);
  function frame8() {
    if (pos8 == 349) {
      clearInterval(id8);
    } else {
      pos8--; 
      elem8.style.top = pos8 + "px"; 
    }
  }

  var elem9 = document.getElementById("bottomright");   
  var pos9 = 500;
  var id9 = setInterval(frame9, 5);
  function frame9() {
    if (pos9 == 349) {
      clearInterval(id9);
    } else {
      pos9--; 
      elem9.style.top = pos9 + "px"; 
      elem9.style.left = pos9 + "px"; 
    }
  }
}
</script>

我会考虑使用 position:absolute 将所有图像层放在彼此之上,我将依靠 mask(或剪辑路径)仅显示其中的一部分。然后你就可以轻松应用翻译了。

悬停查看效果:

.slideshow {
  width: 400px;
  height: 261px;
  margin:100px auto;
  position:relative;
  background-image: url(https://unsplash.com/photos/hzgs56Ze49s/download?force=true&w=2400);
  background-size: 0 0;
}

.tile {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: inherit;
  background-size: cover;
  -webkit-mask:linear-gradient(#fff,#fff) no-repeat;
  -webkit-mask-size:33.4% 33.4%;
  -webkit-mask-position:var(--p);
          mask:linear-gradient(#fff,#fff) no-repeat;
          mask-size:33.4% 33.4%;
          mask-position:var(--p);
   transition:0.5s;
}

.piece1 {--p:top    left;  --t:-10%, -10%;}
.piece2 {--p:center left;  --t:-10%,  0%;}
.piece3 {--p:bottom left;  --t:-10%,  10%;}

.piece4 {--p:top    center;--t: 0%,  -10%;}
.piece5 {--p:center center;--t: 0%,   0%;}
.piece6 {--p:bottom center;--t: 0%,   10%;}

.piece7 {--p:top    right; --t: 10%, -10%;}
.piece8 {--p:center right; --t: 10%,  0%;}
.piece9 {--p:bottom right; --t: 10%,  10%;}

.slideshow:hover .tile{
   transform:translate(var(--t));
}

body {
 background:#f2f2f2;
}
<div class="slideshow">
  <div class="tile piece1"></div>
  <div class="tile piece2"></div>
  <div class="tile piece3"></div>

  <div class="tile piece4"></div>
  <div class="tile piece5"></div>
  <div class="tile piece6"></div>

  <div class="tile piece7"></div>
  <div class="tile piece8"></div>
  <div class="tile piece9"></div>
</div>

您可以使用不需要大量代码的规模效应进行优化,并且可以将 mask-positiontransform-origin

重用

.slideshow {
  width: 400px;
  height: 261px;
  margin:50px auto;
  position:relative;
  background-image: url(https://unsplash.com/photos/hzgs56Ze49s/download?force=true&w=2400);
  background-size: 0 0;
  transition:0.5s;
}

.tile {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: inherit;
  background-size: cover;
  -webkit-mask:linear-gradient(#fff,#fff) no-repeat;
  -webkit-mask-size:33.4% 33.4%;
  -webkit-mask-position:var(--p);
          mask:linear-gradient(#fff,#fff) no-repeat;
          mask-size:33.4% 33.4%;
          mask-position:var(--p);
   transition:inherit;
   transform-origin:var(--p);
}

.piece1 {--p:top    left;}
.piece2 {--p:center left;}
.piece3 {--p:bottom left;}

.piece4 {--p:top    center;}
.piece5 {--p:center center;}
.piece6 {--p:bottom center;}

.piece7 {--p:top    right;}
.piece8 {--p:center right;}
.piece9 {--p:bottom right;}

.slideshow:hover .tile{
   transform:scale(0.8);
}

.slideshow:hover {
   transform:scale(1.25);
}

body {
 background:#f2f2f2;
}
<div class="slideshow">
  <div class="tile piece1"></div>
  <div class="tile piece2"></div>
  <div class="tile piece3"></div>

  <div class="tile piece4"></div>
  <div class="tile piece5"></div>
  <div class="tile piece6"></div>

  <div class="tile piece7"></div>
  <div class="tile piece8"></div>
  <div class="tile piece9"></div>
</div>


您可以轻松缩放到 4x4 或任何 NxN 网格:

.slideshow {
  --n:4;
  
  width: 400px;
  height: 261px;
  margin:100px auto;
  position:relative;
  background-image: url(https://unsplash.com/photos/hzgs56Ze49s/download?force=true&w=2400);
  background-size: 0 0;
   transition:0.5s;
}

.tile {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: inherit;
  background-size: cover;
  -webkit-mask:linear-gradient(#fff,#fff) no-repeat;
  -webkit-mask-size:calc(100%/var(--n) + 1px) calc(100%/var(--n) + 1px);
  -webkit-mask-position:var(--p);
          mask:linear-gradient(#fff,#fff) no-repeat;
          mask-size:calc(100%/var(--n) + 1px) calc(100%/var(--n) + 1px);
          mask-position:var(--p);
   transition:inherit;
   transform-origin:var(--p);
}

.tile:nth-child(1)  {--p:calc(0*100%/(var(--n) - 1)) calc(0*100%/(var(--n) - 1));}
.tile:nth-child(2)  {--p:calc(1*100%/(var(--n) - 1)) calc(0*100%/(var(--n) - 1));}
.tile:nth-child(3)  {--p:calc(2*100%/(var(--n) - 1)) calc(0*100%/(var(--n) - 1));}
.tile:nth-child(4)  {--p:calc(3*100%/(var(--n) - 1)) calc(0*100%/(var(--n) - 1));}

.tile:nth-child(5)  {--p:calc(0*100%/(var(--n) - 1)) calc(1*100%/(var(--n) - 1));}
.tile:nth-child(6)  {--p:calc(1*100%/(var(--n) - 1)) calc(1*100%/(var(--n) - 1));}
.tile:nth-child(7)  {--p:calc(2*100%/(var(--n) - 1)) calc(1*100%/(var(--n) - 1));}
.tile:nth-child(8)  {--p:calc(3*100%/(var(--n) - 1)) calc(1*100%/(var(--n) - 1));}

.tile:nth-child(9)  {--p:calc(0*100%/(var(--n) - 1)) calc(2*100%/(var(--n) - 1));}
.tile:nth-child(10) {--p:calc(1*100%/(var(--n) - 1)) calc(2*100%/(var(--n) - 1));}
.tile:nth-child(11) {--p:calc(2*100%/(var(--n) - 1)) calc(2*100%/(var(--n) - 1));}
.tile:nth-child(12) {--p:calc(3*100%/(var(--n) - 1)) calc(2*100%/(var(--n) - 1));}

.tile:nth-child(13) {--p:calc(0*100%/(var(--n) - 1)) calc(3*100%/(var(--n) - 1));}
.tile:nth-child(14) {--p:calc(1*100%/(var(--n) - 1)) calc(3*100%/(var(--n) - 1));}
.tile:nth-child(15) {--p:calc(2*100%/(var(--n) - 1)) calc(3*100%/(var(--n) - 1));}
.tile:nth-child(16) {--p:calc(3*100%/(var(--n) - 1)) calc(3*100%/(var(--n) - 1));}


.slideshow:hover > *{
   transform:scale(0.8);
}
.slideshow:hover {
   transform:scale(1.25);
}

body {
 background:#f2f2f2;
}
<div class="slideshow">
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>

  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>

  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
</div>