Last active
July 29, 2023 01:44
-
-
Save JobLeonard/987731e86b473d42cd1885e70eed616a to your computer and use it in GitHub Desktop.
A react component that wraps and autosizes a canvas element
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, {PropTypes} from 'react'; | |
| import { debounce } from 'lodash'; | |
| // A simple helper component, wrapping retina logic for canvas and | |
| // auto-resizing the canvas to fill its parent container. | |
| // Expects a "painter" function that takes a "context" to draw on. | |
| // This will draw on the canvas whenever the component updates. | |
| export class Canvas extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| this.fitToZoomAndPixelRatio = this.fitToZoomAndPixelRatio.bind(this); | |
| this.draw = this.draw.bind(this); | |
| this.state = { | |
| width: 1, | |
| height: 1, | |
| resizing: true, | |
| }; | |
| const resize = () => { this.setState({ resizing: true }); }; | |
| this.setResize = debounce(resize, 200); | |
| } | |
| componentDidMount() { | |
| this.fitToZoomAndPixelRatio(); | |
| // Because the resize event can fire very often, we | |
| // add a debouncer to minimise pointless | |
| // resizing/redrawing of the canvas. | |
| window.addEventListener('resize', this.setResize); | |
| } | |
| componentWillUnmount() { | |
| window.removeEventListener('resize', this.setResize); | |
| } | |
| componentDidUpdate(prevProps, prevState) { | |
| // if resizing was not true both before the update, | |
| // but is now, then the canvas has been unmounted | |
| if (!prevState.resizing && this.state.resizing) { | |
| this.fitToZoomAndPixelRatio(); | |
| } | |
| if (!this.state.resizing && !prevProps.loop) { | |
| this.draw(); | |
| } | |
| } | |
| // Make sure we get a sharp canvas on Retina displays | |
| // as well as adjust the canvas on zoomed browsers | |
| // Does NOT scale; painter functions decide how to handle | |
| // window.devicePixelRatio on a case-by-case basis | |
| fitToZoomAndPixelRatio() { | |
| const ratio = window.devicePixelRatio || 1; | |
| const view = this.refs.view; | |
| const width = (view.clientWidth * ratio) | 0; | |
| const height = (view.clientHeight * ratio) | 0; | |
| this.setState({ width, height, ratio, resizing: false }); | |
| } | |
| // Relies on a ref to a DOM element, so only call | |
| // when canvas element has been rendered! | |
| draw() { | |
| let canvas = this.refs.canvas; | |
| const { width, height, ratio } = this.state; | |
| let context = canvas.getContext('2d'); | |
| // store width, height and ratio in context for paint functions | |
| context.width = width; | |
| context.height = height; | |
| context.pixelRatio = ratio; | |
| // should we clear the canvas every redraw? | |
| if (this.props.clear) { context.clearRect(0, 0, canvas.width, canvas.height); } | |
| this.props.paint(context); | |
| // is the provided paint function an animation? | |
| if (this.props.loop) { | |
| window.requestAnimationFrame(this.draw); | |
| } | |
| } | |
| render() { | |
| let canvas = null; | |
| if (!this.state.resizing) { | |
| canvas = (<canvas | |
| ref='canvas' | |
| width={this.state.width} | |
| height={this.state.height} | |
| style={{ | |
| width: '100%', | |
| height: '100%', | |
| }} /> | |
| ); | |
| } | |
| return ( | |
| <div | |
| className='view' | |
| ref='view' | |
| style={this.props.style}> | |
| {canvas} | |
| </div> | |
| ); | |
| } | |
| } | |
| Canvas.propTypes = { | |
| paint: PropTypes.func.isRequired, | |
| clear: PropTypes.bool, | |
| loop: PropTypes.bool, | |
| style: PropTypes.object, | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Will this work for drawing an image on the canvas? e.g. context.drawImage(base_image, 0, 0);
I don't understand why the prototype had to be extended with circle, testSize, etc. Does it need a drawImage?