- const sprinkleCount = 50
- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min)) + min
- let durations = []
- const colors = ['#ff6698','#ffb366','#98ff66','#6698ff']
//- const sprinkles = new Array(sprinkleCount).fill().map(() => {
//- 	const characteristics = {
//-       speed: randomInRange(3, 8),
//-       delay: randomInRange(1, sprinklerCount / 4),
//-       top: randomInRange(0, 40),
//-       color: colors[Math.floor(Math.random() * colors.length)],
//-       rotation: randomInRange(0, 360),
//-       radius: randomInRange(0, 100),
//-       size: randomInRange(4, 15),
//-     }
//-     durations.push(characteristics.delay + characteristics.speed)
//-     return characteristics
//- })
- const sprinkles = Array(sprinkleCount).fill().map(()=>{const a={speed:randomInRange(3,8),delay:randomInRange(1,sprinkleCount/4),top:randomInRange(0,40),color:colors[Math.floor(Math.random()*colors.length)],rotation:randomInRange(0,360),radius:randomInRange(0,100),size:randomInRange(4,15)};return durations.push(a.delay+a.speed),a})
input(type='checkbox', id='cheat')
.cheat-box
  label(for='cheat') Zoom (Cheat 🐵)
form.game__container
  //- The start checkbox will show/hide the intro screen
  //- Clicking it reveal the moving blocks
  input.game__start(type='checkbox')
  //- The winning box needs to go here, it can appear after a certain time
  input.game__winning(type='checkbox', style=`--delay: ${Math.max(...durations) - 3};`)
  .game__header
    .game__header-content
      .game__title Cake 😋
      .game__sub-title.sub-text Click to start
  .game
    .board
      - let a = 0
      while a < sprinkleCount
        - const {speed, delay, top, color, rotation, size, radius } = sprinkles[a]
        .sprinkle(style=`--speed: ${speed}; --delay: ${delay}; --top: ${top}; --size: ${size}`)
          .sprinkle__shape(style=`--color: ${color}; --rotation: ${rotation}; --radius: ${radius}`)
        //- Bump the count
        - a++
      .game__over--internal
        .game__no-cake.sub-text No cake for you!<br>Click to restart
        input(type='reset', value='Restart')
      .win(style=`--delay: ${Math.max(...durations) - 3};`)
        .cake 🎂
  //- If we hover out of bounds, there needs to be a reset
  //- Otherwise it's going to be pretty easy to win 😅
  .game__over.sub-text Hover to restart
  .game__won
    .cake 🎂
    input(type='reset')
View Compiled
*
  box-sizing border-box


:root
  --active 1
  --gameTime $gameTime
  --bg #ffffff
  --body #65c6bb
  --primaryText #111
  --secondaryText #888

body
  align-items center
  background var(--body)
  display flex
  justify-content center
  min-height 100vh
  font-family -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif

$gameTime = 5

.game
  height 50px
  position absolute
  width 100px

  &__header
    font-size 1rem
    text-transform uppercase
    position absolute
    top 0
    left 0
    display flex
    height 50px
    width 100px
    align-items center
    justify-content center
    flex-direction column
    color var(--primaryText)
    background var(--bg)
    transform translate(0, 0)
    transition transform .25s ease 0s
    z-index 2

  &__start
    position absolute
    top 0
    right 0
    bottom 0
    left 0
    opacity 0
    cursor pointer
    height 100%
    width 100%
    z-index 3

  &__container
    background var(--bg)
    height 50px
    width 100px
    position relative
    overflow hidden
    margin 0
    transition box-shadow .25s ease 0s, transform .25s ease 0s
    box-shadow 4px 4px 2px 0px #444

    .game__start:checked ~ .game__over
      transform translate(0, 0)
      z-index 3

    .game__start:checked ~ .game__header
      transform translate(0, -100%)

    &:hover
      box-shadow 0 0 0 0 #444

      .game__start:checked

        & ~ .game__winning
          animation arrive 5s calc(var(--delay) * 1s) forwards

        & ~ .game__over
          transform translate(0, -100%)

        & ~ .game
          --active 0
          cursor crosshair
          z-index 3

          .win
            animation arrive 5s calc(var(--delay) * 1s) forwards

          .cake
            animation pulse 4s calc((var(--delay) + 5) * 1s) infinite linear

          .sprinkle
            animation travel calc(var(--speed) * 1s) calc(var(--delay) * 1s) linear forwards

            &:hover
              animation none
              background var(--bg)
              top 0
              right 0
              bottom 0
              left 0
              height 50px
              width 100px
              transform translate(0, 0)
              z-index 3

              .sprinkle__shape
                visibility hidden

              // If we hover over an sprinkle we need to display a way for the user to reset
              ~ .game__over--internal
                background var(--bg)
                display flex
                z-index 5
                transform translate(0, 0)


