Working with ReactJS and HTML5 Canvas - javascript

I have a root component that renders <Particle /> components and <Particle /> component render function is:
render: function(){
var data = this.props.data,
canvas = document.createElement('canvas'),
context;
canvas.style.position = 'absolute';
canvas.style.top = data.y + 'px';
canvas.style.left = data.x + 'px';
context = canvas.getContext('2d');
context.drawImage(data.img, data.x, data.y, data.tileSize, data.tileSize, 0, 0, data.tileSize, data.tileSize);
return canvas;
}
And this returns the following error:
Uncaught Error: Invariant Violation: Particle.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.
I had a look at Flipboard's react-canvas but I couldn't find any good examples similar to my situation.
So any help would be much appreciated.

You should just return a single canvas element with a reference to it. This element is not the same as a DOM node; React transforms it into a React Component using JSX:
render: function() {
return <canvas ref="canvas" />
}
Then modify it inside a lifecycle method:
componentWillReceiveProps: function() {
var canvas = React.findDOMNode(this.refs.canvas);
canvas.style.position = 'absolute';
// etc ...
}
You could also set some inline style attributes inside the render:
render: function() {
var styles = {
position: 'absolute',
top: this.props.data.y,
left: this.props.data.x
}
return <canvas ref="canvas" style={styles} />
}
...but the context/drawimage would be best to put in a lifecycle method since you need access to the dom node.

Related

scale html canvas (html / reactjs)

I am trying to scale a canvas:
class DungeonMap extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const canvas = this.refs.canvas;
const ctx = canvas.getContext('2d');
ctx.scale(2, 2)
this.setState({})
}
render() {
console.log("render")
return (
<div className="DungeonMap">
<canvas ref="canvas" style={canvasStyle}/>
</div>
);
}
}
The code compiles but the canvas scaling is not applied, i've tried different values for the scaling.
As you can see I also tried putting this.setState({}) to force a re-render but the canvas is still not scaled.
I am pretty sure that componentDidMount() is the right method to use because i have to make sure the scaling is applied after the HTML is loaded: Cannot read property 'getContext' of null, using canvas
How can i apply the scaling for the canvas?
Try setting a width and height attribute on the <canvas> element:
class DungeonMap extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const canvas = this.refs.canvas;
// to scale the <canvas> element itself
canvas.width = 500;
canvas.height = 500;
// to scale the drawings, use the context
const ctx = canvas.getContext('2d');
ctx.scale(2, 2);
}
render() {
console.log("render")
return (
<div className="DungeonMap">
<canvas ref="canvas" style={canvasStyle}/>
</div>
);
}
};
By the way: I'm not really sure, but shouldn't that <canvas> element in your JSX have a closing tag? Or does JSX handle that different? Not using JSX that much myself.
try to set a property canvas in a state of component and in the componentDidMount makes setState({width:600,heigth:500})
class DungeonMap extends Component {
constructor(props) {
super(props);
this.state={
width:200,
height:300
}
}
componentDidMount() {
const canvas = this.refs.canvas;
// to scale the <canvas> element itself
canvas.width = this.state.widht;
canvas.height = this.state.height;
// to scale the drawings, use the context
const ctx = canvas.getContext('2d');
ctx.scale(2, 2);
this.setState({widht:1000,height:800})
}
render() {
console.log("render")
return (
<div className="DungeonMap">
<canvas ref="canvas" style={canvasStyle}/>
</div>
);
}
};
this must found

Polymer Element Error by displaying Image inside FabricJS Canvas

I'm trying to display an Image on the FabricJS Canvas inside my Polymer Element. By doing this, this error appears:
Uncaught TypeError: Cannot read property '_set' of undefined
at klass._onObjectAdded (fabric.js:6964)
at klass.add (fabric.js:260)
at HTMLElement.ready (convert-section.html:97)
at HTMLElement._enableProperties (property-accessors.html:531)
at HTMLElement.connectedCallback (element-mixin.html:630)
at HTMLElement._attachDom (element-mixin.html:690)
at HTMLElement._readyClients (element-mixin.html:663)
at HTMLElement._flushClients (property-effects.html:1518)
at HTMLElement._propertiesChanged (property-effects.html:1644)
at HTMLElement._flushProperties (property-accessors.html:549)
Is it possible to display an Image on a FabricJs canvas inside my Polymer Element? Or did I made an misstake? I'm new to Polymer and I don't have much experience.
I can display shapes like triangles etc.
Here is my Element:
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="my-paper-element-styles.html">
<dom-module id="convert-section">
<template>
<style include="my-paper-element-styles">
</style>
<canvas id="convertCanvas" ></canvas>
</template>
<script>
class ConvertSection extends Polymer.Element {
static get is() {
return 'convert-section';
}
static get properties() {
return {
imageLocationPath: {
type: String,
notify: true
}
};
}
disconnectedCallback() {
super.disconnectedCallback();
this.imageLocationPath = "";
}
ready() {
super.ready();
this.canvas = this.__canvas = new fabric.Canvas(this.$.convertCanvas);
var height = window.innerHeight - 48;
var width = window.innerWidth - 300;
this.canvas.setHeight(height);
this.canvas.setWidth(width);
var image = fabric.Image.fromURL('img/viper-board.png');
this.canvas.add(image);
}
}
window.customElements.define(ConvertSection.is, ConvertSection);
</script>
<script src="../../node_modules/fabric/dist/fabric.js"></script>
</dom-module>
ready() {
super.ready();
var self = this;
self.canvas = self.__canvas = new fabric.Canvas(self.$.convertCanvas);
var height = window.innerHeight - 48;
var width = window.innerWidth - 300;
self.canvas.setHeight(height);
self.canvas.setWidth(width);
fabric.Image.fromURL('img/viper-board.png',function(oImg){
oImg.set({
left:10,
top:10
});
self.canvas.add(oImg);
});
}
As you can see fromURL doesn't return anything. You need to pass source url and a callback function and add that image inside callback function.

