javascripthtmlcssanimation

How do I mimic grass being cut using JavaScript and CSS?


I've been practicing animation in JavaScript and CSS and there's a transition I want to do that goes from dark green to a light green to mimic grass being cut but the animation goes too quick and doesn't properly track the lawn mower's position, so I was wondering what I did wrong. I think it has to do with the progress variable but I'm not sure.

Also I'm not sure if the whole creating a strip of grass in JavaScript is the best approach to this animation.

Any help would be appreciated.

const grass = document.getElementById('grass');
const lawnMower = document.getElementById('lawnMower');

// Function to create a vertical strip of grass that transitions color
function createGrassStrip(x, width) {
  const strip = document.createElement('div');
  strip.style.position = 'absolute';
  strip.style.top = '0';
  strip.style.left = `${x}px`;
  strip.style.width = `${width}px`;
  strip.style.height = '100%';
  grass.appendChild(strip);
  return strip;
}

// Function to update the grass strip color based on lawn mower position
function updateGrassStrip(strip, mowerPosition) {
  const viewportHeight = window.innerHeight;
  const progress = (viewportHeight / mowerPosition) * 10;

  // Transition from dark green to light green BELOW the lawn mower
  const darkGreen = '#388E3C';
  const lightGreen = '#8BC34A';
  strip.style.background = `repeating-linear-gradient(0deg, ${darkGreen}, ${darkGreen} ${progress}%, ${lightGreen} ${progress}%, ${lightGreen} 100%)`;
}

// Create a grass strip for the lawn mower's path
const mowerWidth = 100; // Width of the lawn mower
const mowerX = (window.innerWidth - mowerWidth) / 2; // Center the strip
const grassStrip = createGrassStrip(mowerX, mowerWidth);

// Update grass strip color on every animation frame
function animate() {
  const mowerPosition = lawnMower.getBoundingClientRect().top;
  updateGrassStrip(grassStrip, mowerPosition);
  requestAnimationFrame(animate);
}

// Start the animation
animate();
body,
html {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  font-family: Arial, sans-serif;
}

.lawn {
  position: relative;
  width: 100%;
  height: 100vh;
  background-color: #4CAF50;
  overflow: hidden;
}

.grass {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #388E3C;
  );
}

.lawn-mower {
  position: absolute;
  top: 0;
  left: 50%;
  width: 100px;
  height: 50px;
  background-color: #FF5722;
  border-radius: 10px;
  transform: translateX(-50%);
  animation: mow 2.5s linear;
}

@keyframes mow {
  0% {
    top: 0;
  }

  50% {
    top: 50%;
  }

  100% {
    top: 100%;
  }
}

.content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  color: white;
  z-index: 1;
}
<div class="lawn">
  <div class="grass" id="grass"></div>
  <div class="lawn-mower" id="lawnMower"></div>
</div>
<div class="content">
</div>


Solution

  • A CSS-only solution with a minimal code:

    .box {
      width: 100px;
      aspect-ratio: 2;
      border-radius: 10px;
      box-shadow: 0 -100vh 0 calc(100vh - 10px) #8bc34a; /* a huge box-shadow (taking the radius into consideration) */
      clip-path: inset(-100vh 0 0); /* show only the top part of the box-shadow*/
      background: #FF5722;
      animation: move 5s linear forwards;
    }
    @keyframes move {
     0% {translate: 0 -100%} /* hide from the top*/
     to {translate: 0 100vh} /* move until the bottom of the page */
    }
    
    
    body {
      background: #388E3C;
      margin: 0;
      overflow: hidden;
      place-items: center;
    }
    <div class="box"></div>