Рисуем на холсте с помощью vue.js

Я не думаю, что это обязательно проблема с 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

Ответ 1

Пожалуйста, проверьте этот 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, input и span теперь будут повторно отображены Vue.js, когда он построит DOM.

Во время этого процесса ваш исходный текст на холсте - тот, который вы установили перед запуском приложения Vue, потеряется.

Нет четкого способа решить эту проблему, кроме использования directive, как в моем примере jsFiddle. Директива Vue помогает вам захватить элемент DOM.

В моем примере я определяю директиву Vue, называемую insert-message, которую я использую на холсте как v-insert-message. Это позволяет мне захватить элемент DOM, а затем выполнить другие шаги: getContext и fillText. Существует не id, либо код javascript для getElementById.

Еще одно: ваш холст слишком велик, и поэтому вы не смогли увидеть ваши элементы input и span. Они тоже нормально работали в вашем примере!

EDIT: пример для привязок директив

Я только нашел способ использовать директивы и все еще писать динамические материалы в холст.

Проверьте обновленный jsFiddle: https://jsfiddle.net/mani04/r4mbh6nu/1/

Ответ 2

Вот простой способ, который работает с Vuejs 2:

  • Добавьте шаблон canvas к вам со ссылочным атрибутом:

    ref="myCanvas"

  • Затем вы можете получить доступ к нему в любом месте вашего компонента, используя

    var canvas = this. $refs.myCanvas

Ответ 3

Как описано в @Mani Отвечать, Vue удаляет и повторно отображает элемент DOM, к которому вызывается экземпляр Vue.

Если вы аннулируете обновление своего холста в экземпляр 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>