.game__over--internal
  position absolute
  top 0
  right 0
  bottom 0
  left 0
  display flex
  align-items center
  justify-content center
  transform translate(0, 100%)
  transition transform .25s ease 0s

  &:hover
    display flex
    background var(--bg)
    transform translate(0, 0)
    z-index 5

label
input
  cursor pointer

.cheat-box
  position absolute
  top 1rem
  right 2rem
  text-transform uppercase
  font-size 0.6rem

#cheat
  position absolute
  top .85rem
  right 1rem

  &:checked ~ form
    transform scale(2.5)

.game__over
  background var(--bg)
  position absolute
  top 0
  right 0
  bottom 100%
  height 50px
  width 100px
  line-height 50px
  left 0
  // z-index 5
  text-align center
  transition transform .25s ease 0s
  transform translate(0, -100%)

.board
  position absolute
  top 0
  right 0
  bottom 0
  left 0
  background var(--bg)
  transition transform .25s ease 0s
  transform translate(0, calc(var(--active) * 100%))

// Overlay the reset button so that a user can click anywhere to restart
[type='reset']
  position absolute
  top 0
  left 0
  height 50px
  width 100px
  opacity 0
  cursor pointer

.sub-text
  font-size 0.6rem
  white-space nowrap
  color var(--secondaryText)
  text-transform uppercase

/**
  * Dots that need to be avoided, infinite animation for debugging
  */
.sprinkle
  height calc(var(--size) * 1px)
  width calc(var(--size) * 1px)
  position absolute
  top calc(var(--top) * 1px)
  left calc((var(--size) * 1px) + 100%)

  &__shape
    background var(--color)
    position absolute
    top 50%
    left 50%
    height 100%
    width 100%
    border-radius calc(var(--radius) * 1%)
    transform translate(-50%, -50%) rotate(calc(var(--rotation) * 1deg))

.win
  height 50px
  width 100px
  position absolute
  top 0
  left 0
  font-size 2rem
  display flex
  align-items center
  justify-content center
  transform translate(100%, 0)

.game__winning
  height 2.5rem
  width 2.5rem
  opacity 0
  position absolute
  top 50%
  left 50%
  margin-left -1.25rem
  margin-top -1.25rem
  cursor pointer
  transform translate(150px, 0)
  z-index 5

  &:checked ~ .game__won
    display block


.game__won
  background orange
  position absolute
  top 0
  right 0
  bottom 0
  left 0
  z-index 6
  font-size 2.2rem
  text-align center
  display none
  animation party .8s infinite linear

  .cake
    position absolute
    top 50%
    left 50%
    transform translate(-50%, -50%)


// ANIMATIONS
@keyframes travel
  to
    transform translate(calc((var(--size) + 150) * -1px), 0)

@keyframes arrive
  to
    transform translate(0, 0)

@keyframes pulse
  50%
    transform scale(1.1)

@keyframes party
  0%
    background #8585ff
  30%
    background #ff85c2
  55%
    background #85ffc2
  80%
    background #ffff85


// @keyframes score
//   from
//     counter-increment score 0
//   to
//     counter-increment score 100
/**
  * Thought this was quite cool. Can increment a counter via keyframes.
  * Quite some possibilities with this. You could tell someone how far
  * they got etc. maybe?
  */
// @keyframes score
//   for $second in (0...$gameTime)
//     {(100 / $gameTime * $second) * 1%}
//       counter-increment score floor((100 / $gameTime) * $second)
View Compiled
// Nope 😁
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.