canvas#snow.snow
View Compiled
body,
html
  background-color: #111
  height: 100%
  width: 100%

.snow
  height: 100%
  position: absolute
  width: 100%
View Compiled
const Snow = (canvas, count, options) => {
  const ctx = canvas.getContext('2d')
  const snowflakes = []
 
  const add = item => snowflakes.push(item(canvas))

  const update = () => _.forEach(snowflakes, el => el.update())
 
  const resize = () => {
    ctx.canvas.width = canvas.offsetWidth
    ctx.canvas.height = canvas.offsetHeight

    _.forEach(snowflakes, el => el.resized())
  }

  const draw = () => {
    ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight)
    _.forEach(snowflakes, el => el.draw())
  }
  
  const events = () => {
    window.addEventListener('resize', resize)
  }

  const loop = () => {
    draw()
    update()
    animFrame(loop)
  }

  const init = () => {
    _.times(count, () => add(canvas => SnowItem(canvas, null, options)))
    events()
    loop()
  }

  init(count)
  resize()

  return { add, resize }
}

const defaultOptions = {
  color: 'orange',
  radius: [0.5, 3.0],
  speed: [1, 3],
  wind: [-0.5, 3.0]
}

const SnowItem = (canvas, drawFn = null, opts) => {
  const options = { ...defaultOptions, ...opts }
  const { radius, speed, wind, color } = options
  const params = {
    color,
    x: _.random(0, canvas.offsetWidth),
    y: _.random(-canvas.offsetHeight, 0),
    radius: _.random(...radius),
    speed: _.random(...speed),
    wind: _.random(...wind),
    isResized: false
  }
  const ctx = canvas.getContext('2d')
  
  const updateData = () => {
    params.x = _.random(0, canvas.offsetWidth)
    params.y = _.random(-canvas.offsetHeight, 0)
  }
  
  const resized = () => params.isResized = true

  const drawDefault = () => {
    ctx.beginPath()
    ctx.arc(params.x, params.y, params.radius, 0, 2 * Math.PI)
    ctx.fillStyle = params.color
    ctx.fill()
    ctx.closePath()
  }

  const draw = drawFn
    ? () => drawFn(ctx, params)
    : drawDefault

  const translate = () => {
    params.y += params.speed
    params.x += params.wind
  }

  const onDown = () => {
    if (params.y < canvas.offsetHeight) return

    if (params.isResized) {
      updateData()
      params.isResized = false
    } else {
      params.y = 0
      params.x = _.random(0, canvas.offsetWidth)
    }
  }

  const update = () => {
    translate()
    onDown()
  }

  return {
    update,
    resized,
    draw
  }
}

const el = document.querySelector('.container')
const wrapper = document.querySelector('body')
const canvas = document.getElementById('snow')

const animFrame = window.requestAnimationFrame ||
                  window.mozRequestAnimationFrame ||
                  window.webkitRequestAnimationFrame ||
                  window.msRequestAnimationFrame

Snow(canvas, 150, { color: 'white' })
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js