2

How do I make each array element of a canvas transparent?

For example, I have a canvas that draws a lots of particles:

<!Doctype HTML>

<html>
  <head>
    <meta charset="utf-8">
    <title>Snow Field</title>
  </head>
  
  <body>
    <img src="https://images.unsplash.com/photo-1415025148099-17fe74102b28?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjF9" style="position: fixed ; z-index: -1 ; width: 100%">
    <canvas id="snow-field-canvas" style="position: fixed"></canvas>
  
    <script>
      class SnowField {
        constructor() {
          this.fps = 40
          this.canvas = null
          this.width = 0
          this.height = 0
          this.snow = 200
          this.snowParticles = []
          this.intervalId = 0
        }

        initialize(canvas) {
          this.width = window.innerWidth
          this.height = window.innerHeight

          window.addEventListener('resize', () => {
            this.width = window.innerWidth
            this.height = window.innerHeight
            this.canvas.width = this.width
            this.canvas.height = this.height

            for (let i = 0 ; i < this.snow ; ++i)
              this.snowParticles[i] = new Snow(Math.random() * this.width, Math.random() * this.height)

            this.update()
          })

          this.canvas = document.getElementById(canvas)
          this.canvas.width = this.width
          this.canvas.height = this.height
        }

        start() {
          for (let i = 0 ; i < this.snow ; ++i )
            this.snowParticles[i] = new Snow(Math.random() * this.width, Math.random() * this.height)

          this.intervalId = setInterval(() => { this.update() }, 1000 / this.fps)
        }

        update() {
          var ctx = this.canvas.getContext('2d')
          ctx.clearRect(0, 0, this.width, this.height)

          for (let i = 0 ; i < this.snow ; ++i) {
            var snow = this.snowParticles[i]
            snow.x += Math.sin(i)
            snow.y += snow.yspeed
            // Change the opacity of each snow by 0.01 or something like that

            if ((snow.y > this.height) || (snow.x < -snow.size || snow.x > this.width))
              this.snowParticles[i] = new Snow(Math.random() * this.width, 0)

            ctx.fillStyle = '#fff'
            ctx.fillRect(snow.x, snow.y, snow.size, snow.size)
          }
        }
      }

      class Snow {
        constructor(x, y) {
          this.x = x
          this.y = y
          this.size = Math.random() * 2 + 1
          this.yspeed = Math.random() * 5 + 1
        }
      }
      
      var snowfield = new SnowField()
      snowfield.initialize('snow-field-canvas')
      snowfield.start()
    </script>
  </body>
</html>

Now, I want to decrease the opacity of each snow in the for loop by a small amount (0.01 or something like that) so it looks like snows falling and fading when reaching the end of the viewport.

I have followed the answers here, they tend to change the opacity of the whole canvas the opacity of all the snows within it. and that's not what I want.

S.Goswami
  • 1,370
  • 8
  • 19

1 Answers1

4

If you try to incrementally decrease, like in steps of .01, the snow will disappear fairly quickly because the update function will be called multiple times. Instead, I modified your code to change the opacity based on the distance of the snow particle from the ground, and a dampening factor.

class SnowField {
  constructor() {
    this.fps = 40
    this.canvas = null
    this.width = 0
    this.height = 0
    this.snow = 200
    this.snowParticles = []
    this.intervalId = 0
  }

  initialize(canvas) {
    this.width = window.innerWidth
    this.height = window.innerHeight

    window.addEventListener('resize', () => {
      this.width = window.innerWidth
      this.height = window.innerHeight
      this.canvas.width = this.width
      this.canvas.height = this.height

      for (let i = 0; i < this.snow; ++i)
        this.snowParticles[i] = new Snow(Math.random() * this.width, Math.random() * this.height)

      this.update()
    })

    this.canvas = document.getElementById(canvas)
    this.canvas.width = this.width
    this.canvas.height = this.height
  }

  start() {
    for (let i = 0; i < this.snow; ++i)
      this.snowParticles[i] = new Snow(Math.random() * this.width, Math.random() * this.height)

    this.intervalId = setInterval(() => {
      this.update()
    }, 1000 / this.fps)
  }

  update() {
    var ctx = this.canvas.getContext('2d')
    ctx.clearRect(0, 0, this.width, this.height)

    for (let i = 0; i < this.snow; ++i) {
      var snow = this.snowParticles[i]
      snow.x += Math.sin(i)
      snow.y += snow.yspeed
      // Change the opacity of each snow by 0.01 or something like that 
      var op = this.height / ((snow.y + 1) * snow.dampening)

      ctx.fillStyle = "rgba(255, 255, 255, " + op + ")";

      if ((snow.y > this.height) || (snow.x < -snow.size || snow.x > this.width))
        this.snowParticles[i] = new Snow(Math.random() * this.width, 0)

      //ctx.fillStyle = '#fff'
      ctx.fillRect(snow.x, snow.y, snow.size, snow.size)
    }
  }
}

class Snow {
  constructor(x, y) {
    this.x = x
    this.dampening = 10 // Adjust this value to make snow disappear slower/faster/Random
    this.y = y
    this.size = Math.random() * 2 + 1
    this.yspeed = Math.random() * 5 + 1
  }
}

var snowfield = new SnowField()
snowfield.initialize('snow-field-canvas')
snowfield.start()
<img src="https://images.unsplash.com/photo-1415025148099-17fe74102b28?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjF9" style="position: fixed ; z-index: -1 ; width: 100%">
<canvas id="snow-field-canvas" style="position: fixed"></canvas>
S.Goswami
  • 1,370
  • 8
  • 19
Anurag Srivastava
  • 12,230
  • 3
  • 21
  • 36