vue如何挂载canvas

想用一个网上现成的canvas效果引入到vue中,不知道为何失效,求大神解答,代码直接用是可以运行的,不知道怎么引到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

这是JS代码

<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:#fff;font-size:20px;position:relative;z-index:2;}
.wave canvas{position:absolute;left:0;top:0;z-index:1;}

这是HTML

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

没有报错,但是效果就是不出来,这是引用,麻烦可以告诉我什么问题吗?但我把mounted改成created的时候会报Cannot read property 'appendChild' of null",谢谢各位大佬啦

阅读 26.4k
1 个回答
但我把mounted改成created的时候会报Cannot read property 'appendChild' of null"

这是因为created的时候,Dom节点还没有渲染出来到页面上,这个时候是找不到id=wave的DIV的。


改成Vue的插件

import wavePng from './wave.png'
export default {
    install(Vue){
        Vue.directive('wave', {
            inserted: function(el){
                start(el)
            }
        })
    }
}
    var ctx;
    var waveImage;
    var canvasWidth;
    var canvasHeight;
    var needAnimate = false;

    function init (callback, wave) {
        // var wave = document.getElementById('wave');
        var canvas = document.createElement('canvas');
        if (!canvas.getContext) return;
        ctx = canvas.getContext('2d');
        canvasWidth = wave.offsetWidth;
        canvasHeight = wave.offsetHeight;
        canvas.setAttribute('width', canvasWidth);
        canvas.setAttribute('height', canvasHeight);
        wave.appendChild(canvas);
        waveImage = new Image();
        waveImage.onload = function () {
            console.log('000')
            waveImage.onload = null;
            callback(wave);
        }
        waveImage.src = wavePng;
    }

    function animate () {
        var waveX = 0;
        var waveY = 0;
        var waveX_min = -203;
        var waveY_max = 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 < waveY_max) waveY += 1.5;
            if (waveX < waveX_min) 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 (el) {
        console.log(1)
        if (!ctx) return init(start, el);
        needAnimate = true;
        setTimeout(function () {
            if (needAnimate) animate();
        }, 500);
    }
    function stop () {
        needAnimate = false;
    }

上述的代码,把原先插件的闭包去掉了,因为这个改成vue的插件webpack打包完本身就是一个闭包了。
然后使用的话,就用指令的形式

<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:#fff;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: '#app',
  router,
  components: { App },
  template: '<App/>'
})

这样就可以了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