How vue mounts canvas

want to use a ready-made canvas effect on the Internet to introduce into vue, I don"t know why it doesn"t work, ask for a solution, the code can be run directly, I don"t know how to use it in vue


var wave = (function () {
var ctx
var waveImage
var canvasWidth
var canvasHeight
var needAnimate = false
function init (callback) {
    var wave = document.getElementById("wave")
    var canvas = document.createElement("canvas")
    if (!canvas.getContext) return
        ctx = canvas.getContext("2d")
        canvasWidth = 200
        canvasHeight = 200
        canvas.setAttribute("width", canvasWidth)
        canvas.setAttribute("height", canvasHeight)
        wave.appendChild(canvas)
        waveImage = new Image()
        waveImage.onload = function () {
          waveImage.onload = null
          callback()
        }
        waveImage.src = "../images/wave.png"
    }
    
function animate () {
    var waveX = 0
    var waveY = 0
    var waveXmin = -203
    var waveYmax = canvasHeight * 0.7
    var requestAnimationFrame =
      window.requestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function (callback) { window.setTimeout(callback, 1000 / 60) }
      
function loop () {
  ctx.clearRect(0, 0, canvasWidth, canvasHeight)
  if (!needAnimate) return
  if (waveY < waveXmin) waveY += 1.5
  if (waveX < waveYmax) waveX = 0; else waveX -= 3

  ctx.globalCompositeOperation = "source-over"
  ctx.beginPath()
  ctx.arc(canvasWidth / 2, canvasHeight / 2, canvasHeight / 2, 0, Math.PI * 2, true)
  ctx.closePath()
  ctx.fill()

  ctx.globalCompositeOperation = "source-in"
  ctx.drawImage(waveImage, waveX, canvasHeight - waveY)

  requestAnimationFrame(loop)
}
loop()
}

function start () {
if (!ctx) return init(start)
needAnimate = true
setTimeout(function () {
  if (needAnimate) animate()
}, 500)
}

function stop () {
  needAnimate = false
}
return {start: start, stop: stop}
}())
export default wave

this is the JS code

<div id="wave" class="wave"><span>60%</span></div>
.wave{width:200px;height:200px;overflow:hidden;border-radius:50%;background:rgba(255,203,103,.6);margin:100px auto;position:relative;text-align:center;display:table-cell;vertical-align:middle;}
.wave span{display:inline-block;color:-sharpfff;font-size:20px;position:relative;z-index:2;}
.wave canvas{position:absolute;left:0;top:0;z-index:1;}

this is HTML

import wave from "../assets/js/circle"  
export default {  
  name: "home",  
  created: function () {  
    wave.start()  
  },  
} 

there is no error, but the effect is that it will not come out. This is a quote. Could you please tell me what the problem is? But when I change mounted to created, I will say Cannot read property "appendChild" of null. "Thank you, boss.

Mar.28,2021

but when I change mounted to created, I will say Cannot read property 'appendChild' of null "
.

this is because at the time of created, the Dom node has not yet been rendered to the page, and the DIV of id=wave cannot be found at this time.

< hr >

change to plug-in for Vue

<template>
  <div id="wave" class="wave" v-wave><span>60%</span></div>
</template>

<script>
import wave from './a'
export default {

}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.wave{width:200px;height:200px;overflow:hidden;border-radius:50%;background:rgba(255,203,103,.6);margin:100px auto;position:relative;text-align:center;display:table-cell;vertical-align:middle;}
.wave span{display:inline-block;color:-sharpfff;font-size:20px;position:relative;z-index:2;}
.wave canvas{position:absolute;left:0;top:0;z-index:1;}
</style>

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import wave from './components/a'

Vue.config.productionTip = false
Vue.use(wave)
/* eslint-disable no-new */
new Vue({
  el: '-sharpapp',
  router,
  components: { App },
  template: '<App/>'
})

that's fine.

Menu