1
\$\begingroup\$

I've have make this animation on the weekend as a "just for fun" thing. And for to play with various techniques I've seen in others code.

I think it works quite alright.

Nevertheless I would appreciate comments about:

  1. How to structure / shorten the JavaScript- and Sass-code better?

  2. How could the animation be improved? So that it runs smoother and more "natural"?

  3. How to improve the responsiveness?

Looking forward to read your comments and suggestions.

'use strict';

var field = document.querySelector('.field');
var starCount = 1000;

function addStar(parent, maxX, maxY) {
  var x = Math.floor(Math.random() * (maxX + 1));
  var y = Math.floor(Math.random() * (maxY + 1));
  var randomNumber = Math.random();

  var star = document.createElement('div');
  var starTop = document.createElement('div');
  var starBottom = document.createElement('div');
  var styleValue = 'left: ' + x + 'px; top: ' + y + 'px;';
  var starKind = '';

  switch (true) {
    case randomNumber < 0.25:
      starKind = 'large-star';
      break;
    case randomNumber < 0.5:
      starKind = 'medium-star';
      break;
    case randomNumber < 0.75:
      starKind = 'small-star';
      break;
    default:
      starKind = 'tiny-star';
  }

  starTop.classList.add(starKind + '-top');
  starBottom.classList.add(starKind + '-bottom');

  star.appendChild(starTop);
  star.appendChild(starBottom);

  star.setAttribute('style', styleValue);
  star.classList.add('star');
  star.classList.add(starKind);

  parent.appendChild(star);

  return star;
}

for (var i = 0; i < starCount; i++) {
  addStar(field, 4000, 400);
}
@keyframes rotateStar {
  0% {
    transform: rotate(0deg);
    opacity: 0.09; }
  10% {
    transform: rotate(36deg);
    opacity: 0.18; }
  20% {
    transform: rotate(72deg);
    opacity: 0.27; }
  30% {
    transform: rotate(108deg);
    opacity: 0.36; }
  40% {
    transform: rotate(144deg);
    opacity: 0.45; }
  50% {
    transform: rotate(180eg);
    opacity: 0.54; }
  60% {
    transform: rotate(216deg);
    opacity: 0.63; }
  70% {
    transform: rotate(248deg);
    opacity: 0.72; }
  80% {
    transform: rotate(284deg);
    opacity: 0.81; }
  90% {
    transform: rotate(320deg);
    opacity: 0.9; }
  100% {
    transform: rotate(356deg);
    opacity: 1.0; } }
@keyframes moveGlass {
  100% {
    margin-left: -3200px; } }
* {
  padding: 0;
  border: 0;
  margin: 0; }

body {
  background-color: black; }

.wrap {
  width: 100%;
  max-width: 800px;
  height: 200px;
  margin: 50px auto;
  overflow: hidden;
  border: 2px solid white;
  border-radius: 8px;
  line-height: 0;
  padding: 0 20px; }

.field {
  height: 200px;
  position: relative;
  background-color: #000000;
  animation: moveGlass 55s ease-in infinite alternate;
  display: inline-block; }

.star {
  background-color: white;
  opacity: 1.0;
  position: absolute;
  animation: rotateStar 2s infinite alternate; }

.star .large-star-top:before {
  border-bottom-right-radius: 100%; }

.star .large-star-top:after {
  left: 7px;
  border-bottom-left-radius: 100%; }

.star .large-star-bottom:before {
  top: 7px;
  border-top-right-radius: 100%; }

.star .large-star-bottom:after {
  top: 7px;
  left: 7px;
  border-top-left-radius: 100%; }

.star .large-star-top:before, .star .large-star-top:after,
.star .large-star-bottom:before, .star .large-star-bottom:after {
  content: "";
  position: absolute;
  background-color: black;
  width: 8px;
  height: 8px; }

.star .medium-star-top:before {
  border-bottom-right-radius: 100%; }

.star .medium-star-top:after {
  left: 6px;
  border-bottom-left-radius: 100%; }

.star .medium-star-bottom:before {
  top: 6px;
  border-top-right-radius: 100%; }

.star .medium-star-bottom:after {
  top: 6px;
  left: 6px;
  border-top-left-radius: 100%; }

.star .medium-star-top:before, .star .medium-star-top:after,
.star .medium-star-bottom:before, .star .medium-star-bottom:after {
  content: "";
  position: absolute;
  background-color: black;
  width: 7px;
  height: 7px; }

.star .small-star-top:before {
  border-bottom-right-radius: 100%; }

.star .small-star-top:after {
  left: 4px;
  border-bottom-left-radius: 100%; }

.star .small-star-bottom:before {
  top: 4px;
  border-top-right-radius: 100%; }

.star .small-star-bottom:after {
  top: 4px;
  left: 4px;
  border-top-left-radius: 100%; }

.star .small-star-top:before, .star .small-star-top:after,
.star .small-star-bottom:before, .star .small-star-bottom:after {
  content: "";
  position: absolute;
  background-color: black;
  width: 5px;
  height: 5px; }

