2015-05-18 16 views
15

Próbuję renderować płótno za pomocą przycisku React. Grafika wewnątrz płótna może być również obsługiwana za pomocą metody Reakcje render.Renderowanie/zwracanie Canvas HTML5 w ReactJS

Obecny kod renderuje płótno z potrzebną grafiką. Ale szukam sposobu, aby zwrócił płótno z moją grafiką z metody render. Powodem, dla którego tego chcę, jest to, że chcę, żeby to działało w "React Way".

Idealnie chciałbym mój kod będzie coś takiego:

return(
    <div> 
    <canvas id='my_canvas_with_graphics_rendered_in_react'></canvas> 
    </div> 
); 

Po pierwsze, chcę wiedzieć, czy jest to w ogóle możliwe. Lub gdybym szukał alternatywy.

Może Im brakuje punktu React, jeśli tak jest, proszę dać mi znać. Mam nadzieję, że istnieje rozwiązanie, ponieważ z tego, co przeczytałem, React nazywa się V w MVC. Dlatego, jeśli to możliwe, byłoby to idealne rozwiązanie dla tego, co próbuję zrobić. Nie musiałbym się martwić o renderowanie w moim płótnie. Po prostu dostarczyłbym dane do komponentu, a React ponownie renderowałby zmiany.

Mam oznaczone w oświadczeniu return, gdzie uważam, poprawny kod powinien iść.

Kod HTML:

<div id='wrapper'> 
    <canvas id='canvas'></canvas> 
</div> 

Kod jsx:

var MyCanvas = React.createClass({ 
    render:function(){ 
     var line = this.props.lines; 
     var ctx = this.props.canvas.getContext("2d"); 

     ctx.strokeStyle = 'rgba(0,0,0,0.5)'; 
     ctx.beginPath(); 
     ctx.moveTo(line[0].x0, line[0].y0); 
     // .... more code ..... 
     ctx.stroke(); 

     return(
      <div> 
       /* ********************************* 
       ??? RETURN MY RENDERED CANVAS 
        WITH GRAPHIC LINES. ???? 
      This is where I want to return the canvas 
       ********************************* */ 
      </div> 
     ); 

    } 
}); 


var wrapper = document.getElementById('wrapper'); 
var canvas = document.getElementById('canvas'); 
var line1 = { x0: 0, y0: 10, x1: 20, y1: 30 }; 
var line2 = { x0: 40, y0: 50, x1: 60, y1: 70 }; 

var myCanvas = <MyCanvas canvas={ canvas } lines={ [line1, line2] } />; 
React.render(myCanvas, wrapper); 

nadzieję, że wyraziłem się jasno.

Odpowiedz

11

Zamiast canvas w swojej metodzie render, chcesz zrobić coś takiego:

var MyCanvas = React.createClass({ 
    componentDidMount: function() { 
    React.getDOMNode(this).appendChild(this.props.canvas); 
    }, 
    render: function() { 
    return <div />; 
    } 
}); 

Po prostu renderowanie pusty div i czekać na React zamontowanie tego. Po zamontowaniu dodajesz płótno do rzeczywistego węzła DOM utworzonego przez React.

Powodem tego jest, że metoda render zwraca tylko wirtualne węzły DOM, które zostaną przetworzone, a następnie przetłumaczy je na rzeczywiste węzły DOM. A ponieważ masz prawdziwy węzeł DOM, nie możesz go przekonwertować do wirtualnego węzła. Innym powodem jest to, że powinieneś zwrócić tylko węzły z render, które kontrolują. A ponieważ zarządzasz płótnem spoza kontroli Reakcji, powinieneś używać prawdziwego DOM-a.

+0

Pierwsze pytanie: czy to ma znaczenie, jeśli wzywam moje procedury rysowania grafiki w 'componentDidMount' lub' render'? – germ13

+0

Drugie pytanie: Czy zarządzanie płótnem z wnętrza React (jego tworzenie i tworzenie odniesień) byłoby bardziej React sposobem robienia rzeczy? Moim pomysłem jest uczynienie tego komponentu częścią większego zestawu komponentów React. – germ13

+1

Pierwsze pytanie: Tak, to ma znaczenie. 'render' może być wywołany dowolną liczbę razy podczas trwania instancji składnika, natomiast' componentDidMount' będzie wywoływane tylko raz. Pamiętaj, aby zaimplementować również 'componentWillUnmount' i tam przeprowadzić czyszczenie. –

