Javascript 在 ReactJS 中渲染/返回 HTML5 Canvas
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/30296341/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rendering / Returning HTML5 Canvas in ReactJS
提问by germ13
I am trying to render a canvas using React.  The graphics inside the canvas would also be handled by Reacts rendermethod.
我正在尝试使用 React 渲染画布。画布内的图形也将由 Reactsrender方法处理。
The current code does render a canvas with the graphics I want.  But I am looking for a way to return a canvas with my graphicsfrom within the rendermethod.  The reason I want this, is cause I want this to work in "React Way".  
当前代码确实使用我想要的图形渲染画布。但是我正在寻找一种方法来从方法中返回带有我的图形的画布render。我想要这个的原因是因为我希望它以“反应方式”工作。  
Ideally, I would want my code to be something like this:
理想情况下,我希望我的代码是这样的:
return(
  <div>
    <canvas id='my_canvas_with_graphics_rendered_in_react'></canvas>
  </div>
);
First, I want to know if this is even possible. Or if I should look for an alternative.
首先,我想知道这是否可能。或者,如果我应该寻找替代方案。
Maybe Im missing the point of React, if that is the case please let me know.  I do hope there is a solution, because from what I've read React touts itself as the Vin MVC.  That is why, if possible, this would be a perfect solution for what I'm trying to do.  I would not have to worry about the rendering inside my canvas.  I would simply supply the data to the component and React would re-render the changes.
也许我错过了 React 的重点,如果是这样,请告诉我。我确实希望有一个解决方案,因为从我读过的内容来看,React 将自己吹捧为Vin MVC。这就是为什么,如果可能的话,这将是我正在尝试做的事情的完美解决方案。我不必担心画布内的渲染。我会简单地向组件提供数据,React 会重新呈现更改。
I have marked of in the returnstatement where I believe the correct code should go.
我在return声明中标记了我认为正确的代码应该去的地方。
The HTMLcode:
该HTML代码:
<div id='wrapper'>
    <canvas id='canvas'></canvas>
</div>
The jsxcode:
该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);
Hope I made myself clear.
希望我说清楚了。
回答by Aligertor
Your only error was that you were not familar with react refs. try this:
您唯一的错误是您不熟悉 react refs。尝试这个:
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>
        );
    }
}
You might have get rid of the ES6 style but you get the idea. Of cause u can paint in other methods too ^^
您可能已经摆脱了 ES6 风格,但您明白了。因为你也可以用其他方法绘画^^
回答by Anders Ekdahl
Instead of using the canvasin your rendermethod, you'd do something like this:
而不是canvas在你的render方法中使用,你会做这样的事情:
var MyCanvas = React.createClass({
  componentDidMount: function () {
    React.getDOMNode(this).appendChild(this.props.canvas);
  },
  render: function() {
    return <div />;
  }
});
You just render an empty divand wait for React to mount that. When mounted, you append your canvas to the actual DOM node that React created.
你只需渲染一个空的div并等待 React 安装它。挂载后,您将画布附加到 React 创建的实际 DOM 节点。
The reason for this is that the rendermethod only returns virtual DOM nodes that React then translates to real DOM nodes. And since you have a real DOM node, you can't convert that to a virtual node. Another reason for is that you should only return nodes from renderthat React controls. And since you're managing the canvas from outside of Reacts control, you should use real DOM apis do manage it.
这样做的原因是该render方法只返回虚拟 DOM 节点,然后 React 将其转换为真实的 DOM 节点。并且由于您有一个真实的 DOM 节点,因此您无法将其转换为虚拟节点。另一个原因是你应该只从renderReact 控件返回节点。并且由于您是从 Reacts 控制之外管理画布,您应该使用真正的 DOM api 来管理它。
回答by RedMatt
The key is overriding the right React lifecycle methods to do the drawing.  The render method creates the canvas DOM element and you can add a refscallback to set a reference to it, but you have to wait until the component is mounted to draw on the canvas.
关键是覆盖正确的 React 生命周期方法来进行绘图。render 方法创建画布 DOM 元素,您可以添加一个refs回调来设置对它的引用,但您必须等到组件安装后才能在画布上绘制。
Here's an example that uses a callback ref as the React docs recommend.  It is designed to render a static (non-animated) graphic component based on props and updates when props change.  It also subscribes to the window resize event so that it can be dynamically sized via CSS (such as width: 100%).  This example is based on the code from this Gist.
这是一个使用 React 文档推荐的回调引用的示例。它旨在根据道具和道具更改时的更新呈现静态(非动画)图形组件。它还订阅窗口调整大小事件,以便可以通过 CSS(例如width: 100%)动态调整大小。  此示例基于此 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} />
        );
    }
}
You can set the secondRectprop from the parent component or set it from state to see the component update when the prop updates.  You can extend this idea to build any kind of data-driven canvas rendering, such as a chart or graph.
您可以secondRect从父组件设置prop 或从 state 设置它以在 prop 更新时查看组件更新。您可以扩展此想法以构建任何类型的数据驱动画布渲染,例如图表或图形。
回答by matthew
I thought I would update this answer using the React.createRef()because the this.refsis now legacy. Refs and the DOM
我想我会使用 更新这个答案,React.createRef()因为this.refs现在是遗留的。Refs 和 DOM
import React from "react";
import * as PropTypes from "prop-types";
class Canvas extends React.Component {
    constructor() {
        super()
        this.canvas = React.createRef()
    }
    componentDidMount() {
        const ctx = this.canvas.current.getContext('2d')
        this.props.draw(ctx);
    }
    render() {
        return <canvas ref={this.canvas} />;        
    }
}
Canvas.propTypes = {
  draw: PropTypes.func.isRequired
};
export default Canvas
You would use this Canvasby supplying the draw context.
您可以Canvas通过提供绘制上下文来使用它。
<Canvas  
  draw={ctx => {
    ctx.beginPath();
    ctx.arc(95, 50, 40, 0, 2 * Math.PI);
    ctx.closePath();
    ctx.stroke();
    }
  }
/>

