I'm trying to replicate this https://codepen.io/swizec/pen/bgvEvp
I've installed the d3-timer package with npm https://www.npmjs.com/package/d3-timer
It is definitely there because I have read through the files.
What I am confused about is how to import the timer into my code. In the code on codepen it just uses d3.timer but doesn't show the import above. So I tried importing d3 but it can't find it in the d3-timer package. I tried timer, Timer, D3, d3.
So my question is - how do I go about investigating the package to work out what the names of the exports are?
Or if that is too complicated - in this particular case what should I be importing to get the functionality of d3.timer?
Many thanks!
Code from code pen:
const Component = React.Component;
const Ball = ({ x, y }) => (
<circle cx={x} cy={y} r={5} />
);
const MAX_H = 750;
class App extends Component {
constructor() {
super();
this.state = {
y: 5,
vy: 0
}
}
componentDidMount() {
this.timer = d3.timer(() => this.gameLoop());
this.gameLoop();
}
componentWillUnmount() {
this.timer.stop();
}
gameLoop() {
let { y, vy } = this.state;
if (y > MAX_H) {
vy = -vy*.87;
}
this.setState({
y: y+vy,
vy: vy+0.3
})
}
render() {
return (
<svg width="100%" height={MAX_H}>
<Ball x={50} y={this.state.y} />
</svg>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
my code
import React from 'react';
import d3 from 'd3-timer'
const Component = React.Component;
const Ball = ({ x, y }) => (
<circle cx={x} cy={y} r={5} />
);
const MAX_H = 750;
export default class App extends Component {
constructor() {
super();
this.state = {
y: 5,
vy: 0
}
}
componentDidMount() {
this.timer = d3.timer(() => this.gameLoop());
this.gameLoop();
}
componentWillUnmount() {
this.timer.stop();
}
gameLoop() {
let { y, vy } = this.state;
if (y > MAX_H) {
vy = -vy*.87;
}
this.setState({
y: y+vy,
vy: vy+0.3
})
}
render() {
return (
<svg width="100%" height={MAX_H}>
<Ball x={50} y={this.state.y} />
</svg>
)
}
}
Error message:
Attempted import error: 'd3-timer' does not contain a default export (imported as 'd3').
Try
import { timer } from 'd3-timer' and then use timer()
Related
I have a simple application consisting of two - parent App and child Circle components. My aim is to draw circle on canvas (Circle component does that) and have input that takes number value and makes angle of that numerical value with the radius. like so :
example
The problem is, i've set up onChange event listener with handler on input, which is supposed to update my state, but whenever i type something into input field, it tells me that my canvas is null, and .getContext("2d") method can't be performed, which messes up entire application.
I've googled and found out that this is because of the fact that canvas renders after state is updated, or something like that. I can't think of any solution that would make that possible. here's my code :
App.js
import React, { Component } from "react";
import "./App.css";
import { degreesToRadiansFlipped } from "./helpers/helpers";
import Circle from "./components/Circle";
class App extends Component {
state = { degrees: 0 };
handleChange = (event) => {
this.setState({ degrees: event.target.value });
};
coordinates = {
x: Math.cos(degreesToRadiansFlipped(120)) * 100 + 150,
y: Math.sin(degreesToRadiansFlipped(120)) * 100 + 150
};
drawCircle = (context, x, y) => {
context.beginPath();
context.arc(150, 150, 100, 0, Math.PI * 2);
context.moveTo(150, 150);
context.lineTo(x, y);
context.stroke();
};
render() {
console.log(this.currentDegreeValue);
return (
<div className="main">
<Circle drawCircle={this.drawCircle} coordinates={this.coordinates} />
<form>
<input name="degrees" type="text" onChange={this.handleChange} />
</form>
</div>
);
}
}
export default App;
Circle.js
import React, { Component } from "react";
class Circle extends Component {
componentDidMount() {
this.props.drawCircle(
this.context,
this.props.coordinates.x,
this.props.coordinates.y
);
}
render() {
return (
<canvas
ref={(canvas) => (this.context = canvas.getContext("2d"))}
width={300}
height={300}
/>
);
}
}
export default Circle;
Actual Error is :
TypeError: Cannot read property 'getContext' of null
// index.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Circle from "./Circle";
class App extends Component {
state = { degrees: 0, showCirle: false };
handleChange = event => {
this.setState({ degrees: event.target.value, showCirle: true });
};
degreesToRadiansFlipped(angle) {
return (angle + Math.PI) % (2 * Math.PI);
}
coordinates = {
x: Math.cos(this.degreesToRadiansFlipped(120)) * 100 + 150,
y: Math.sin(this.degreesToRadiansFlipped(120)) * 100 + 150
};
drawCircle = (context, x, y) => {
console.log("inside drawcircle");
context.beginPath();
context.arc(150, 150, 100, 0, Math.PI * 2);
context.moveTo(150, 150);
context.lineTo(x, y);
context.stroke();
};
render() {
console.log(this.currentDegreeValue);
return (
<div className="main">
{this.state.showCirle && (
<Circle drawCircle={this.drawCircle} coordinates={this.coordinates} />
)}
<form>
<input name="degrees" type="text" onChange={this.handleChange} />
</form>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// Circel Component
import React, { Component } from "react";
class Circle extends Component {
constructor(props) {
super(props);
}
fillContext = () => {
const canvas = this.refs.canvas
const ctx = canvas.getContext("2d")
this.context = ctx
this.props.drawCircle(
this.context,
this.props.coordinates.x,
this.props.coordinates.y
);
}
componentDidMount() {
this.fillContext()
}
componentDidUpdate() {
this.fillContext()
}
render() {
return <canvas ref="canvas" width={300} height={300} />;
}
}
export default Circle;
In your example, x and y coordinates should change based on input value change, but coordinates is an object with fixed x and y values. I think that logic should also change.
I've playing around with animation implemented with reactjs.
In the app I created a car which drives around a track. On this track there are obstacles, which the car should recognize.
I'm using window.setInterval for the repeating events. Maybe this is not the best option, but actually I don't know how to do else.
Since some changes, there are multiple intervals running.
But actually I don't know the reason for it. Can anybody give me a hint, why the racer component is instantly running in componentdidmount event?
The Racer component is giving the current position and degree / ankle to the Track component. The Track component is storing these values in states and giving it to the Racer component as props. But this should not lead to instantly firing componentdidmount event of Racer component, or?
Here is my code:
App.js
import React, { Component } from 'react';
import Track from './components/track.js';
const uuidv1 = require('uuid/v1');
class App extends Component {
constructor(props) {
super(props);
this.state = {
obstacles: [
{
key: uuidv1(),
position: {
left: 500,
top:10,
},
width: 25,
height: 25,
},
{
key: uuidv1(),
position: {
left: 650,
top:60,
},
width: 25,
height: 25,
}
],
};
}
render() {
return (
<div className="App">
<Track width={800} height={100} obstacles={this.state.obstacles}>
</Track>
</div>
);
}
}
export default App;
Track.js
import React, { Component } from 'react';
import styled from 'styled-components';
import Racer from './racer.js';
import Obstacle from './obstacle';
import centralStrip from '../images/centralStrip.png';
const uuidv1 = require('uuid/v1');
class Track extends Component {
constructor(props) {
super(props);
this.state = {
racerCurrentPosition: {
top: 60,
left:150
},
racerDegree: 0,
};
}
componentDidMount() {
}
handleObstacleCheck(position, racerPosition) {
let obstacleFound = false;
obstacleFound = this.props.obstacles.map((obstacle) => {
let returnValue = false;
let obstacleRect = document.getElementById(obstacle.key).getBoundingClientRect();
if( position.right >= obstacleRect.left && position.right <= obstacleRect.right && racerPosition.top >= obstacleRect.top && racerPosition.bottom <= obstacleRect.bottom) {
returnValue = true;
}
return returnValue;
});
let isObstacleFound = false;
if(obstacleFound.indexOf(true) !== -1) {
isObstacleFound = true;
}
return isObstacleFound;
}
handleRacerPositionChange(position) {
this.setState({
racerCurrentPosition: position,
});
}
handleRacerDegreeChange(newDegree) {
this.setState({
racerDegree: newDegree,
});
}
render() {
return (
<TrackImage key={uuidv1()}
id="track"
width={this.props.width}
height={this.props.height}>
<Racer key={uuidv1()}
position={this.state.racerCurrentPosition}
onRacerPositionChange={this.handleRacerPositionChange.bind(this)}
degree={this.state.racerDegree}
onRacerDegreeChange={this.handleRacerDegreeChange.bind(this)}
obstacleFound={this.state.obstacleFound}
trackWidth={this.props.width}
trackHeight={this.props.height}
onObstacleCheck={this.handleObstacleCheck.bind(this)}
/>
{
this.props.obstacles.map((obstacle) => {
return (
<Obstacle key={obstacle.key}
id={obstacle.key}
position={obstacle.position}
width={obstacle.width}
height={obstacle.height}
/>
);
})
}
</TrackImage>
);
}
}
export default Track;
Racer.js
import React, { Component, Fragment } from 'react';
import styled from 'styled-components';
import HelperDistance from './helpers/distance.js';
import HelperCenterCar from './helpers/centerCar.js';
import racerImage from '../images/racer.png';
const uuidv1 = require('uuid/v1');
class Racer extends Component {
constructor(props) {
super(props);
this.state = {
key: uuidv1(),
intervalId: 0,
speed: 0,
helperForLeftPositioning: 0,
helperForTopPositioning: 0,
isMoving: false,
collision: false,
centerOfCarCoordinates: {
x: 25,
y: 12.5
},
obstacleFound: false,
};
this.start = this.start.bind(this);
this.move = this.move.bind(this);
}
componentDidMount() {
if(this.state.intervalId === 0) {
this.start();
}
}
componentWillUnmount() {
window.clearInterval(this.state.intervalId);
}
start() {
this.setState({
speed: 3,
isMoving: true,
}, () => {
this.createInterval();
});
}
stop() {
this.setState({
speed: 0,
isMoving: false,
}, () => {
window.clearInterval(this.state.intervalId);
});
}
move() {
if(this.state.obstacleFound === true) {
let newDegree;
if(this.props.degree === 0) {
newDegree = 360;
}
newDegree--;
this.props.onRacerDegreeChange(newDegree);
}
this.step();
}
step() {
if(this.state.isMoving) {
//...calculate new position
this.setState({
helperForTopPositioning: helperForTopPositioning,
helperForLeftPositioning: helperForLeftPositioning,
},() => {
let position = {
left: positionNewLeft,
top: positionNewTop
};
this.props.onRacerPositionChange(position);
});
}
}
createInterval = () => {
let intervalId = window.setInterval(() => {
this.move();
console.log("IntervalId: " + intervalId);
},100);
this.setState({
intervalId: intervalId,
})
}
handleDistanceChange(position) {
let racerRect = document.getElementById(this.state.key).getBoundingClientRect();
let obstacleFound = this.props.onObstacleCheck(position, racerRect);
if(this.state.obstacleFound !== obstacleFound) {
this.setState({
obstacleFound: obstacleFound
});
}
}
render() {
return (
<Fragment>
<Car key={this.state.key} id={this.state.key} position={this.props.position} degree={this.props.degree}>
<HelperCenterCar key={uuidv1()} position={this.state.centerOfCarCoordinates} degree={this.props.degree} />
<HelperDistance key={uuidv1()} onChange={this.handleDistanceChange.bind(this)} position={this.state.centerOfCarCoordinates} degree={this.props.degree} />
</Car>
</Fragment>
);
}
}
export default Racer;
The HelperCenterCar and HelperDistance are components, which helps to identify, if there is an obstacle in the way. I'll post just the code of HelperDistance, because here instantly state updates are fired.
HelperDistance.js
import React, { Component } from 'react';
import styled from 'styled-components';
const uuidv1 = require('uuid/v1');
class HelperDistance extends Component {
constructor(props) {
super(props);
this.state = {
key: uuidv1(),
};
}
componentDidMount() {
this.handleOnChange();
}
componentDidUpdate(prevProps, prevState, snapshot) {
this.handleOnChange();
}
handleOnChange() {
let position = document.getElementById(this.state.key).getBoundingClientRect();
this.props.onChange(position);
}
render() {
return (
<Line id={this.state.key} key={this.state.key} position={this.props.position} degree={this.props.degree} />
);
}
}
export default HelperDistance;
Using react-native, I'm creating sub-Components within the parent App and providing their position to the array this.state.objLocation within the parent App.
I can get the initial location data into the array straight after the render, but because my subcomponents are draggable, each time they re-render on drag, it adds a new position object to the array.
I'd like to avoid this, and I thought that creating this.state = { firstRender: true } in the constructor and then using componentDidMount = () => { this.setState({ firstRender: false }) } after the first render would allow me to create a 'gate' to stop the addition of the extra position objects.
I can see that if I comment out //componentDidMount = () => { this.setState({ firstRender: false }) } then I will get multiple entries to my array but if it's included in the class I get absolutely none.
So possibly my interpretation of the render lifecycle and componentDidMount is incorrect?
Here is my code.
// App
import React, { Component } from 'react';
import { View, Text, } from 'react-native';
import styles from './cust/styles';
import Draggable from './cust/draggable';
const dataArray = [{num: 1,id: 'A',},{num: 2,id: 'B',},{num: 3,id: 'Z',}]
export default class Viewport extends Component {
constructor(props){
super(props);
this.state = {
dID : null,
objLocation: [],
firstRender: true,
};
}
render(){
return (
<View style={styles.mainContainer}>
<View style={styles.draggableContainer}>
<Text>Draggable Container</Text> {dataArray.map( d => { return(
<Draggable
id={d.id}
onLayout={ e=> this.onLayout(e)}
onPanResponderGrant={(dID) =>this.setState({ dID })}
onPanResponderRelease={() => this.setState({dID: null})} /> ) })}
<View style={[styles.findPoint ]} />
</View>
<View style={styles.infoBar}>
<Text>{this.state.dID ? this.state.dID : ''}</Text>{this.compFrame()}
</View>
</View>
);
}
onLayout = (e) => {
if ( e && this.state.firstRender) {
const n = e.nativeEvent.layout;
const position = {
width: n.width,
height: n.height,
x: n.x,
y: n.y
}
console.log(position);
this.setState({
objLocation: this.state.objLocation.concat([position])
});
}
}
componentWillMount = () => {
console.log("START");
}
compFrame = () => {
return(
this.state.objLocation.map( d => {<View style={[styles.findPoint2,{left: d.x, top: d.y, width: d.width, height: d.height} ]} ></View>})
)
}
componentDidMount = () => {
this.setState({firstRender: true })
console.log(this.state.objLocation.length);
}
}
// Draggable
import React, { Component } from 'react';
import { Text, PanResponder, Animated } from 'react-native';
import styles from './styles';
class Draggable extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(),
};
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
this.props.onPanResponderGrant(this.props.id);
},
onPanResponderMove: Animated.event([ null, {
dx: this.state.pan.x,
dy: this.state.pan.y,
},
]),
onPanResponderRelease: () => {
Animated.spring(this.state.pan, { toValue: { x: 0, y: 0 } }).start();
this.props.onPanResponderRelease();
},
});
}
render() {
return (
<Animated.View
onLayout={ (e) => this.props.onLayout(e) }
{...this.panResponder.panHandlers}
style={[this.state.pan.getLayout(), styles.circleAlt, styles.position]}>
<Text style={styles.textAlt}>Drag me!</Text>
<Text style={styles.textNum}>{this.props.id}</Text>
</Animated.View>
);
}
componentDidMount = () => {
this.props.onLayout(this.props.dragEvent)
}
}
export default Draggable;
// Output of console.log
START xxx
0
{width:108,height:108,x:133.5,y:376.5}
{width:108,height:108,x:133.5,y:78.5}
{width:108,height:108,x:133.5,y:227.5}
You could set the firstRender state in onLayout function
onLayout = (e) => {
if ( e && this.state.firstRender) {
const n = e.nativeEvent.layout;
const position = {
width: n.width,
height: n.height,
x: n.x,
y: n.y
}
console.log(position);
this.setState({
firstRender: false,
objLocation: this.state.objLocation.concat([position])
});
}
}
According to the information provided by you, your onLayout function is called by the component so its not included in the component lifecycle process, so when the component completes its lifecycle it goes into componentDidMount after mounting (which is not calling onLayout func) & thus changed the firstRender state to false and hence when you drag the component each time it goes from true to false.
I hope this explains
I feel like I've hacked this, to get it to work, so please correct me as to correct procedure.
This is the onLayout method from the App. I've included an if statement that checks if the new positions array length is equal too the dataArray length that the draggable items are based on.
It looks like this.
onLayout = (e) => {
if ( this.state.objLocation.length != dataArray.length ) {
if ( e ) {
const n = e.nativeEvent.layout;
const position = {
width: n.width,
height: n.height,
x: n.x,
y: n.y
}
console.log(position);
this.setState({
objLocation: this.state.objLocation.concat([position])
});
}
}
}
I'm working to use Victory's charting library with React to render a Animating Circular Progress bar like so:
https://formidable.com/open-source/victory/gallery/animating-circular-progress-bar
Here is my code:
import React from 'react';
import {connect} from 'react-redux';
import {VictoryPie, VictoryAnimation, VictoryLabel} from 'victory';
class YourRatings2 extends React.Component {
constructor() {
super();
this.state = {
percent: 25, data: this.getData(0)
};
}
componentDidMount() {
let percent = 25;
}
getData(percent) {
return [{x: 1, y: percent}, {x: 2, y: 100 - percent}];
}
render() {
return (
<section className="YourRatings">
<h2>Core Skills</h2>
<div>
<svg viewBox="0 0 400 400" width="100%" height="100%">
<VictoryPie
animate={{duration: 1000}}
width={400} height={400}
data={this.state.data}
innerRadius={120}
cornerRadius={3}
labels={() => null}
style={{
data: { fill: (d) => {
const color = d.y > 30 ? "green" : "red";
return d.x === 1 ? color : "transparent";
}
}
}}
/>
<VictoryAnimation duration={1000} data={this.state}>
{(newProps) => {
return (
<VictoryLabel
textAnchor="middle" verticalAnchor="middle"
x={200} y={200}
text={`${Math.round(newProps.percent)}%`}
style={{ fontSize: 45 }}
/>
);
}}
</VictoryAnimation>
</svg>
</div>
</section>
);
}
}
const mapStateToProps = state => {
return {
};
};
export default connect(mapStateToProps)(YourRatings2);
Unfortunately, this is only rendering the text, not the full graph. see:
Any pointers as to what I am doing wrong?
I'm not an expert on this library but the example that you gave has an interval function to update this.state.data with the new percentage. You are setting initial percentage to 0 and not updating.
Setting the interval like the example or setting the initial state with the example below should fix the problem.
Example
constructor() {
super();
this.state = {
percent: 25,
data: this.getData(25)
};
}
I think you may need to add the prop standalone={false} to VictoryPie
I'm using the react-dnd-touch-backend.
I'm able to get my DragSources to drag correctly, but the DropTargets don't accept them (or react to being dragged over).
The application only uses one wrapper component for each role (DragSource and DropTarget). I have also defined a custom drag layer. The drag/drop worked fine before adding the custom drag layer except my DragSources were invisible on iOS (which is why I added the drag layer in the first place), but now I can see the DragSources, but the DropTargets don't work.
Any help is much appreciated.
DragSource:
import React from "react";
import cn from "util/cn";
import {isCordova} from "util/detect-platform";
import {DragSource} from "react-dnd";
import {getEmptyImage} from "react-dnd-html5-backend";
require("./style.scss");
const TYPE = "DRAG-CONTAINER";
const source = {
beginDrag({value, left, top, children, DragPreviewComponent}) {
return {value, left, top, children, DragPreviewComponent};
}
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging()
};
}
function getStyles(props) {
const {left, top, isDragging} = props;
const transform = `translate3d(${left}px, ${top}px, 0)`;
return {
transform: transform,
WebkitTransform: transform,
opacity: isDragging ? 0 : 1
};
}
#DragSource(TYPE, source, collect)
export default class DragContainer extends React.Component {
static propTypes = {
value: React.PropTypes.any
};
static defaultProps = {style: {}};
componentDidMount() {
if(!isCordova()) {
this.props.connectDragPreview(getEmptyImage(), {
captureDraggingState: true
});
}
}
render() {
const {className, isDragging, connectDragSource, style} = this.props;
const classNames = cn(
"Drag-container",
isDragging ? "Drag-container--dragging" : null,
className
);
return connectDragSource(
<div {...this.props} className={classNames} value={null} style={{...style, ...getStyles(this.props)}}/>
);
}
}
DropTarget:
import React from "react";
import {DropTarget} from "react-dnd";
import cn from "util/cn";
require("./style.scss");
const TYPE = "DRAG-CONTAINER";
const target = {
drop(props, monitor) {
const {onDrop} = props;
const {value} = (monitor.getItem() || {value: null});
if(typeof onDrop === "function") {
setTimeout(() => onDrop(value), 100);
}
}
};
function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver()
};
}
#DropTarget(TYPE, target, collect)
export default class DropContainer extends React.Component {
static propTypes = {
onDrop: React.PropTypes.func
};
render() {
const {connectDropTarget, isOver, className} = this.props;
const classNames = cn("Drop-container", isOver ? "Drop-container--over" : null, className);
return connectDropTarget(
<div {...this.props} className={classNames} onDrop={null} onDragEnter={null} onDragExit={null}/>
);
}
}
Custom Drag Layer:
import React from "react";
import {DragLayer} from "react-dnd";
const layerStyles = {
position: "fixed",
pointerEvents: "none",
width: "100%",
height: "100%",
zIndex: 100,
left: 0,
top: 0
};
function getItemStyles(props) {
const { initialOffset, currentOffset } = props;
if (!initialOffset || !currentOffset) {
return {
display: 'none'
};
}
let { x, y } = currentOffset;
if (props.snapToGrid) {
x -= initialOffset.x;
y -= initialOffset.y;
[x, y] = snapToGrid(x, y);
x += initialOffset.x;
y += initialOffset.y;
}
const transform = `translate(${x}px, ${y}px)`;
return {
transform: transform,
WebkitTransform: transform
};
}
#DragLayer(monitor => ({
item: monitor.getItem(),
itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset(),
isDragging: monitor.isDragging()
}))
export default class CustomDragLayer extends React.Component {
render() {
const {item, itemType, isDragging} = this.props;
if (!isDragging || !item) return null;
const {DragPreviewComponent} = item;
if(!DragPreviewComponent) return null;
return (
<div style={layerStyles}>
<div style={getItemStyles(this.props)}>
<DragPreviewComponent {...item}/>
</div>
</div>
);
}
}
The problem was this issue in the react-dnd-touch-backend library:
https://github.com/yahoo/react-dnd-touch-backend/issues/34
Rolling back to version 0.2.7 fixed the issue.