用 vue.js 在 canvas 上绘图

Drawing onto a canvas with vue.js

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

我想向 canvas 写入一个 Vue 变量。如果我删除 vue,我的初始代码可以正常工作,但是如果我添加 Vue,canvas 实际上不会启动。

这是我的代码

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 的值,那也可以。但是关于 canvas 的某些东西不起作用。

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

请检查这个 jsFiddle:https://jsfiddle.net/mani04/r4mbh6nu/

编辑: 使用动态文本更新:https://jsfiddle.net/mani04/r4mbh6nu/1/

在上面的示例中,我可以写入 canvas,并确保 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 应用程序 的模板。因此,您的 canvasinputspan 元素现在将由 Vue.js 在构建 DOM.

时重新呈现

在此过程中,您的原始 canvas 文本 - 您在启动 Vue 应用程序之前设置的文本会丢失。

除了在我的 jsFiddle 示例中使用 directive 之外,没有明确的方法可以解决此问题。 Vue 指令可帮助您捕获 DOM 元素。

在我的示例中,我定义了一个名为 insert-message 的 Vue 指令,我在 canvas 上将其用作 v-insert-message。这允许我捕获 DOM 元素,然后执行其他步骤:getContextfillText。不需要 id,或者 getElementById.

没有 javascript 代码

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

编辑:指令绑定示例

我刚刚找到了一种使用指令的方法,并且仍然将动态内容写入 canvas。

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

中所述,Vue 删除并重新呈现调用 Vue 实例的 DOM 元素。

如果您将 canvas 更新抽象为 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

这里有一个使用 Vuejs 2 的简单方法:

  1. 将 canvas 添加到具有引用属性的模板:

    ref="myCanvas"

  2. 然后您可以使用

    在组件内的任何位置访问它

    var canvas = this.$refs.myCanvas

这个问题已经被看过几次了,自从我第一次问这个问题以来,我已经使用 canvases 和 Vue 数千次了。这是我在实践中找到的最佳选择。

给canvas一个ref,有几个人提到过:

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

在挂载函数中设置canvas的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);
}

完整演示/入门: https://codepen.io/EightArmsHQ/pen/gOROpqQ