2015-05-18 21 views
15

Tôi đang cố gắng hiển thị canvas bằng React. Đồ họa bên trong khung cũng sẽ được xử lý bằng phương pháp Reacts render.Hiển thị/Trả lại Canvas HTML5 trong ReactJS

Mã hiện tại không hiển thị canvas bằng đồ họa tôi muốn. Nhưng tôi đang tìm cách để trả lại canvas với đồ họa của tôi từ trong phương thức render. Lý do tôi muốn điều này, là nguyên nhân tôi muốn điều này làm việc trong "React Way".

Lý tưởng nhất, tôi muốn mã của tôi là một cái gì đó như thế này:

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

Trước tiên, tôi muốn biết nếu điều này thậm chí còn có thể. Hoặc nếu tôi nên tìm một giải pháp thay thế.

Có thể Im thiếu điểm phản ứng, nếu đúng như vậy, hãy cho tôi biết. Tôi hy vọng có một giải pháp, bởi vì từ những gì tôi đã đọc, React chào hàng chính nó như là V trong MVC. Đó là lý do tại sao, nếu có thể, đây sẽ là một giải pháp hoàn hảo cho những gì tôi đang cố gắng làm. Tôi sẽ không phải lo lắng về việc dựng hình bên trong khung hình của mình. Tôi chỉ đơn giản cung cấp dữ liệu cho thành phần và React sẽ trả lại các thay đổi.

Tôi đã đánh dấu trong tuyên bố return nơi tôi tin rằng mã chính xác nên đi.

Các HTML mã:

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

Các jsx mã:

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

Hope tôi làm bản thân mình rõ ràng.

Trả lời

11

Thay vì sử dụng trong phương pháp canvasrender của bạn, bạn muốn làm điều gì đó như thế này:

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

Bạn chỉ cần làm cho một sản phẩm nào div và chờ Phản ứng để gắn kết đó. Khi được gắn kết, bạn thêm khung hình của bạn vào nút DOM thực tế mà React đã tạo.

Lý do cho điều này là phương thức render chỉ trả về các nút DOM ảo mà React sau đó chuyển thành các nút DOM thực. Và vì bạn có nút DOM thực, bạn không thể chuyển đổi nút đó thành nút ảo. Một lý do khác là bạn chỉ nên trả lại các nút từ render rằng các điều khiển React. Và vì bạn đang quản lý canvas từ bên ngoài điều khiển Reacts, bạn nên sử dụng apis DOM thực sự để quản lý nó.

+0

Câu hỏi đầu tiên: Có vấn đề gì nếu tôi gọi các thường trình vẽ đồ họa của tôi trong 'componentDidMount' hoặc' render'? – germ13

+0

Câu hỏi thứ hai: Sẽ quản lý canvas từ bên trong React (tạo ra và tham chiếu) của nó có nhiều hơn để phản ứng cách làm việc không? Ý tưởng của tôi là làm cho thành phần này trở thành một phần lớn hơn của các thành phần React. – germ13

+1

Câu hỏi đầu tiên: Có, nó quan trọng. 'render' có thể được gọi bất kỳ số lần nào trong suốt thời gian tồn tại của cá thể thành phần, trong khi' componentDidMount' sẽ chỉ được gọi một lần. Hãy chắc chắn để thực hiện 'componentWillUnmount' là tốt và làm sạch ở đó. –

16

Lỗi duy nhất của bạn là bạn không quen với các phản hồi phản hồi. thử điều này:

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

Bạn có thể đã thoát khỏi phong cách ES6 nhưng bạn sẽ có được ý tưởng. Vì lý do bạn có thể vẽ bằng các phương pháp khác quá ^^

+3

Ghi chú từ React [docs] (https://facebook.github.io/react /docs/more-about-refs.html#the-ref-string-attribute): _ "Mặc dù các tham chiếu chuỗi không được chấp nhận, chúng được coi là kế thừa và có thể sẽ không được chấp nhận tại một thời điểm nào đó trong tương lai. [Callback refs] (https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute) được ưu tiên. "_ –

+1

Câu trả lời hay nhưng chúng được coi là di sản bây giờ thấy -> https: //facebook.github.io/react/docs/refs-and-the-dom.html#legacy-api-string-refs – timebandit

5

Điều quan trọng là ghi đè đúng phương pháp vòng đời React để thực hiện thao tác vẽ. Phương thức render tạo ra phần tử DOM canvas và bạn có thể thêm một cuộc gọi lại refs để đặt tham chiếu đến nó, nhưng bạn phải chờ cho đến khi thành phần được gắn kết để vẽ trên canvas.

Dưới đây là ví dụ sử dụng ref refback làm tài liệu React đề xuất. Nó được thiết kế để hiển thị thành phần đồ họa tĩnh (không động) dựa trên các đạo cụ và cập nhật khi thay đổi đạo cụ. Nó cũng đăng ký với sự kiện thay đổi kích thước cửa sổ để nó có thể được kích thước động thông qua CSS (chẳng hạn như 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} /> 
     ); 
    } 
} 

Bạn có thể đặt secondRect prop từ thành phần cha mẹ hoặc thiết lập nó từ trạng thái để xem cập nhật phần khi các bản cập nhật prop. Bạn có thể mở rộng ý tưởng này để tạo bất kỳ loại kết xuất canvas theo hướng dữ liệu nào, chẳng hạn như biểu đồ hoặc biểu đồ.

+0

Tôi đã chỉnh sửa ví dụ này cũng cho thấy cách thực hành tốt nhất để kiểm tra xem các đạo cụ hoặc trạng thái có liên quan đã thay đổi trong ' componentDidUpdate' trước khi vẽ lại canvas. Nếu bạn không kiểm tra xem các đạo cụ hoặc tiểu bang có thực sự thay đổi hay không, bạn có thể sẽ vẽ lại canvas nhiều hơn mức cần thiết. – RedMatt