.star .tiny-star-top:before {
  border-bottom-right-radius: 100%; }

.star .tiny-star-top:after {
  left: 3px;
  border-bottom-left-radius: 100%; }

.star .tiny-star-bottom:before {
  top: 3px;
  border-top-right-radius: 100%; }

.star .tiny-star-bottom:after {
  top: 3px;
  left: 3px;
  border-top-left-radius: 100%; }

.star .tiny-star-top:before, .star .tiny-star-top:after,
.star .tiny-star-bottom:before, .star .tiny-star-bottom:after {
  content: "";
  position: absolute;
  background-color: black;
  width: 4px;
  height: 4px; }

.star.large-star {
  width: 14px;
  height: 14px; }

.star.medium-star {
  width: 12px;
  height: 12px; }

.star.small-star {
  width: 8px;
  height: 8px; }

.star.tiny-star {
  width: 6px;
  height: 6px; }
<div class="wrap">
  <div class="field"></div>
</div>

Here's the uncompiled Sass-code:

$largeStarDimension: 14px;
$mediumStarDimension: 12px;
$smallStarDimension: 8px;
$tinyStarDimension: 6px;
$animationDuration: 2s;
$windowDimension: 200px;
$primaryColor: #000000;
@mixin addOffsets($starClass, $dimension) {
  .star .#{$starClass}-top:before {
    border-bottom-right-radius:  100%;
  }
  .star .#{$starClass}-top:after {
    left: $dimension;
    border-bottom-left-radius:  100%;
  }
  .star .#{$starClass}-bottom:before {
    top: $dimension;
    border-top-right-radius: 100%;
  }
  .star .#{$starClass}-bottom:after {
    top: $dimension;
    left: $dimension;
    border-top-left-radius: 100%;
  }
  .star .#{$starClass}-top:before, .star .#{$starClass}-top:after, 
  .star .#{$starClass}-bottom:before, .star .#{$starClass}-bottom:after {
    content: "";
    position: absolute;
    background-color: black;
    width: $dimension + 1;
    height: $dimension + 1;
  }
}
@mixin setStarDimensions($className, $starDimension) {
  .star.#{$className} {
    width: $starDimension;
    height: $starDimension;
  }  
}
@keyframes rotateStar {
  0% {
    transform: rotate(0deg);
    opacity: 0.09;
  }
  10%{
    transform: rotate(36deg);
    opacity: 0.18;
  }
  20% {
    transform: rotate(72deg);
    opacity: 0.27;
  }
  30% {
    transform: rotate(108deg);
    opacity: 0.36;
  }
  40% {
    transform: rotate(144deg);
    opacity: 0.45;
  }
  50% {
    transform: rotate(180eg);
    opacity: 0.54;
  }
  60% {
    transform: rotate(216deg);
    opacity: 0.63;
  }
  70% {
    transform: rotate(248deg);
    opacity: 0.72;
  }
  80% {
    transform: rotate(284deg);
    opacity: 0.81;
  }
  90% {
    transform: rotate(320deg);
    opacity: 0.9;
  }
  100% {
    transform: rotate(356deg);
    opacity: 1.0;
  }
}
@keyframes moveGlass {
  100% {
    margin-left: -3200px;
  }
}
* {
  padding: 0;
  border: 0;
  margin: 0;
}
body {
  background-color: black;
}
.wrap {
  width: 100%;
  max-width: 800px;
  height: 200px;
  margin: 50px auto;
  overflow: hidden;
  border: 2px solid white;
  border-radius: 8px;
  line-height: 0;
  padding: 0 20px;
}
.field {
  height: $windowDimension;
  position: relative;
  background-color: $primaryColor;
  animation: moveGlass 55s ease-in infinite alternate;
  display: inline-block;
}
.star {
  background-color: white;
  opacity: 1.0;
  position: absolute;
  animation: rotateStar 2s infinite alternate;
}
@include addOffsets("large-star", $largeStarDimension / 2);
@include addOffsets("medium-star", $mediumStarDimension / 2);
@include addOffsets("small-star", $smallStarDimension / 2);
@include addOffsets("tiny-star", $tinyStarDimension / 2);
@include setStarDimensions("large-star", $largeStarDimension);
@include setStarDimensions("medium-star", $mediumStarDimension);
@include setStarDimensions("small-star", $smallStarDimension);
@include setStarDimensions("tiny-star", $tinyStarDimension);
\$\endgroup\$
1
\$\begingroup\$

There's no need to define an animation frame at every 10%. This is prone to many errors (e.g. the change in angle from 60% to 70%).

You need only a from and a to (aliases for 0% and 100%):

@keyframes rotateStar {
    from { transform: rotate(0); }
    to { transform: rotate(360); }
}

I'm guessing you'd like the stars to always rotate in one direction, not alternate in animation-direction. One way to do that would be to decouple the star "blinking" and the rotation. Have the opacity be its own keyframe animation. Then you can have the blinking alternate while keeping the rotation normal.

The rotation animation would work with a linear animation-timing-function.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.