16

Twoim jedynym błędem było to, że nie byłeś zainteresowany reakcją na ref. spróbuj tego:

class ConnectionChart extends React.Component { 

    componentDidMount() { 
     let canvas = ReactDOM.findDOMNode(this.refs.myCanvas); 
     let ctx = canvas.getContext('2d'); 

     ctx.fillStyle = 'rgb(200,0,0)'; 
     ctx.fillRect(10, 10, 55, 50); 
    } 

    render() { 
     return (
      <div> 
       <canvas ref="myCanvas" /> 
      </div> 
     ); 
    } 
} 

Mogłeś pozbyć stylu ES6 ale masz pomysł. Przyczyna, dla której można malować innymi metodami ^^

+3

Notatka z React [docs] (https://facebook.github.io/react /docs/more-about-refs.html#the-ref-string- attribute): _ "Chociaż odwołania łańcuchowe nie są przestarzałe, są uważane za starsze i prawdopodobnie będą przestarzałe w pewnym momencie w przyszłości. [Odesłania zwrotne] (https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback- attribute) są preferowane. "_ –

+1

Dobra odpowiedź, ale są uważane za starsze, teraz zobacz -> https: //facebook.github.io/react/docs/refs-and-the-dom.html#legacy-api-string-refs – timebandit

5

Kluczem jest nadpisanie właściwych metod cyklu życia React, aby wykonać rysunek. Metoda renderowania tworzy element DOM projektu canvas i można dodać wywołanie zwrotne refs, aby ustawić odwołanie do niego, ale należy poczekać, aż komponent zostanie podłączony do rysowania na obszarze roboczym.

Oto przykład, który używa odwołania zwrotnego, jak zalecają dokumenty React. Został zaprojektowany do renderowania statycznego (nie animowanego) komponentu graficznego opartego na rekwizytach i aktualizacjach po zmianie rekwizytów. Subskrybuje także zdarzenie zmiany rozmiaru okna, aby można było dynamicznie zmieniać rozmiar za pomocą CSS (na przykład width: 100%). This example is based on the code from this Gist.

export default class CanvasComponent extends React.Component { 

    constructor(props) { 
     super(props); 

     this._resizeHandler =() => { 
      /* Allows CSS to determine size of canvas */ 
      this.canvas.width = this.canvas.clientWidth; 
      this.canvas.height = this.canvas.clientHeight; 

      this.clearAndDraw(); 
     } 
    } 

    componentDidMount() { 
     window.addEventListener('resize', this._resizeHandler); 

     /* Allows CSS to determine size of canvas */ 
     this.canvas.width = this.canvas.clientWidth; 
     this.canvas.height = this.canvas.clientHeight; 

     this.clearAndDraw(); 
    } 

    componentWillUnmount() { 
     window.removeEventListener('resize', this._resizeHandler); 
    } 

    componentDidUpdate(prevProps, prevState) { 
     if (this.props.secondRect !== prevProps.secondRect) { 
      this.clearAndDraw(); 
     } 
    } 

    clearAndDraw() { 
     const ctx = this.canvas.getContext('2d'); 
     if (ctx) { 
      ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); 
      this.draw(ctx); 
     } 
    } 

    draw(ctx) { 
     ctx.fillStyle = 'rgb(200, 0, 0)'; 
     ctx.fillRect(10, 10, 50, 50); 

     if (this.props.secondRect) { 
      ctx.fillStyle = 'rgba(0, 0, 200, 0.5)'; 
      ctx.fillRect(30, 30, 50, 50); 
     } 
    } 

    render() { 
     return (
      <canvas ref={canvas => this.canvas = canvas} /> 
     ); 
    } 
} 

Można ustawić secondRect rekwizyt z elementu nadrzędnego lub ustawić go ze stanu zobaczyć aktualizację składnika, gdy aktualizacje prop. Możesz rozszerzyć ten pomysł, aby zbudować dowolny rodzaj renderowania kanwy opartej na danych, takiego jak wykres lub wykres.

+0

Edytowałem przykład, aby pokazać najlepszą praktykę sprawdzania, czy odpowiednie rekwizyty lub stan zostały zmienione w componentDidUpdate' przed ponownym rysowaniem płótna. Jeśli nie sprawdzisz, czy rekwizyty lub stan rzeczywiście się zmieniły, możesz przerysować płótno znacznie częściej niż to konieczne. – RedMatt