Last active
July 29, 2023 01:44
-
-
Save JobLeonard/987731e86b473d42cd1885e70eed616a to your computer and use it in GitHub Desktop.
Revisions
-
JobLeonard revised this gist
Sep 7, 2016 . 1 changed file with 10 additions and 9 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -15,14 +15,15 @@ class CanvasEnhancer extends React.Component { this.draw = this.draw.bind(this); // Attach helper functions to context prototype let prototype = CanvasRenderingContext2D.prototype; if (!prototype.circle) { prototype.circle = function (x, y, radius) { this.moveTo(x + radius, y); this.arc(x, y, radius, 0, 2 * Math.PI); }; } if (!prototype.textSize) { prototype.textSize = function (size = 10) { // will return an array with [ size, font ] as strings const fontArgs = this.font.split(' '); const font = fontArgs[fontArgs.length - 1]; @@ -36,19 +37,19 @@ class CanvasEnhancer extends React.Component { } }; } if (!prototype.textStyle) { prototype.textStyle = function (fill = 'black', stroke = 'white', lineWidth = 2) { this.fillStyle = fill; this.strokeStyle = stroke; this.lineWidth = lineWidth; }; } if (!prototype.drawText) { prototype.drawText = function (text, x, y) { this.strokeText(text, x, y); this.fillText(text, x, y); }; } } // Make sure we get a sharp canvas on Retina displays -
JobLeonard revised this gist
Sep 7, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -97,7 +97,7 @@ class CanvasEnhancer extends React.Component { // and inconsistent across browsers. To make it dependent on // the layout of the parent container, we only render it after // mounting, after CSS layouting is done. const canvas = this.state ? ( <canvas ref='canvas' width={this.state.width} -
JobLeonard revised this gist
Sep 7, 2016 . 1 changed file with 119 additions and 49 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -6,56 +6,56 @@ import { debounce } from 'lodash'; // To determine size/layout, we just use CSS on the div containing // the Canvas component (we're using this with flexbox, for example). // Expects a "paint" function that takes a "context" to draw on // Whenever this component updates it will call this paint function // to draw on the canvas. For convenience, pixel dimensions are stored // in context.width, context.height and contex.pixelRatio. class CanvasEnhancer extends React.Component { constructor(props) { super(props); this.draw = this.draw.bind(this); // Attach helper functions to context prototype if (CanvasRenderingContext2D.prototype.circle === undefined) { CanvasRenderingContext2D.prototype.circle = function (x, y, radius) { this.moveTo(x + radius, y); this.arc(x, y, radius, 0, 2 * Math.PI); }; } if (CanvasRenderingContext2D.prototype.textSize === undefined) { CanvasRenderingContext2D.prototype.textSize = function (size = 10) { // will return an array with [ size, font ] as strings const fontArgs = this.font.split(' '); const font = fontArgs[fontArgs.length - 1]; switch (typeof size) { case 'number': this.font = size + 'px ' + font; break; case 'string': this.font = size + font; break; } }; } if (CanvasRenderingContext2D.prototype.textStyle === undefined) { CanvasRenderingContext2D.prototype.textStyle = function (fill = 'black', stroke = 'white', lineWidth = 2) { this.fillStyle = fill; this.strokeStyle = stroke; this.lineWidth = lineWidth; }; } if (CanvasRenderingContext2D.prototype.drawText === undefined) { CanvasRenderingContext2D.prototype.drawText = function (text, x, y) { this.strokeText(text, x, y); this.fillText(text, x, y); }; } } // 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 componentDidMount() { const view = this.refs.view; const ratio = window.devicePixelRatio || 1; const width = (view.clientWidth * ratio) | 0; @@ -64,11 +64,18 @@ export class Canvas extends React.Component { this.setState({ width, height, ratio, resizing }); } componentDidUpdate(prevProps) { if (!prevProps.loop) { this.draw(); } } // Relies on a ref to a DOM element, so only call // when canvas element has been rendered! draw() { if (this.state) { const { width, height, ratio } = this.state; const canvas = this.refs.canvas; let context = canvas.getContext('2d'); // store width, height and ratio in context for paint functions @@ -78,8 +85,8 @@ export class Canvas extends React.Component { // 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? (not entirely sure about this API) if (this.props.loop) { window.requestAnimationFrame(this.draw); } @@ -88,33 +95,96 @@ export class Canvas extends React.Component { render() { // The way canvas interacts with CSS layouting is a bit buggy // and inconsistent across browsers. To make it dependent on // the layout of the parent container, we only render it after // mounting, after CSS layouting is done. let canvas = this.state ? ( <canvas ref='canvas' width={this.state.width} height={this.state.height} style={{ width: '100%', height: '100%', }} /> ) : null; return ( <div ref='view' className={this.props.className ? this.props.className : 'view'} style={this.props.style}> {canvas} </div> ); } } CanvasEnhancer.propTypes = { paint: PropTypes.func.isRequired, clear: PropTypes.bool, loop: PropTypes.bool, className: PropTypes.string, style: PropTypes.object, }; // This pattern turns out to be generic enough to // warrant its own component export class RemountOnResize extends React.Component { constructor(props) { super(props); this.state = { resizing: true }; const resize = () => { this.setState({ resizing: true }); }; // Because the resize event can fire very often, we // add a debouncer to minimise pointless // (unmount, resize, remount)-ing of the child nodes. this.setResize = debounce(resize, 500); } componentDidMount() { window.addEventListener('resize', this.setResize); this.setState({ resizing: false }); } componentWillUnmount() { window.removeEventListener('resize', this.setResize); } componentDidUpdate(prevProps, prevState) { if (!prevState.resizing && this.state.resizing) { this.setState({ resizing: false }); } } render() { return this.state.resizing ? null : this.props.children; } } RemountOnResize.propTypes = { className: PropTypes.string, style: PropTypes.object, children: PropTypes.node, }; export const Canvas = function (props) { return ( <RemountOnResize /* Since canvas interferes with CSS layouting, we unmount and remount it on resize events */ > <CanvasEnhancer paint={props.paint} clear={props.clear} loop={props.loop} className={props.className} style={props.style} /> </RemountOnResize> ); }; Canvas.propTypes = { paint: PropTypes.func.isRequired, clear: PropTypes.bool, -
JobLeonard revised this gist
Sep 6, 2016 . 1 changed file with 6 additions and 6 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -3,7 +3,7 @@ import { debounce } from 'lodash'; // A simple helper component, wrapping retina logic for canvas and // auto-resizing the canvas to fill its parent container. // To determine size/layout, we just use CSS on the div containing // the Canvas component (we're using this with flexbox, for example). // Expects a "paint" function that takes a "context" to draw on // Whenever this component updates it will call this paint function @@ -86,11 +86,11 @@ export class Canvas extends React.Component { } render() { // The way canvas interacts with CSS layouting is a bit buggy // and inconsistent across browsers. To make it dependent on // the layout of the parent container, we completely remove // the node when resizing. After calculations are done, // we mount it again at the proper dimensions. let canvas = null; if (!this.state.resizing) { canvas = (<canvas -
JobLeonard revised this gist
Sep 6, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ import { debounce } from 'lodash'; // A simple helper component, wrapping retina logic for canvas and // auto-resizing the canvas to fill its parent container. // So determine size/layout, just use CSS on the div containing // the Canvas component (we're using this with flexbox, for example). // Expects a "paint" function that takes a "context" to draw on // Whenever this component updates it will call this paint function // to draw on the canvas. For convenience, pixel dimensions are stored -
JobLeonard revised this gist
Sep 6, 2016 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -106,8 +106,8 @@ export class Canvas extends React.Component { return ( <div ref='view' className={this.props.className} style={this.props.style}> {canvas} </div> @@ -119,5 +119,6 @@ Canvas.propTypes = { paint: PropTypes.func.isRequired, clear: PropTypes.bool, loop: PropTypes.bool, className: PropTypes.string, style: PropTypes.object, }; -
JobLeonard revised this gist
Sep 6, 2016 . No changes.There are no files selected for viewing
-
JobLeonard revised this gist
Sep 6, 2016 . 1 changed file with 31 additions and 18 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -3,8 +3,12 @@ import { debounce } from 'lodash'; // A simple helper component, wrapping retina logic for canvas and // auto-resizing the canvas to fill its parent container. // So determine size/layout, just use CSS on the div containing // the Canvas component (we're using this with flexbox, fox example). // Expects a "paint" function that takes a "context" to draw on // Whenever this component updates it will call this paint function // to draw on the canvas. For convenience, pixel dimensions are stored // in context.width, context.height and contex.pixelRatio. export class Canvas extends React.Component { constructor(props) { super(props); @@ -15,18 +19,19 @@ export class Canvas extends React.Component { this.state = { width: 1, height: 1, ratio: window.devicePixelRatio || 1, resizing: true, }; const resize = () => { this.setState({ resizing: true }); }; // Because the resize event can fire very often, we // add a debouncer to minimise pointless // (unmount, resize, remount)-ing of the canvas. this.setResize = debounce(resize, 200); } componentDidMount() { this.fitToZoomAndPixelRatio(); window.addEventListener('resize', this.setResize); } @@ -51,33 +56,41 @@ export class Canvas extends React.Component { // Does NOT scale; painter functions decide how to handle // window.devicePixelRatio on a case-by-case basis fitToZoomAndPixelRatio() { const view = this.refs.view; const ratio = window.devicePixelRatio || 1; const width = (view.clientWidth * ratio) | 0; const height = (view.clientHeight * ratio) | 0; const resizing = false; this.setState({ width, height, ratio, resizing }); } // Relies on a ref to a DOM element, so only call // when canvas element has been rendered! draw() { const { width, height, ratio, resizing } = this.state; if (!resizing) { const canvas = this.refs.canvas; 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? (not entirely sure about this API) } if (this.props.loop) { window.requestAnimationFrame(this.draw); } } render() { // Canvas layouting is a bit buggy and inconsistent // across browsers. So, to make it dependent on the // layout of the parent container, we must completely // remove the node when resizing. After calculations // are done, we mount it again at the proper dimensions let canvas = null; if (!this.state.resizing) { canvas = (<canvas -
JobLeonard revised this gist
Sep 6, 2016 . No changes.There are no files selected for viewing
-
JobLeonard revised this gist
Sep 6, 2016 . 1 changed file with 78 additions and 49 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,8 @@ 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 { @@ -11,71 +11,100 @@ export class Canvas extends React.Component { 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, }; -
JobLeonard revised this gist
Aug 14, 2016 . 1 changed file with 4 additions and 1 deletion.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -66,7 +66,10 @@ export class Canvas extends React.Component { }}> <canvas ref='canvas' style={{ display: 'block', width: '100%', height: '100%' }} /> </div> ); -
JobLeonard revised this gist
Aug 14, 2016 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,5 @@ import React, {PropTypes} from 'react'; import { debounce } from 'lodash'; // A simple helper component, wrapping retina logic for canvas. @@ -49,7 +49,7 @@ export class Canvas extends React.Component { // Because the resize event can fire very often, we // add a debouncer to minimise pointless // resizing/redrawing of the canvas. window.addEventListener("resize", debounce(this.draw, 200)); } componentDidUpdate() { @@ -75,4 +75,4 @@ export class Canvas extends React.Component { Canvas.propTypes = { paint: PropTypes.func.isRequired, }; -
JobLeonard revised this gist
Aug 14, 2016 . 1 changed file with 20 additions and 11 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,6 @@ import React, {PropTypes} from 'react'; import * as _ from 'lodash'; // A simple helper component, wrapping retina logic for canvas. // Expects a "painter" function that takes a "context" to draw on. @@ -16,16 +18,20 @@ export class Canvas extends React.Component { fitToZoomAndPixelRatio() { let el = this.refs.canvas; if (el) { const ratio = window.devicePixelRatio || 1; const width = (el.parentNode.clientWidth * ratio) | 0; const height = (el.parentNode.clientHeight * ratio) | 0; if (width !== el.width || height !== el.height) { el.width = width; el.height = height; let context = el.getContext('2d'); context.mozImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; context.scale(ratio, ratio); context.clearRect(0, 0, el.width, el.height); } } } @@ -40,7 +46,10 @@ export class Canvas extends React.Component { componentDidMount() { this.draw(); // Because the resize event can fire very often, we // add a debouncer to minimise pointless // resizing/redrawing of the canvas. window.addEventListener("resize", _.debounce(this.draw, 200)); } componentDidUpdate() { @@ -66,4 +75,4 @@ export class Canvas extends React.Component { Canvas.propTypes = { paint: PropTypes.func.isRequired, }; -
JobLeonard created this gist
Aug 9, 2016 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,69 @@ import React, {PropTypes} from 'react'; // A simple helper component, wrapping retina logic for canvas. // 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); } // Make sure we get a sharp canvas on Retina displays // as well as adjust the canvas on zoomed browsers fitToZoomAndPixelRatio() { let el = this.refs.canvas; if (el) { let context = el.getContext('2d'); const ratio = window.devicePixelRatio || 1; el.width = el.parentNode.clientWidth * ratio; el.height = el.parentNode.clientHeight * ratio; context.mozImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; context.scale(ratio, ratio); context.clearRect(0, 0, el.width, el.height); } } draw() { let el = this.refs.canvas; if (el) { this.fitToZoomAndPixelRatio(); let context = el.getContext('2d'); this.props.paint(context, el.clientWidth, el.clientHeight); } } componentDidMount() { this.draw(); window.addEventListener("resize", this.draw); } componentDidUpdate() { this.draw(); } render() { return ( <div style={{ flex: '1 1 auto', margin: 0, padding: 0, border: 0, }}> <canvas ref='canvas' style={{ display: 'block', width: '100%', height: '100%' }} /> </div> ); } } Canvas.propTypes = { paint: PropTypes.func.isRequired, };