3

I have an animation for a "firework" that will explode around a circular image, with 10 evenly spaced rays coming out like spokes in a wheel. Each "spoke" expands to its full length, has the "tail" catch up to the "head", and finally disappears. I have the first one coming out of the bottom:

HTML:

<div id="firework"></div>

CSS:

#firework {
  background-color: red;
  border-radius: 30%;
  height: 0px;
  margin: 0px 0px;
  width: 2px;
  -webkit-animation: firework-0 1s 1;
}


@-webkit-keyframes firework-0 {
  0% {
    height: 0px;
    margin-top: 0px;
  }
  50% {
    height: 64px;
    margin-top: 0px;
  }
  100% {
    height: 0px;
    margin-top: 64px;
  }
}

fiddle: http://jsfiddle.net/xcWge/1407/

Whats the best way to replicate this every 36 degree? I tried creating the one coming out of the top, but I don't think I can continue using this margin attribute for the same head / tail effect. I've read up on placing things around a circle (Position icons into circle) but I need to maintain the same radial animation effect as well, which is stumping me.

Community
  • 1
  • 1
Brandon
  • 1,029
  • 3
  • 11
  • 21

1 Answers1

5

Using CSS:

Doing what you are looking for using a single element with pure CSS is going to be really tough (if not impossible). We can do it by using as many elements as the no. of spokes that are required. Position the spokes absolutely at a certain location and rotate by the required angles. Origin should be fixed at the bottom of the element so that they all converge into one point.

The animation itself can be achieved by using linear-gradient and animating their position.

.firework {
  position: absolute;
  top: 100px;
  left: 100px;
  border-radius: 30%;
  height: 64px;
  width: 2px;
  background: linear-gradient(to top, red, red);
  background-repeat: no-repeat;
  background-size: 100% 64px;
  background-position: 0px -64px;
  transform-origin: bottom;
  animation: firework-0 1s 1;
}
.firework:nth-child(2){
  transform: rotate(36deg)
}
.firework:nth-child(3){
  transform: rotate(72deg)
}
.firework:nth-child(4){
  transform: rotate(108deg)
}
.firework:nth-child(5){
  transform: rotate(144deg)
}
.firework:nth-child(6){
  transform: rotate(180deg)
}
.firework:nth-child(7){
  transform: rotate(216deg)
}
.firework:nth-child(8){
  transform: rotate(252deg)
}
.firework:nth-child(9){
  transform: rotate(288deg)
}
.firework:nth-child(10){
  transform: rotate(324deg)
}

@keyframes firework-0 {
  0% {
    background-position: 0px 64px;
  }
  50% {
    background-position: 0px 0px;
  }
  100% {
    background-position: 0px -64px;
  }
}
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>

Note: The no. of elements can be reduced greatly if pseudo-elements are used but I'll leave it to you.

Lines that don't meet at the center:

If you need them to not converge at a point and look like they are apart but placed around a circle then the animation becomes a bit more complex. It would require a gradient which is transparent for half of its size and colored for the other . By animating the background's size and position, the required effect can be achieved.

The calc in background position is critical because it makes the gradient be positioned with respect to the bottom of the element and thus makes the animation work as expected.

.firework {
  position: absolute;
  top: 100px;
  left: 100px;
  border-radius: 30%;
  height: 64px;
  width: 2px;
  background: linear-gradient(to top, transparent 32px, red 32px);
  background-repeat: no-repeat;
  background-size: 100% 64px;
  background-position: 100% calc(100% - 32px);
  transform-origin: bottom;
  animation: firework-0 1s 1;
}
.firework:nth-child(2) {
  transform: rotate(36deg)
}
.firework:nth-child(3) {
  transform: rotate(72deg)
}
.firework:nth-child(4) {
  transform: rotate(108deg)
}
.firework:nth-child(5) {
  transform: rotate(144deg)
}
.firework:nth-child(6) {
  transform: rotate(180deg)
}
.firework:nth-child(7) {
  transform: rotate(216deg)
}
.firework:nth-child(8) {
  transform: rotate(252deg)
}
.firework:nth-child(9) {
  transform: rotate(288deg)
}
.firework:nth-child(10) {
  transform: rotate(324deg)
}
@keyframes firework-0 {
  0% {
    background-size: 100% 32px;
    background-position: 100% calc(100% - 0px);
  }
  50% {
    background-size: 100% 64px;
    background-position: 100% calc(100% - 0px);
  }
  100% {
    background-size: 100% 32px;
    background-position: 100% calc(100% - 32px);
  }
}
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>
<div class="firework"></div>

