Vue.js 1000 SVG dots

by ChangJoo Park

HTML

<script src="https://cdn.jsdelivr.net/stats.js/r11/stats.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <svg>
    <circle v-for='point in model.points' :cx='point.x' :cy='point.y' r='2px' fill='#FC309D'></circle>
  </svg>
</div>

CSS

html,
  body {
    height: 100%;
    width: 100%;
    padding: 0;
    margin: 0;
    overflow: hidden;
  }

  svg {
    width: 400px;
    height: 400px;
  }

Vue

var stats = new Stats()

stats.setMode(2)
stats.domElement.style.position = 'absolute'
stats.domElement.style.right = '0px'
stats.domElement.style.top = '0px'
document.body.appendChild(stats.domElement)

var WIDTH = 400
var HEIGHT = 400

new Vue({
  el: '#app',
  data: {
    model: createModel(1000),
    optimized: true
  },
  created: function() {
    var self = this
    requestAnimationFrame(render)
    stats.begin()

    function render() {
      stats.end()
      stats.begin()
      requestAnimationFrame(render)
      self.model.step()
      if (self.optimized) {
        self.$forceUpdate()
      }
    }
  },
  methods: {
    toggleOptimization: function() {
      this.model = this.optimized ?
        createModel(1000) :
        Object.freeze(createModel(1000))
      this.optimized = !this.optimized
    }
  }
});

function createModel(count) {
  var points = []
  for (var i = 0; i < count; ++i) {
    points.push({
      x: Math.random() * WIDTH,
      y: Math.random() * HEIGHT,
      vx: Math.random() * 4 - 2,
      vy: Math.random() * 4 - 2
    })
  }
  return {
    points: points,
    step: step
  }

  function step() {
    points.forEach(move)
  }

  function move(p) {
    if (p.x > WIDTH || p.x < 0) p.vx *= -1
    if (p.y > HEIGHT || p.y < 0) p.vy *= -1
    p.y += p.vy
    p.x += p.vx
  }
}