React-Three-Renderer refs not current in componentDidUpdate (MVCE included)

I'm using react-three-renderer (npm, github) for building a scene with three.js.
I'm having a problem that I've boiled down to an MVCE. Refs aren't updating in the sequence I expect them to. First, here's the main code to look at:
var React = require('react');
var React3 = require('react-three-renderer');
var THREE = require('three');
var ReactDOM = require('react-dom');
class Simple extends React.Component {
constructor(props, context) {
super(props, context);
// construct the position vector here, because if we use 'new' within render,
// React will think that things have changed when they have not.
this.cameraPosition = new THREE.Vector3(0, 0, 5);
this.state = {
shape: 'box'
};
this.toggleShape = this.toggleShape.bind(this);
}
toggleShape() {
if(this.state.shape === 'box') {
this.setState({ shape: 'circle' });
} else {
this.setState({ shape: 'box' });
}
}
renderShape() {
if(this.state.shape === 'box') {
return <mesh>
<boxGeometry
width={1}
height={1}
depth={1}
name='box'
ref={
(shape) => {
this.shape = shape;
console.log('box ref ' + shape);
}
}
/>
<meshBasicMaterial
color={0x00ff00}
/>
</mesh>;
} else {
return <mesh>
<circleGeometry
radius={2}
segments={50}
name='circle'
ref={
(shape) => {
this.shape = shape;
console.log('circle ref ' + shape);
}
}
/>
<meshBasicMaterial
color={0x0000ff}
/>
</mesh>
}
}
componentDidUpdate() {
console.log('componentDidUpdate: the active shape is ' + this.shape.name);
}
render() {
const width = window.innerWidth; // canvas width
const height = window.innerHeight; // canvas height
var position = new THREE.Vector3(0, 0, 10);
var scale = new THREE.Vector3(100,50,1);
var shape = this.renderShape();
return (<div>
<button onClick={this.toggleShape}>Toggle Shape</button>
<React3
mainCamera="camera"
width={width}
height={height}
onAnimate={this._onAnimate}>
<scene>
<perspectiveCamera
name="camera"
fov={75}
aspect={width / height}
near={0.1}
far={1000}
position={this.cameraPosition}/>
{shape}
</scene>
</React3>
</div>);
}
}
ReactDOM.render(<Simple/>, document.querySelector('.root-anchor'));
This renders a basic scene with a green box, a fork of the example on react-three-renderer's github landing page. The button on the top left toggles the shape in the scene to be a blue circle, and if clicked again, back to the green box. I'm doing some logging in the ref callbacks and in componentDidUpdate. Here's where the core of the problem I'm encountering occurs. After clicking the toggle button for the first time, I expect the ref for the shape to be pointing to the circle. But as you can see from the logging, in componentDidUpdate the ref is still pointing to the box:
componentDidUpdate: the active shape is box
Logging in lines after that reveals the ref callbacks are hit
box ref null [React calls null on the old ref to prevent memory leaks]
circle ref [object Object]
You can drop breakpoints in to verify and to inspect. I would expect these two things to happen before we enter componentDidUpdate, but as you can see, it's happening in reverse. Why is this? Is there an underlying issue in react-three-renderer (if so, can you diagnose it?), or am I misunderstanding React refs?
The MVCE is available in this github repository. Download it, run npm install, and open _dev/public/home.html.
Thanks in advance.
I checked the source in react-three-renderer. In lib/React3.jsx, there is a two phased render.
componentDidMount() {
this.react3Renderer = new React3Renderer();
this._render();
}
componentDidUpdate() {
this._render();
}
The _render method is the one that seems to loads the children - the mesh objects within Three.
_render() {
const canvas = this._canvas;
const propsToClone = { ...this.props };
delete propsToClone.canvasStyle;
this.react3Renderer.render(
<react3
{...propsToClone}
onRecreateCanvas={this._onRecreateCanvas}
>
{this.props.children}
</react3>, canvas);
}
The render method draws the canvas and does not populate the children or invoke Three.
render() {
const {
canvasKey,
} = this.state;
return (<canvas
ref={this._canvasRef}
key={canvasKey}
width={this.props.width}
height={this.props.height}
style={{
...this.props.canvasStyle,
width: this.props.width,
height: this.props.height,
}}
/>);
}
Summarizing, this is the sequence:
App Component render is called.
This renders react-three-renderer which draws out a canvas.
componentDidUpdate of App component is called.
componentDidUpdate of react-three-renderer is called.
This calls the _render method.
The _render method updates the canvas by passing props.children (mesh objects) to the Three library.
When the mesh objects are mounted, the respective refs are invoked.
This explains what you are observing in the console statements.