Using SVG:

For effects like this, my recommendation would be to use SVG as SVG is the correct tool for creating them. It is also fairly simple to create and maintain.

All that we need are 10 line elements that draw a line from one point to another, give it a dashed fill and then animating the stroke-dashoffset to get the required effect.

Lines meeting at a center:

For this effect, the lines have to be created such that the first coordinate (x,y) will be the center of the imaginary circle (32,32). For second coordinate, we should find points on the imaginary circle based on the angle of the line. The formula is described here.

Note: In SVG, 0 degree corresponds to 3 o'clock position.

svg{
  width: 128px;
  height: 128px;
}
line{
  stroke: red;
  animation: firework 1s 1;
  stroke-dasharray: 32 32; /* radius radius */
  stroke-dashoffset: -32; /* -radius */
}
@keyframes firework{
  from{
    stroke-dashoffset: 32; /* radius */
  }
  to{
    stroke-dashoffset: -32; /* -radius */
  }
}
<svg viewBox='0 0 64 64'>
  <g>
    <line x1='32' y1='32' x2='32' y2='0' />
    <line x1='32' y1='32' x2='50.80' y2='6.11' />
    <line x1='32' y1='32' x2='62.43' y2='22.11' />
    <line x1='32' y1='32' x2='62.43' y2='41.88' />
    <line x1='32' y1='32' x2='50.80' y2='57.88' />
    <line x1='32' y1='32' x2='32' y2='64' />
    <line x1='32' y1='32' x2='13.19' y2='57.88' />
    <line x1='32' y1='32' x2='1.56' y2='41.88' />
    <line x1='32' y1='32' x2='1.56' y2='22.11' />
    <line x1='32' y1='32' x2='13.19' y2='6.11' />
  </g>
</svg>

Lines that don't meet at center: (but their projections will)

For this effect, we should find points along two circles for the two coordinates. The first coordinate will be on an inner circle whose radius is smaller than the first (16 in this demo). The second will be on the larger circle (radius = 32 in this demo).

svg{
  width: 128px;
  height: 128px;
}
line{
  stroke: red;
  animation: firework 1s 1;
  stroke-dasharray: 16 16; /* length of the line length of the line */
  stroke-dashoffset: -16; /* -length */
}
@keyframes firework{
  from{
    stroke-dashoffset: 16; /* length */
  }
  to{
    stroke-dashoffset: -16; /* length */
  }
}
<svg viewBox='0 0 64 64'>
  <g>
    <line x1='32' y1='16' x2='32' y2='0' />
    <line x1='41.40' y1='19.05' x2='50.80' y2='6.11' />
    <line x1='47.21' y1='27.05' x2='62.43' y2='22.11' />
    <line x1='47.21' y1='36.94' x2='62.43' y2='41.88' />
    <line x1='41.40' y1='44.94' x2='50.80' y2='57.88' />
    <line x1='32' y1='48' x2='32' y2='64' />
    <line x1='22.59' y1='44.94' x2='13.19' y2='57.88' />
    <line x1='16.78' y1='36.94' x2='1.56' y2='41.88' />
    <line x1='16.78' y1='27.05' x2='1.56' y2='22.11' />
    <line x1='22.59' y1='19.05' x2='13.19' y2='6.11' />
  </g>
</svg>
Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214