使用vue.js在画布上绘图

Drawing onto a canvas with vue.js

本文关键字:绘图 vue js 使用      更新时间:2023-09-26

我不认为这一定是一个基于Vue的问题,但是我遇到了一些麻烦。

我想给画布写一个Vue变量。如果我删除vue,我的初始代码工作得很好,但是如果我添加vue,画布实际上不会启动。

我的代码

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = "black";
ctx.font="20px Georgia";
ctx.fillText("Hello World!",10,50);
var v = new Vue({
  el: '#app',
  data: {
    'exampleContent': 'This is TEXT'
  },
  watch: {
    exampleContent: function(val, oldVal) {
      ctx.clearRect(0,0,canvas.width,canvas.height);
      ctx.fillStyle = "black";
      ctx.font="20px Georgia";
      ctx.fillText(this.exampleContent,10,50);
    }
  }
});

如果我注释掉/* var v = new Vue({ ...,初始位有效。如果我在监视器中记录exampleContent的值,也可以工作。但是画布的某些部分不工作。

演示:http://codepen.io/EightArmsHQ/pen/EgGbgR?editors=1010

请查看此jsFiddle: https://jsfiddle.net/mani04/r4mbh6nu/

EDIT:更新为动态文本:https://jsfiddle.net/mani04/r4mbh6nu/1/

在上面的例子中,我能够写入画布,并确保input文本进入span,正如你所期望的那样,所有使用Vue.js

回到你的例子,HTML是:
<div id="app">
    <canvas id="canvas" width="800" height="600"></canvas>
    <input type="text" v-model="exampleContent" />
    <span>{{ exampleContent }}</span>
</div>

Vue.js读取#app中的元素,并将其作为Vue应用的模板。所以你的canvas, inputspan元素现在将由Vue.js在构建DOM时重新渲染。

在此过程中,您的原始画布文本-在初始化Vue应用程序之前设置的文本将丢失。

没有明确的方法来解决这个问题,除了使用directive,在我的jsFiddle的例子。Vue指令可以帮助您捕获DOM元素。

在我的例子中,我定义了一个名为insert-message的Vue指令,我在画布上使用v-insert-message。这允许我捕获DOM元素,然后执行其他步骤:getContextfillText。不需要id,或者不需要javascript代码到getElementById

还有一件事-你的画布太大了,因此你无法看到你的inputspan元素。他们也在你的例子中正常工作!

编辑:指令绑定示例

我刚刚找到了一种方法,使用指令,仍然写入动态的东西到画布。

查看更新后的jsFiddle: https://jsfiddle.net/mani04/r4mbh6nu/1/

下面是一个使用Vuejs 2的简单方法:

  1. 使用引用属性将画布添加到模板中:

    ref="myCanvas"

  2. 你可以使用

    在组件内的任何地方访问它

    var canvas = this.$refs.myCanvas

正如@Mani的答案中详述的那样,Vue删除并重新呈现Vue实例所调用的DOM元素。

如果你将你的画布更新抽象到一个Vue实例method中,你可以在mounted生命周期钩子上触发它(获得exampleContent的初始值),当exampleContent发生变化时,在watch中重新调用它。

var v = new Vue({
  el: '#app',
  data: {
    'exampleContent': 'This is TEXT'
  },
  methods: {
    updateCanvas: function (){
      var canvas = document.getElementById('canvas'),
          ctx = canvas.getContext('2d');
      ctx.clearRect(0,0,canvas.width,canvas.height);
      ctx.fillStyle = "black";
      ctx.font="20px Georgia";
      ctx.fillText(this.exampleContent,10,50);
    }
  },
  watch: {
    exampleContent: function(val, oldVal) {
      this.updateCanvas();
    }
  },
  mounted: function (){
    this.updateCanvas();
  }
});
canvas{
  background:red;
  width:800px;
  height:600px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js"></script>
<div id="app">
  <canvas id="canvas" width="800" height="600"></canvas>
  <input type="text" v-model="exampleContent" />
  <span>{{ exampleContent }}</span>
</div>

前面的SO代码片段是从OP的CodePen的分支版本创建的

这个问题已经被浏览了几次,自从我第一次问这个问题以来,我已经使用画布和Vue数千次了。这是我在实践中发现的最好的选择。

给画布一个ref,正如一些人提到的:

<div id="app">
  <canvas width="800" height="600" ref="nameOfCanvas"></canvas>
</div>

在挂载函数中设置画布的context:

mounted(){
  this.canvas = this.$refs.nameOfCanvas;
  this.ctx = this.canvas.getContext("2d");
  this.render();
}

没有必要将ctx放在data对象中,除非您想要监视它。

就是这样。从那里,可以创建render方法并在组件中引用数据…

render(){
  this.ctx.clearRect(0,0,this.canvas.width, this.canvas.height);
  this.ctx.fillRect(0, 0, 20, 20);
}

您还可以在此方法中设置一个requestAnimationFrame循环,如下所示:

render(){
  this.ctx.clearRect(0,0,this.canvas.width, this.canvas.height);
  this.ctx.fillRect(0, 0, 20, 20);
  requestAnimationFrame(this.render);
}

完整demo/starter:https://codepen.io/EightArmsHQ/pen/gOROpqQ