Search code examples
htmlcssbackground-imagecss-animations

Background position animation is not working smoothly


Hi I have some image as follows:

smily

which has some animation from left to right then bottom then left to right and so on.

Image dimension 600 X 738 is and no and as number of squire is 5x5. I wrote css as follows to implement requirement:

.cute_angry_steam_coming_out_of_face {
  border-radius: 5px;
  display: inline-block;
  line-height: 52px;
  padding: 2px 2px 8px;
  position: relative;
  text-align: center;
  width: 120px;
  height: 148px;
  animation: cute_happy_smiling_face 0 2s;
  animation-iteration-count: infinite;
  background-image: url('https://i.sstatic.net/gQIqY.png');
  background-repeat: no-repeat;
  background-size: 960px 785px;
  image-rendering: -webkit-optimize-contrast;
  border: 1px solid red;
}

@keyframes cute_happy_smiling_face {
  0% {
    background-position: 0px 0px;
  }
  3% {
    background-position: 120px 0px;
  }
  6% {
    background-position: 240px 0px;
  }
  8% {
    background-position: 360px 0px;
  }
  11% {
    background-position: 480px 0px;
  }
  14% {
    background-position: 600px 0px;
  }
  17% {
    background-position: 0px 148px;
  }
  19% {
    background-position: 120px 148px;
  }
  22% {
    background-position: 240px 148px;
  }
  25% {
    background-position: 360px 148px;
  }
  28% {
    background-position: 480px 148px;
  }
  31% {
    background-position: 600px 148px;
  }
  33% {
    background-position: 0px 295px;
  }
  36% {
    background-position: 120px 295px;
  }
  39% {
    background-position: 240px 295px;
  }
  42% {
    background-position: 360px 295px;
  }
  44% {
    background-position: 480px 295px;
  }
  47% {
    background-position: 600px 295px;
  }
  50% {
    background-position: 0px 443px;
  }
  53% {
    background-position: 120px 443px;
  }
  56% {
    background-position: 240px 443px;
  }
  58% {
    background-position: 360px 443px;
  }
  61% {
    background-position: 480px 443px;
  }
  64% {
    background-position: 600px 443px;
  }
  67% {
    background-position: 0px 590px;
  }
  69% {
    background-position: 120px 590px;
  }
  72% {
    background-position: 240px 590px;
  }
  75% {
    background-position: 360px 590px;
  }
  78% {
    background-position: 480px 590px;
  }
  81% {
    background-position: 600px 590px;
  }
  83% {
    background-position: 0px 738px;
  }
  86% {
    background-position: 120px 738px;
  }
  89% {
    background-position: 240px 738px;
  }
  92% {
    background-position: 360px 738px;
  }
  94% {
    background-position: 480px 738px;
  }
  97% {
    background-position: 600px 738px;
  }
}
<div class="cute_angry_steam_coming_out_of_face"></div>

But it is not animating smoothly till end. What I want with every picture it should hold for a second and position should change to next as fast it can with looping infinitely.

At the end animation will look like following

Animation Video