Use Onload on image tag in ember

I have a template in which photos are being displayed in a frame ( each frame is different for different images) .I have written a function which uses the images original height and width and gives me customized width and height for that particular frame inorder to restore the aspect ratio.Now I have called that function through onload as images loads on that particular moment.
My feed.hbs( template)
<img src = "{{photo.0.photo_url}}" onload = "OnImageLoad(event);" {{action "imgOverlay0" photo}}/>
Function
function OnImageLoad(evt) {
var img = evt.currentTarget;
// what's the size of this image and it's parent
var w = $(img).width();
var h = $(img).height();
var tw = $(img).parent().width();
var th = $(img).parent().height();
// compute the new size and offsets
var result = scaling(w, h, tw, th,false);
// adjust the image coordinates and size
img.width = result.width;
img.height = result.height;
$(img).css("margin-left", result.targetleft);
$(img).css("margin-top", result.targettop);
// console.log("result",result)
return result;
}
function scaling (w, h, tw, th,false){
//manipulation with data
}
But it will not be included in the build of ember as I have kept the function file in bower_compontent.How do I include it in my ember app ?
Instead of creating a bower component, I'd create a few ember components: one that triggers an action when the image is loaded, and another that handles scaling.
app/components/x-image/component.js
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'img',
didInsertElement() {
this._super(...arguments);
this.$()[0].onload = () => {
this.sendAction('imageLoaded');
};
},
});
app/components/scaled-image/component.js
import Ember from 'ember';
export default Ember.Component.extend({
setImageDimensions() {
const img = this.$('img');
// what's the size of this image and it's parent
const w = img.width();
const h = img.height();
const tw = img.parent().width();
const th = img.parent().height();
// compute the new size and offsets
const result = this.scaling(w, h, tw, th, false);
// adjust the image coordinates and size
img.width = result.width;
img.height = result.height;
img.css("margin-left", result.targetleft);
img.css("margin-top", result.targettop);
// console.log("result",result)
},
scaling(w, h, tw, th,false) {
//manipulation with data
},
actions: {
imageLoaded() {
this.setImageDimensions();
}
}
});
app/components/scaled-image/template.hbs
{{x-image
src=src
imageLoaded=(action 'imageLoaded')
}}
In-use in a template
{{scaled-image
src=photo.0.photo_url
action=(action "imgOverlay0" photo)
}}
It will be better to put this javascript file in vendor directory because normally bower_components is included in .gitignore.
Say you put this code in vendor/file-onload.js.
Then do a import in ember-cli-build.js
app.import('vendor/file-onload.js');
It will be even easier if you put these functions in corresponding feed controller.

React.JS calculating img width in virtual DOM

I'm trying to find the width of an <img> to center it via JavaScript.
Trying to calculate the width of this react.js DOM node returns 0.
var Player = React.createClass({
componentDidMount:function(){
var imgEl = this.refs.imgSize.getDOMNode();
console.log(imgEl.offsetWidth);
},
render: function () {
return (
<img ref="imgSize" src={this.props.imageURL} />
);
}
});
You could use the image's onLoad event to make sure it's loaded before you try this:
<script src="http://fb.me/react-0.12.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
<script type="text/jsx;harmony=true">void function() { "use strict";
var Player = React.createClass({
_onLoad(e) {
console.log(e.target.offsetWidth)
},
render() {
return <img src={this.props.imageURL} onLoad={this._onLoad}/>
}
})
React.render(<Player imageURL="http://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg"/>, document.body)
}()</script>
I wrote a React library that exposes a size object (with width and height props) to your components.
You can use it like so for your use case:
var SizeMe = require('react-sizeme');
var Player = React.createClass({
componentDidMount:function(){
var imgEl = this.refs.imgSize.getDOMNode();
console.log(imgEl.offsetWidth);
},
render: function () {
// height and width via props!!
var width = this.props.width;
var height = this.props.height;
return (
<img ref="imgSize" src={this.props.imageURL} />
);
}
});
// Wrap your component with the SizeMe HOC!
module.exports = SizeMe()(Player);
Demo: https://react-sizeme-example-esbefmsitg.now.sh/
Github: https://github.com/ctrlplusb/react-sizeme

Categories

Resources