script(type="text/x-template" id="little-car")
  g
    mt-shortcut(shortcode="left", @trigger="moveLeft")
    mt-shortcut(shortcode="right", @trigger="moveRight")
    mt-shortcut(shortcode="up", @trigger="moveUp")
    mt-shortcut(shortcode="down", @trigger="moveDown")
    v-timer(time="0.05", @buzz="updatePosition")
    
    path(:d="trace", stroke="red", fill="none")
    rect(:x="x", :y="y", width="20" height="20" fill="black")

svg#app
  little-car
View Compiled
svg
  margin: 5vh 0 0 5vw
  width: 90vw
  height: 90vh
  border: solid 1px black
  box-sizing: border-box
html, body
  margin: 0
  padding: 0
View Compiled
// Composant Mousetrap, je ne reviens pas dessus
var mtShortcut = {
  props: ['shortcode', 'modifier'],
  // S'execute lors de l'apparition de l'element
  beforeMount () {
    Mousetrap.bind(this.shortcode, evt => this.$emit('trigger', evt), this.modifier)
  },
  // Se supprime lors de la disparition de l'element
  beforeDestroy () {
    Mousetrap.unbind(this.shortcode, this.modifier)
  }
}
// Un timer permettant d'executer une method à interval régulier
// La propriété 'time' est à donner en secondes.
var vTimer = {
  props: ['time'],
  data () {
    return { timer: undefined }
  },
  // S'execute lors de l'apparition de l'element
  beforeMount () {
    this.timer = setInterval(e => this.$emit("buzz"), parseFloat(this.time)*1000)
  },
  // Se supprime lors de la disparition de l'element
  beforeDestroy () {
    this.clearInterval(this.timer)
  },
  reset () {
    this.clearInterval(this.timer)
    this.timer = setInterval(e => this.$emit("buzz"), parseFloat(this.time)*1000)
  }
}

// Enfin, notre composant.
let littleCar = {
  // la liste des composants dont nous allons avoir besoin
  components: { mtShortcut, vTimer },
  template: '#little-car',
  data() {
    // Ce petit paragraphe permet de récupérer le centre de notre SVG
    // qui nous servira de point de départ
    let boundingBox = document.querySelector('svg').getBoundingClientRect(),
        screenWidth = boundingBox.width,
        screenHeight = boundingBox.height,
        startingPosX = (screenWidth / 2) - 10,
        startingPosY = (screenHeight / 2) - 10
    
    // Trace est cette trace rouge qui suit notre bolide
    return {
      x: startingPosX,
      y: startingPosY,
      vx: 0,
      vy: 0,
      trace: `M${startingPosX + 10} ${startingPosY + 10}`
    }
  },
  methods: {
    updatePosition () {
      // On récupère les dimmensions de l'écran
      let boundingBox = document.querySelector('svg').getBoundingClientRect(),
          screenWidth = boundingBox.width - 20,
          screenHeight = boundingBox.height - 20,
          // On détermine l'éventuelle nouvelle position (position actuelle + vitesse)
          newX = this.x + this.vx,
          newY = this.y + this.vy
      
      // On vérifie que la nouvelle position est bien à l'interieur du SVG, sinon on la contraint
      if (newX > screenWidth) {
        newX = screenWidth
        // On inverse la vitesse pour un effet 'rebond'
        this.vx = - this.vx
      } else if (newX < 0) {
        newX = 0 
        this.vx = - this.vx
      }
      
      // comme au dessus, mais pour l'axe Y
      if (newY > screenHeight) {
        newY = screenHeight
        this.vy = - this.vy
      } else if (newY < 0) {
        newY = 0 
        this.vy = - this.vy
      }
      
      // On applique la nouvelle position
      this.x = newX
      this.y = newY
      // on trace
      this.trace += `L${newX + 10} ${newY + 10}`
      // On réduit la vitesse, pour ajouter des 'frictions'
      this.vx = this.vx * 0.95
      this.vy = this.vy * 0.95
    },
    moveUp () {
      this.vy -= 5
    },
    moveDown () {
      this.vy += 5
    },
    moveRight () {
      this.vx += 5
    },
    moveLeft () {
      this.vx -= 5
    }
  }
}
//---------------------------------------------

new Vue({
  el: "#app",
  components: { littleCar },
})
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/mousetrap/1.6.0/mousetrap.min.js