Solution

  • To make it easier you can consider CSS variables and the trick is to use negative values for the position based on the width height.

    You have 5 rows and 5 columns and 22 images so you split the animation into 22 states (100/22 = 4.54). On each state we increment a variable from 0 to 4 on the x-axis and when we reach 4 we reset to 0 and we increment the y-axis.

    You can also easily adjust the dimension by simply changing the CSS variables so you don't need to know the width/height of the image, you only need to know the number of rows and columns

    .cute_angry_steam_coming_out_of_face {
      border-radius: 5px;
      display: inline-block;
      border: 1px solid red;
      --w:120px;
      --h:148px;
      width: var(--w);
      height: var(--h);
      background-image: url('https://i.sstatic.net/gQIqY.png');
      background-size: calc(5*var(--w)) calc(5*var(--h));
      animation: cute_happy_smiling_face 3s infinite;
    }
    
    @keyframes cute_happy_smiling_face {
      0%,4.54% {
        background-position: calc(0*var(--w)) calc(0*var(--h));
      }
      4.55%,9.09% {
        background-position: calc(-1*var(--w)) calc(0*var(--h));
      }
      9.10%,13.63% {
        background-position: calc(-2*var(--w)) calc(0*var(--h));
      }
      13.64%,18.18% {
        background-position: calc(-3*var(--w)) calc(0*var(--h));
      }
      18.19%,22.72% {
        background-position: calc(-4*var(--w)) calc(0*var(--h));
      }
      22.73%,27.27% {
        background-position: calc(0*var(--w)) calc(-1*var(--h));
      }
      27.28%,31.81% {
        background-position: calc(-1*var(--w)) calc(-1*var(--h));
      }
      31.82%,36.36% {
        background-position: calc(-2*var(--w)) calc(-1*var(--h));
      }
      36.37%,40.90% {
        background-position: calc(-3*var(--w)) calc(-1*var(--h));
      }
      40.91%,45.45% {
        background-position: calc(-4*var(--w)) calc(-1*var(--h));
      }
      45.46%,50% {
        background-position: calc(0*var(--w)) calc(-2*var(--h));
      }
      50.01%,54.54% {
        background-position: calc(-1*var(--w)) calc(-2*var(--h));
      }
      54.55%,59.09% {
        background-position: calc(-2*var(--w)) calc(-2*var(--h));
      }
      59.10%,63.63% {
        background-position: calc(-3*var(--w)) calc(-2*var(--h));
      }
      63.64%,68.18% {
        background-position: calc(-4*var(--w)) calc(-2*var(--h));
      }
      68.19%,72.72% {
        background-position: calc(0*var(--w)) calc(-3*var(--h));
      }
      72.73%,77.27% {
        background-position: calc(-1*var(--w)) calc(-3*var(--h));
      }
      77.28%,81.81% {
        background-position: calc(-2*var(--w)) calc(-3*var(--h));
      }
      81.82%,86.36% {
        background-position: calc(-3*var(--w)) calc(-3*var(--h));
      }
      86.37%,90.9% {
        background-position: calc(-4*var(--w)) calc(-3*var(--h));
      }
      90.91%,95.45% {
        background-position: calc(0*var(--w)) calc(-4*var(--h));
      }
      95.46%,100% {
        background-position: calc(-1*var(--w)) calc(-4*var(--h));
      }
    
    }
    <div class="cute_angry_steam_coming_out_of_face"></div>
    
    
    <div class="cute_angry_steam_coming_out_of_face" style="--w:100px;--h:120px"></div>
    
    <div class="cute_angry_steam_coming_out_of_face" style="--w:50px;--h:80px"></div>

    Another intresting idea (which I recommend) with less of code is to animate each position alone and use steps(). This will make the code easier. The only drawback is that you will see the 3 empty slots. This method is perfect if you have exactly 5x5 images (25) and not only 22.

    .cute_angry_steam_coming_out_of_face {
      border-radius: 5px;
      display: inline-block;
      border: 1px solid red;
      --w:120px;
      --h:148px;
      width: var(--w);
      height: var(--h);
      background-image: url('https://i.sstatic.net/gQIqY.png');
      background-size: calc(5*var(--w)) calc(5*var(--h));
      animation-name:     smiling_face-x, smiling_face-y; 
      animation-duration: 0.5s            ,2.5s; /* 2.5 = 5 x 0.5 */
      animation-timing-function:steps(5);
      animation-iteration-count:infinite;
    }
    
    @keyframes smiling_face-x {
      100% {
        background-position-x: calc(-5*var(--w));
      }
    }
    @keyframes smiling_face-y {
      100% {
        background-position-y: calc(-5*var(--h));
      }
    }
    <div class="cute_angry_steam_coming_out_of_face"></div>
    
    
    <div class="cute_angry_steam_coming_out_of_face" style="--w:100px;--h:120px"></div>
    
    <div class="cute_angry_steam_coming_out_of_face" style="--w:50px;--h:80px"></div>

    Here is a more generic code consider NxM image:

    .cute_angry_steam_coming_out_of_face {
      border-radius: 5px;
      display: inline-block;
      border: 1px solid red;
      --w:120px;
      --h:148px;
      --n:3; /* number of rows     */
      --m:5; /* number of columns */
      --d:2s; /*duration*/
      width: var(--w);
      height: var(--h);
      background-image: url('https://i.sstatic.net/M2n58.png');
      background-size: calc(var(--m)*var(--w)) calc(var(--n)*var(--h));
      animation-name:     smiling_face-x, smiling_face-y; 
      animation-duration: calc(var(--d)/var(--n)),var(--d);
      animation-timing-function:steps(var(--m)),steps(var(--n));
      animation-iteration-count:infinite;
    }
    
    @keyframes smiling_face-x {
      100% {
        background-position-x: calc(-1*var(--m)*var(--w));
      }
    }
    @keyframes smiling_face-y {
      100% {
        background-position-y: calc(-1*var(--n)*var(--h));
      }
    }
    <div class="cute_angry_steam_coming_out_of_face"></div>
    
    
    <div class="cute_angry_steam_coming_out_of_face" style="--w:100px;--h:120px"></div>
    
    <div class="cute_angry_steam_coming_out_of_face" style="--w:50px;--h:80px"></div>