【问题标题】:Drawing onto a canvas with vue.js使用 vue.js 在画布上绘图
【发布时间】:2016-10-21 13:08:11
【问题描述】:

我认为这不一定是基于 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

【问题讨论】:

    标签: javascript canvas vue.js


    【解决方案1】:

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

    编辑:已更新为动态文本: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 应用程序 的模板。因此,您的 canvasinputspan 元素现在将由 Vue.js 在构造 DOM 时重新渲染。

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

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

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

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

    编辑:指令绑定示例

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

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

    【讨论】:

    • 有关指令的更多信息,这里是文档的链接 - vuejs.org/guide/custom-directive.html
    • 嗨,玛尼,非常感谢您的回答。在您的示例中,我如何触发更改/观察者的指令以将文本放在画布上?无论如何,我将画布移到了应用程序之外(正如您所说),效果很好,非常感谢您的帮助。
    • 如果您不想走directive 路线,您可以将您的画布更新抽象为method,该method 在vue 实例mountedwatch 上都被触发。 codepen.io/itopizarro/pen/amPEEO?editors=1010
    • @ItoPizarro 感谢您指出此方法。我没有想到它,因为我通常不喜欢在 Vue 模板中使用 ids,因为它限制了组件的重用。尽管如此,您的解决方案是@Djave 目前想要的。你能把它作为一个单独的答案,所以他可能想接受吗? :-)
    • @Mani 可以,尽管我同意directive 是更具可扩展性的方法,并分享您对可重用性的看法。
    【解决方案2】:

    这是一种适用于 Vuejs 2 的简单方法:

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

      ref="myCanvas"

    2. 然后您可以在组件内的任何位置使用

      var canvas = this.$refs.myCanvas

    【讨论】:

    • 为了进一步说明,这只是意味着将ref="myCanvas" 添加到模板中的&lt;canvas&gt; HTML 标记中,如下所示:&lt;canvas ref="myCanvas"&gt;
    【解决方案3】:

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

    如果您将画布更新抽象为 Vue 实例 method,您可以在 mounted 生命周期挂钩处触发它(以获取 exampleContent 的初始值),然后在您的 watch 中重新调用它当exampleContent 改变时。

    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 的分叉版本创建的

    【讨论】:

    • 我刚刚找到了一种使用指令绑定的方法,并更新了我的答案。查看它如何与指令一起使用:jsfiddle.net/mani04/r4mbh6nu/1 :-)
    【解决方案4】:

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

    如少数人所说,给画布一个ref

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

    在mounted函数中设置画布的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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多