2016-10-21 4 views
13

Ich glaube nicht, dass dies ein Vue-basiertes Problem ist, aber ich laufe in Schwierigkeiten.Mit vue.js auf eine Leinwand zeichnen

Ich möchte auf die Leinwand eine Vue-Variable schreiben. Wenn ich Vue entferne, funktioniert mein ursprünglicher Code gut, aber wenn ich Vue hinzufüge, startet der Canvas tatsächlich nicht.

Hier ist mein Code

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); 
    } 
    } 
}); 

Wenn ich /* var v = new Vue({ ... die ersten Bit Werke Kommentar aus. Wenn ich den Wert exampleContent im Watcher protokolliere, der auch funktioniert. Aber etwas an der Leinwand funktioniert nicht.

Demo spielen: http://codepen.io/EightArmsHQ/pen/EgGbgR?editors=1010

Antwort

13

Bitte überprüfen Sie diese jsFiddle: https://jsfiddle.net/mani04/r4mbh6nu/

EDIT: mit dynamischem Text aktualisiert: https://jsfiddle.net/mani04/r4mbh6nu/1/

Im obigen Beispiel, ich bin in der Lage zu schreiben Sie in Canvas, und stellen Sie auch sicher, dass der input Text in span geht, wie Sie erwarten würden, alle mit Vue.js

zu Ihrem Beispiel zurückzukommen, das HTML ist:

<div id="app"> 
    <canvas id="canvas" width="800" height="600"></canvas> 
    <input type="text" v-model="exampleContent" /> 
    <span>{{ exampleContent }}</span> 
</div> 

Vue.js die Elemente innerhalb #app liest und hält sie als Vorlage für Ihre Vue App. Also Ihre canvas, input und span Elemente werden nun Vue.js neu gerendert werden, wenn sie die DOM-Konstrukte.

Während dieses Prozesses Ihre ursprüngliche Leinwand Text - diejenige, die Sie festlegen, bevor die Vue-Anwendung initiiert verloren.

Es gibt keine klare Lösung dieses Problems außer der Verwendung eines directive, wie in meinem JsFiddle-Beispiel. Eine Vue-Direktive hilft Ihnen, das DOM-Element zu erfassen.

In meinem Beispiel definiere ich eine Vue-Direktive namens insert-message, die ich auf Canvas als v-insert-message verwende. Dadurch kann ich das DOM-Element erfassen und dann die anderen Schritte ausführen: getContext und fillText. Es ist kein id erforderlich oder kein JavaScript-Code zu getElementById.

Noch eine Sache - Ihre Leinwand ist einfach zu groß und deshalb konnten Sie Ihre input und span Elemente nicht sehen. Sie arbeiteten normal in Ihrem Beispiel auch!

EDIT: Beispiel für eine Richtlinie Bindung

Ich fand nur einen Weg Richtlinien zu verwenden und dennoch dynamische Sachen in Leinwand zu schreiben.

die aktualisierte jsFiddle Check: https://jsfiddle.net/mani04/r4mbh6nu/1/

+0

Für weitere Informationen über Richtlinien, hier ist der Link zu docs - https://vuejs.org/guide/custom-directive.html – Mani

+0

Hallo Mani, vielen Dank für die Antwort. Wie könnte ich in Ihrem Beispiel die Anweisung "change/watcher" auslösen, um den Text auf die Arbeitsfläche zu legen? Egal, ich habe die Leinwand außerhalb der App (genau wie du gesagt hast) bewegt und das funktioniert super, also danke für die Hilfe. – Djave

+1

Wenn Sie nicht auf die 'directive' Route gehen wollten, könnten Sie Ihre canvas updates zu einer' Methode' abstrahieren, die sowohl auf der Vue-Instanz 'mounted' als auch auf Ihrer' watch' ausgelöst wird. http://codepen.io/itopizarro/pen/amPEEO?editors=1010 –

5

Wie in @Mani's Answer detailliert, entfernt Vue und wieder macht das DOM-Element, dass eine Vue Instanz auf aufgerufen wird.

Wenn Sie abstrakte Ihre Leinwand in eine Vue Instanz Aktualisierung method, können Sie es am mounted Lifecycle Haken auslösen können (den Anfangswert von exampleContent zu erhalten), die Wieder rufen Sie es in Ihren watch wenn exampleContent Änderungen.

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>

Die vorhergehende SO Code Snippet wurde von gegabelten Version von CodePen

+1

Ich habe gerade einen Weg gefunden, die Bindung mit Direktiven zu verwenden, und meine Antwort aktualisiert. Schau Dir an, wie es mit Direktiven funktioniert: https://jsfiddle.net/mani04/r4mbh6nu/1/ :-) – Mani

9

des OP erstellt Hier ist eine einfache Möglichkeit, die mit Vuejs arbeitet 2:

  1. die Leinwand hinzufügen Vorlage mit einem Referenzattribut:

    .

    ref="myCanvas"

  2. Sie können es dann in Ihrem Komponente zugreifen überall mit

    var Leinwand = $ this refs.myCanvas