Skip to content

Instantly share code, notes, and snippets.

@suvirbhargav
Created April 13, 2020 14:51
Show Gist options
  • Select an option

  • Save suvirbhargav/b71e94cd6df21bf051cf1cba385eef1e to your computer and use it in GitHub Desktop.

Select an option

Save suvirbhargav/b71e94cd6df21bf051cf1cba385eef1e to your computer and use it in GitHub Desktop.

Revisions

  1. sb created this gist Apr 13, 2020.
    9 changes: 9 additions & 0 deletions crowd-simulator.markdown
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    Crowd Simulator
    ---------------
    Hey, remember _crowds_?

    Made with the wonderful [open peeps](https://www.openpeeps.com/) by [Pablo Stanley](https://www.instagram.com/pablostanley/)

    A [Pen](https://codepen.io/zadvorsky/pen/xxwbBQV) by [Szenia Zadvornykh](https://codepen.io/zadvorsky) on [CodePen](https://codepen.io).

    [License](https://codepen.io/zadvorsky/pen/xxwbBQV/license).
    1 change: 1 addition & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    <canvas id="canvas"></canvas>
    240 changes: 240 additions & 0 deletions script.babel
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,240 @@
    console.clear()
    console.log('lsakdfalskjdflnksd')

    const config = {
    src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/open-peeps-sheet.png',
    rows: 15,
    cols: 7
    }

    // UTILS

    const randomRange = (min, max) => min + Math.random() * (max - min)

    const randomIndex = (array) => randomRange(0, array.length) | 0

    const removeFromArray = (array, i) => array.splice(i, 1)[0]

    const removeItemFromArray = (array, item) => removeFromArray(array, array.indexOf(item))

    const removeRandomFromArray = (array) => removeFromArray(array, randomIndex(array))

    const getRandomFromArray = (array) => (
    array[randomIndex(array) | 0]
    )

    // TWEEN FACTORIES

    const resetPeep = ({ stage, peep }) => {
    const direction = Math.random() > 0.5 ? 1 : -1
    // using an ease function to skew random to lower values to help hide that peeps have no legs
    const offsetY = 100 - 250 * gsap.parseEase('power2.in')(Math.random())
    const startY = stage.height - peep.height + offsetY
    let startX
    let endX

    if (direction === 1) {
    startX = -peep.width
    endX = stage.width
    peep.scaleX = 1
    } else {
    startX = stage.width + peep.width
    endX = 0
    peep.scaleX = -1
    }

    peep.x = startX
    peep.y = startY
    peep.anchorY = startY

    return {
    startX,
    startY,
    endX
    }
    }

    const normalWalk = ({ peep, props }) => {
    const {
    startX,
    startY,
    endX
    } = props

    const xDuration = 10
    const yDuration = 0.25

    const tl = gsap.timeline()
    tl.timeScale(randomRange(0.5, 1.5))
    tl.to(peep, {
    duration: xDuration,
    x: endX,
    ease: 'none'
    }, 0)
    tl.to(peep, {
    duration: yDuration,
    repeat: xDuration / yDuration,
    yoyo: true,
    y: startY - 10
    }, 0)

    return tl
    }

    const walks = [
    normalWalk,
    ]

    // CLASSES

    class Peep {
    constructor({
    image,
    rect,
    }) {
    this.image = image
    this.setRect(rect)

    this.x = 0
    this.y = 0
    this.anchorY = 0
    this.scaleX = 1
    this.walk = null
    }

    setRect (rect) {
    this.rect = rect
    this.width = rect[2]
    this.height = rect[3]

    this.drawArgs = [
    this.image,
    ...rect,
    0, 0, this.width, this.height
    ]
    }

    render (ctx) {
    ctx.save()
    ctx.translate(this.x, this.y)
    ctx.scale(this.scaleX, 1)
    ctx.drawImage(...this.drawArgs)
    ctx.restore()
    }
    }

    // MAIN

    const img = document.createElement('img')
    img.onload = init
    img.src = config.src

    const canvas = document.querySelector('#canvas')
    const ctx = canvas.getContext('2d')

    const stage = {
    width: 0,
    height: 0,
    }

    const allPeeps = []
    const availablePeeps = []
    const crowd = []

    function init () {
    createPeeps()

    // resize also (re)populates the stage
    resize()

    gsap.ticker.add(render)
    window.addEventListener('resize', resize)
    }

    function createPeeps () {
    const {
    rows,
    cols
    } = config
    const {
    naturalWidth: width,
    naturalHeight: height
    } = img
    const total = rows * cols
    const rectWidth = width / rows
    const rectHeight = height / cols

    for (let i = 0; i < total; i++) {
    allPeeps.push(new Peep({
    image: img,
    rect: [
    (i % rows) * rectWidth,
    (i / rows | 0) * rectHeight,
    rectWidth,
    rectHeight,
    ]
    }))
    }
    }

    function resize () {
    stage.width = canvas.clientWidth
    stage.height = canvas.clientHeight
    canvas.width = stage.width * devicePixelRatio
    canvas.height = stage.height * devicePixelRatio

    crowd.forEach((peep) => {
    peep.walk.kill()
    })

    crowd.length = 0
    availablePeeps.length = 0
    availablePeeps.push(...allPeeps)

    initCrowd()
    }

    function initCrowd () {
    while (availablePeeps.length) {
    // setting random tween progress spreads the peeps out
    addPeepToCrowd().walk.progress(Math.random())
    }
    }

    function addPeepToCrowd () {
    const peep = removeRandomFromArray(availablePeeps)
    const walk = getRandomFromArray(walks)({
    peep,
    props: resetPeep({
    peep,
    stage,
    })
    }).eventCallback('onComplete', () => {
    removePeepFromCrowd(peep)
    addPeepToCrowd()
    })

    peep.walk = walk

    crowd.push(peep)
    crowd.sort((a, b) => a.anchorY - b.anchorY)

    return peep
    }

    function removePeepFromCrowd (peep) {
    removeItemFromArray(crowd, peep)
    availablePeeps.push(peep)
    }

    function render () {
    canvas.width = canvas.width
    ctx.save()
    ctx.scale(devicePixelRatio, devicePixelRatio)

    crowd.forEach((peep) => {
    peep.render(ctx)
    })

    ctx.restore()
    }
    1 change: 1 addition & 0 deletions scripts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script>
    11 changes: 11 additions & 0 deletions style.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    html, body {
    height: 100%;
    }
    body {
    margin: 0;
    }

    #canvas {
    width: 100%;
    height: 100%;
    }