I would like to create a canvas where the user can draw an Arrow by using his mouse.
What I'm trying to accomplish is exactly this: https://jsfiddle.net/w33e9fpa/
But I don't understand how to convert that to React code, and my implementation currently doesn't work. When I run this code it seems that an arrow is drawn on the top left of the canvas, but nothing happens if I click on it.
Here's my code:
class DrawArrow extends Component {
state = {
isDrawing: false,
mode: "brush"
};
componentDidMount() {
const canvas = document.createElement("canvas");
canvas.width = 300;
canvas.height = 300;
const context = canvas.getContext("2d");
this.setState({ canvas, context });
}
handleMouseDown = () => {
this.setState({ isDrawing: true });
// TODO: improve
const stage = this.arrow.parent.parent;
this.lastPointerPosition = stage.getPointerPosition();
this.setState({
posX: this.lastPointerPosition.x,
poxY: this.lastPointerPosition.y
})
}
handleMouseUp = () => {
this.setState({ isDrawing: false });
};
handleMouseMove = () => {
if (this.state.drawing === true) {
const stage = this.arrow.parent.parent;
this.lastPointerPosition = stage.getPointerPosition();
var pos = stage.getPointerPosition();
var oldPoints = this.arrow.points();
this.arrow.points([oldPoints[0], oldPoints[1], pos.x, pos.y])
this.arrow.getLayer().draw();
}
}
render() {
return (
<Arrow
points= {[this.state.posX,this.state.posY, this.state.posX, this.state.posY]}
pointerLength= {20}
pointerWidth= {20}
fill= 'black'
stroke= 'black'
strokeWidth= {4}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
onMouseMove={this.handleMouseMove}
/>
);
}
}
class NewWhite extends Component {
render() {
return (
<Stage width={900} height={700}>
<Layer>
<DrawArrow />
</Layer>
</Stage>
);
}
}
Thanks for you help !
Here you go:
import React, { Component } from "react";
import { Stage, Layer, Arrow, Circle, Line } from "react-konva";
import ReactDOM from "react-dom";
import "./styles.css";
class Drawable {
constructor(startx, starty) {
this.startx = startx;
this.starty = starty;
}
}
class ArrowDrawable extends Drawable {
constructor(startx, starty) {
super(startx, starty);
this.x = startx;
this.y = starty;
}
registerMovement(x, y) {
this.x = x;
this.y = y;
}
render() {
const points = [this.startx, this.starty, this.x, this.y];
return <Arrow points={points} fill="black" stroke="black" />;
}
}
class CircleDrawable extends ArrowDrawable {
constructor(startx, starty) {
super(startx, starty);
this.x = startx;
this.y = starty;
}
render() {
const dx = this.startx - this.x;
const dy = this.starty - this.y;
const radius = Math.sqrt(dx * dx + dy * dy);
return (
<Circle radius={radius} x={this.startx} y={this.starty} stroke="black" />
);
}
}
class FreePathDrawable extends Drawable {
constructor(startx, starty) {
super(startx, starty);
this.points = [startx, starty];
}
registerMovement(x, y) {
this.points = [...this.points, x, y];
}
render() {
return <Line points={this.points} fill="black" stroke="black" />;
}
}
class SceneWithDrawables extends Component {
constructor(props) {
super(props);
this.state = {
drawables: [],
newDrawable: [],
newDrawableType: "FreePathDrawable"
};
}
getNewDrawableBasedOnType = (x, y, type) => {
const drawableClasses = {
FreePathDrawable,
ArrowDrawable,
CircleDrawable
};
return new drawableClasses[type](x, y);
};
handleMouseDown = e => {
const { newDrawable } = this.state;
if (newDrawable.length === 0) {
const { x, y } = e.target.getStage().getPointerPosition();
const newDrawable = this.getNewDrawableBasedOnType(
x,
y,
this.state.newDrawableType
);
this.setState({
newDrawable: [newDrawable]
});
}
};
handleMouseUp = e => {
const { newDrawable, drawables } = this.state;
if (newDrawable.length === 1) {
const { x, y } = e.target.getStage().getPointerPosition();
const drawableToAdd = newDrawable[0];
drawableToAdd.registerMovement(x, y);
drawables.push(drawableToAdd);
this.setState({
newDrawable: [],
drawables
});
}
};
handleMouseMove = e => {
const { newDrawable } = this.state;
if (newDrawable.length === 1) {
const { x, y } = e.target.getStage().getPointerPosition();
const updatedNewDrawable = newDrawable[0];
updatedNewDrawable.registerMovement(x, y);
this.setState({
newDrawable: [updatedNewDrawable]
});
}
};
render() {
const drawables = [...this.state.drawables, ...this.state.newDrawable];
return (
<div>
<button
onClick={e => {
this.setState({ newDrawableType: "ArrowDrawable" });
}}
>
Draw Arrows
</button>
<button
onClick={e => {
this.setState({ newDrawableType: "CircleDrawable" });
}}
>
Draw Circles
</button>
<button
onClick={e => {
this.setState({ newDrawableType: "FreePathDrawable" });
}}
>
Draw FreeHand!
</button>
<Stage
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
onMouseMove={this.handleMouseMove}
width={900}
height={700}
>
<Layer>
{drawables.map(drawable => {
return drawable.render();
})}
</Layer>
</Stage>
</div>
);
}
}
function App() {
return <SceneWithDrawables />;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working example to play with:
https://codesandbox.io/s/w12qznzx5
Related
When I add scale and dpr to my drawing app's canvas to improve the look of objects on the canvas, it produces issues when resizing the window. The scale seems to be off and possibly flickering between 2 different scales as you change window size quickly? I can't seem to figure out the cause of this. As soon as I remove scale and dpr, then everything works fine. Here's my code and a codesandbox with it. https://codesandbox.io/s/holy-wave-t9y2g0?file=/src/App.js
import React, { useEffect, useRef, useState } from "react";
import getStroke from "perfect-freehand";
function getSvgPathFromStroke(stroke) {
if (!stroke.length) return "";
const d = stroke.reduce(
(acc, [x0, y0], i, arr) => {
const [x1, y1] = arr[(i + 1) % arr.length];
acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
return acc;
},
["M", ...stroke[0], "Q"]
);
d.push("Z");
return d.join(" ");
}
export default function App() {
const [elements, setElements] = useState([]);
const wrapperRef = useRef(null);
const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const [dimensions, setDimensions] = useState({
height: 0,
width: 0
});
const elementsRef = useRef(elements);
const dpr = window.devicePixelRatio || 1;
// in place of original `setElements`
const setElementsRef = (x) => {
elementsRef.current = x; // keep updated
setElements(x);
};
const setNewElements = ({ type, points, ...rest }) => {
setElementsRef([...elements, { type, points, ...rest }]);
};
useEffect(() => {
document.body.style.overscrollBehavior = "contain";
window.addEventListener("resize", () => {
resizeCanvas();
});
}, []);
useEffect(() => {
if (wrapperRef.current && canvasRef.current) {
setupCanvas(true);
}
}, [wrapperRef.current, canvasRef.current]);
useEffect(() => {
drawCanvas();
}, [elements]);
const drawCanvas = () => {
const context = canvasRef.current.getContext("2d");
context.clearRect(0, 0, dimensions.width * dpr, dimensions.height * dpr);
elementsRef.current.forEach((element) => {
const stroke = getStroke(element.points);
const pathData = getSvgPathFromStroke(stroke);
const myPath = new Path2D(pathData);
context.fill(myPath);
});
};
const setupCanvas = () => {
const newHeight = wrapperRef.current.clientHeight ?? 0;
const newWidth = wrapperRef.current.clientWidth ?? 0;
setDimensions({
height: newHeight,
width: newWidth
});
const context = canvasRef?.current?.getContext("2d");
context.scale(dpr, dpr);
return new Promise((fulfill) => fulfill(true));
};
const resizeCanvas = () => {
if (canvasRef.current) {
setupCanvas().then(() => drawCanvas());
}
};
function handleMouseDown(e) {
setIsDrawing(true);
if (canvasRef.current) {
const bounds = canvasRef?.current?.getBoundingClientRect();
const x = e.pageX - bounds.left - window.scrollX;
const y = e.pageY - bounds.top - window.scrollY;
setNewElements({ type: "pen", points: [[x, y]] });
}
}
function handleMouseMove(e) {
if (canvasRef.current && isDrawing) {
const bounds = canvasRef.current.getBoundingClientRect();
const x = e.pageX - bounds.left - window.scrollX;
const y = e.pageY - bounds.top - window.scrollY;
const arr = [...elements];
arr[arr.length - 1].points.push([x, y]);
setElementsRef(arr);
}
}
function handleMouseUp(e) {
setIsDrawing(false);
}
return (
<div
id="wrapper"
style={{ height: "100vh", width: "100vw", overflow: "hidden" }}
ref={wrapperRef}
>
<canvas
id="canvas"
ref={canvasRef}
style={{ height: "100%", width: "100%" }}
width={dimensions.width * dpr}
height={dimensions.height * dpr}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
/>
</div>
);
}
I'm trying to modify a Spinner component by adding a new method Spinner.moveToPosition so that it will spin to a specific symbol, as opposed to Spinner.forceUpdateHandler which spins it to a random symbol.
Here's my CodePen: https://codepen.io/gameveloster/pen/qBxJPVK
Original CodePen: https://codepen.io/antibland/pen/ypagZd
However, when I added a "SPIN!" button where clicking the button will get them to spin to positions 0, 0 and 1 so that the left two spinners will display the same image
const handleClick = () => {
// The first and second symbols should be the same after spinning, but they are not
_child1?.current?.moveToPosition(0);
_child2?.current?.moveToPosition(0);
_child3?.current?.moveToPosition(1);
};
they end up not doing that:
What might be the issue here? Thank you!
Spinner.js
import React from "react";
class Spinner extends React.Component {
constructor(props) {
super(props);
this.forceUpdateHandler = this.forceUpdateHandler.bind(this);
}
moveToPosition(position) {
if (this.timer) {
clearInterval(this.timer);
}
this.setState({
position: position * Spinner.iconHeight,
timeRemaining: this.props.timer,
});
this.timer = setInterval(() => {
this.tick();
}, 100);
}
forceUpdateHandler() {
this.reset();
}
reset() {
if (this.timer) {
clearInterval(this.timer);
}
this.start = this.setStartPosition();
this.setState({
position: this.start,
timeRemaining: this.props.timer,
});
this.timer = setInterval(() => {
this.tick();
}, 100);
}
state = {
position: 0,
lastPosition: null,
};
static iconHeight = 188;
multiplier = Math.floor(Math.random() * (4 - 1) + 1);
start = this.setStartPosition();
speed = Spinner.iconHeight * this.multiplier;
setStartPosition() {
return Math.floor(Math.random() * 9) * Spinner.iconHeight * -1;
}
moveBackground() {
this.setState({
position: this.state.position - this.speed,
timeRemaining: this.state.timeRemaining - 100,
});
}
getSymbolFromPosition() {
let { position } = this.state;
const totalSymbols = 9;
const maxPosition = Spinner.iconHeight * (totalSymbols - 1) * -1;
let moved = (this.props.timer / 100) * this.multiplier;
let startPosition = this.start;
let currentPosition = startPosition;
for (let i = 0; i < moved; i++) {
currentPosition -= Spinner.iconHeight;
if (currentPosition < maxPosition) {
currentPosition = 0;
}
}
this.props.onFinish(currentPosition);
}
tick() {
if (this.state.timeRemaining <= 0) {
clearInterval(this.timer);
this.getSymbolFromPosition();
} else {
this.moveBackground();
}
}
componentDidMount() {
clearInterval(this.timer);
this.setState({
position: this.start,
timeRemaining: this.props.timer,
});
this.timer = setInterval(() => {
this.tick();
}, 100);
}
render() {
let { position, current } = this.state;
return (
<div
style={{ backgroundPosition: "0px " + position + "px" }}
className={`icons`}
/>
);
}
}
export default Spinner;
App.tsx
import React, { useRef } from "react";
import "./App.css";
import Spinner from "./Spinner.js";
const App = () => {
const [matches, setMatches] = React.useState<number[]>([]);
const _child1 = useRef<Spinner | null>(null);
const _child2 = useRef<Spinner | null>(null);
const _child3 = useRef<Spinner | null>(null);
const handleClick = () => {
// The first and second symbols should be the same after spinning, but they are not
_child1?.current?.moveToPosition(0);
_child2?.current?.moveToPosition(0);
_child3?.current?.moveToPosition(1);
};
const finishHandler = (value: number) => {
setMatches([...matches, value]);
if (matches.length === 3) {
console.log("Done");
emptyArray();
}
};
const emptyArray = () => {
setMatches([]);
};
return (
<div>
<div className={`spinner-container`}>
<Spinner onFinish={finishHandler} ref={_child1} timer="1000" />
<Spinner onFinish={finishHandler} ref={_child2} timer="1400" />
<Spinner onFinish={finishHandler} ref={_child3} timer="2200" />
<div className="gradient-fade"></div>
</div>
<button onClick={handleClick}>SPIN!!!</button>
</div>
);
};
export default App;
We recently worked on an auto-scrolling while freely swipeable component using React.js. The implementation idea is inspired by this article
And we've made something like this in React:
import React, { Component } from "react";
import PropTypes from "prop-types";
import "./AutoScroller.css";
const NUM_OF_CLONES = 10;
const AUTO_SCROLL_OFFSET = 1; // min offset of scrollTo is 1
const AUTO_SCROLL_INTERVAL = 32; // 1000 ms / 30 fps
export default class AutoScroller extends Component {
static propTypes = {
contents: PropTypes.array.isRequired,
itemWidth: PropTypes.number.isRequired,
numsOfItemsPerScreen: PropTypes.number.isRequired
};
constructor(props) {
super(props);
this.autoScrollerRef = React.createRef();
this.currentPosition = 0;
this.autoScrollTimer = null;
this.scrollingTimer = null;
/* boolean status */
this.isTouch = false;
this.isScrolling = false;
}
componentDidMount() {
this.startAutoScroll();
this.autoScrollerRef.current.addEventListener(
"touchstart",
this.touchStartHandler
);
this.autoScrollerRef.current.addEventListener(
"touchend",
this.touchEndHandler
);
this.autoScrollerRef.current.addEventListener("scroll", this.scrollHandler);
this.autoScrollerRef.current.addEventListener(
"contextmenu",
this.contextMenuHandler
);
}
componentWillUnmount() {
this.clearAutoScroll();
this.clearScrollingTimer();
this.autoScrollerRef.current.removeEventListener(
"touchstart",
this.touchStartHandler
);
this.autoScrollerRef.current.removeEventListener(
"touchend",
this.touchEndHandler
);
this.autoScrollerRef.current.removeEventListener(
"scroll",
this.scrollHandler
);
this.autoScrollerRef.current.removeEventListener(
"contextmenu",
this.contextMenuHandler
);
}
touchStartHandler = () => {
this.isTouch = true;
this.clearAutoScroll();
};
touchEndHandler = () => {
this.isTouch = false;
if (!this.isScrolling) {
this.currentPosition = this.autoScrollerRef.current.scrollLeft;
this.startAutoScroll();
}
};
scrollHandler = () => {
const {
contents: { length },
itemWidth
} = this.props;
this.isScrolling = true;
this.currentPosition = this.autoScrollerRef.current.scrollLeft;
const maxOffset = length * itemWidth;
if (this.currentPosition > maxOffset) {
const offset = this.currentPosition - maxOffset;
this.autoScrollerRef.current.scrollTo(offset, 0);
this.currentPosition = offset;
} else if (this.currentPosition <= 0) {
const offset = this.currentPosition + maxOffset;
this.autoScrollerRef.current.scrollTo(offset, 0);
this.currentPosition = offset;
}
/***
* note: there will be only one timer, and the timer is only created by the very last scroll
* only when the scroll event is not triggered anymore, the timer starts to get executed.
*/
if (this.scrollingTimer) {
clearTimeout(this.scrollingTimer);
}
this.scrollingTimer = setTimeout(() => {
this.isScrolling = false;
/***
* note: resume auto-scroll when the momentum scroll (after finger leaves) stalls the scroll
*/
if (!this.isTouch) {
this.startAutoScroll();
}
}, 300);
};
contextMenuHandler = (event) => {
event.preventDefault();
};
startAutoScroll = () => {
if (!this.autoScrollTimer) {
this.autoScrollTimer = setInterval(this.autoScroll, AUTO_SCROLL_INTERVAL);
}
};
clearAutoScroll = () => {
if (this.autoScrollTimer) {
clearInterval(this.autoScrollTimer);
this.autoScrollTimer = null;
}
};
clearScrollingTimer = () => {
if (this.scrollingTimer) {
clearTimeout(this.scrollingTimer);
this.scrollingTimer = null;
}
};
autoScroll = () => {
const {
contents: { length },
itemWidth,
numsOfItemsPerScreen
} = this.props;
if (this.currentPosition < 0) {
this.currentPosition = 0;
}
if (length > numsOfItemsPerScreen) {
const position = this.currentPosition + AUTO_SCROLL_OFFSET;
this.autoScrollerRef.current.scrollTo(position, 0);
const maxOffset = length * itemWidth;
if (this.currentPosition > maxOffset) {
const offset = this.currentPosition - maxOffset;
this.autoScrollerRef.current.scrollTo(offset, 0);
this.currentPosition = offset;
} else {
this.currentPosition = position;
}
}
};
getWrappedData = () => {
const { contents } = this.props;
const { length } = contents;
const numberOfClones = length < NUM_OF_CLONES ? length : NUM_OF_CLONES;
return [...contents, ...contents.slice(0, numberOfClones)];
};
render() {
const { itemGap, lineHeight } = this.props;
return (
<div className="auto-scroller" ref={this.autoScrollerRef}>
<ul>
{this.getWrappedData().map((content, index) => (
<Item
key={`auto-scroller-item-${index}`}
content={content}
itemGap={itemGap}
lineHeight={lineHeight}
/>
))}
</ul>
</div>
);
}
}
class Item extends Component {
static propTypes = {
content: PropTypes.object.isRequired,
itemGap: PropTypes.number,
lineHeight: PropTypes.number
};
render() {
const { content, itemGap = 10 } = this.props;
return (
<li
className="auto-scroller__item"
style={{ paddingRight: `${itemGap}px` }}
>
<div className="auto-scroller__item__content">
<img draggable={false} src={content.imgUrl} />
<div className="auto-scroller__item__content__title">
{content.title}
</div>
</div>
</li>
);
}
}
You can test with the demo from PlayCode (source code).
Just open the link with Safari on the iPhone.
What I observed was every time when it was on the boundary cases, the image started to flicker.
Further, if you swipe it with your finger forth and back on that point, the whole UI started to flicker. (see this screen recording) However, we didn't spot this glitch on Android devices.
Any possible solutions are welcome. Does anyone encounter something like this before?
removing overflow-y: hidden; and overflow-x: auto; from autoscroller.css
solved it on my end.
another solution would be to add z-index: 1; and scroll-behavior: smooth; to .auto-scroller
let me know if it worked!
I have troubles connecting an audio element with an analyser in Safari. Safari expects the user to interact with the browser before allowing an audio context to start, which is needed to connect an analyser with the audio element. For that I wait for a click event to connect, but the click on the audio element itself is not bubbled up to the body so that it cannot be caught. Any ideas what could be missing?
Here's some code:
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
getAudioElement,
loadAudio,
getNextTrack,
} from '../../helpers/common/audio';
import { isTouch } from '../../helpers/common/screen';
import { captureKeys } from '../../helpers/common/control';
import './Player.css';
const drawAnalyser = (canvas, analyser, mode = 'frequency') => {
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
canvas.width = bufferLength;
canvas.height = 256;
const ctx = canvas.getContext('2d');
const barWidth = 1;
const barDistance = 1.375;
const paintCanvas = () => {
if (mode === 'waveform') {
analyser.getByteTimeDomainData(dataArray);
} else {
analyser.getByteFrequencyData(dataArray);
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
dataArray.forEach((item, index) => {
const barHeight = item * 3 / 4;
if (mode === 'waveform') {
ctx.fillStyle = 'rgba(255, 170, 0, 1)';
ctx.fillRect(index, canvas.height - barHeight, barWidth, 1);
} else {
ctx.fillStyle = `rgba(255, ${255 - item}, 0, ${1 - (item / canvas.height)})`;
ctx.fillRect(index * barDistance, canvas.height, barWidth, 0 - barHeight);
}
});
window.requestAnimationFrame(paintCanvas);
};
paintCanvas();
};
const connect = (audio, canvas) => {
try {
const AudioContext = window.AudioContext;
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
const source = audioContext.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(audioContext.destination);
drawAnalyser(canvas, analyser);
} catch (error) {
}
};
export const attachEvents = () => {
document.addEventListener('click', handleClickBody);
};
export const unattachEvents = () => {
document.removeEventListener('click', handleClickBody);
};
const handleClickBody = e => {
connect(document.querySelector('.audio'), document.querySelector('.canvas'));
unattachEvents();
};
const Player = ({ initialTrack, trackList }) => {
const [currentTrack, setCurrentTrack] = useState(initialTrack);
useEffect(() => {
loadAudio(initialTrack);
if (!isTouch()) {
attachEvents();
return unattachEvents;
}
}, [initialTrack]);
useEffect(() => {
const handleKeydown = e => {
captureKeys(e, getAudioElement());
};
document.addEventListener('keydown', handleKeydown);
return () => {
document.removeEventListener('keydown', handleKeydown);
};
});
const handleAudioEnded = () => {
const nextTrack = getNextTrack(trackList, currentTrack);
loadAudio(nextTrack, true);
setCurrentTrack(nextTrack);
};
return (
<>
<canvas className="canvas" />
<div className="controls">
<audio
className="audio"
crossOrigin="anonymous"
controls
onEnded={handleAudioEnded}
/>
</div>
</>
);
};
Player.propTypes = {
initialTrack: PropTypes.object,
trackList: PropTypes.array,
};
export default Player;
Thanks in advance.
I'm trying to receive fog effect in React. The main idea is that I have two components: first component handles with updating of coordinates of clouds and their velocity, the second component is responsible for one cloud. I have problem with moving of clouds, if I don't clear canvas I can see track of every cloud, if I apply canvas.clear I can't see anything. Do you have any tip, where I should place clear canvas.clear or do you have other ideas?
The first component:
import React from 'react';
import styled from 'styled-components';
import FogPiece from './Fog-Piece.jsx';
const CanvasContext = React.createContext();
const FogDiv = styled.div`
position: absolute;
width:100vw;
height:100vh;
`
class Fog extends React.Component{
constructor(props){
super(props);
this.canvas = React.createRef();
this.state = {
ctx: null,
parameters:[],
screenWidth : 0,
screenHeight: 0,
}
}
componentDidMount = () => {
Promise.all(this.newCoordinates()).then((paramArray) =>{
this.setState({
ctx: this.canvas.current.getContext('2d'),
screenWidth: this.canvas.current.parentNode.getBoundingClientRect().width,
screenHeight: this.canvas.current.parentNode.getBoundingClientRect().height,
parameters: paramArray
});
window.requestAnimationFrame(this.update)
})
}
newCoordinates = () => {
return(Array.from(Array(this.props.density).keys()).map(elem =>{
return new Promise (resolve => {
const params = {
x: this.random(0,this.state.screenWidth),
y: this.random(0,this.state.screenHeight),
velocityX: this.random(-this.props.maxVelocity, this.props.maxVelocity),
velocityY: this.random(-this.props.maxVelocity, this.props.maxVelocity)
}
resolve(params)
})
}))
}
updateCoordinates = () => {
return(this.state.parameters.map(elem =>{
return new Promise (resolve => {
elem = this.ifCross(elem.x, elem.y, elem.velocityX, elem.velocityY);
const params = {
x: elem.x + elem.velocityX,
y: elem.y + elem.velocityY,
velocityX: elem.velocityX,
velocityY: elem.velocityY
}
resolve(params)
})
}))
}
random = (min,max) => {
return Math.random()*(max - min) + min
}
ifCross = (x,y, velocityX, velocityY) => {
if (x > this.state.screenWidth){
x = this.state.screenWidth
velocityX = - velocityX
}
if (x < 0){
x = 0
velocityX = - velocityX
}
if (y > this.state.screenHeight){
y = this.state.screenHeight
velocityY = - velocityY
}
if (y < 0){
y = 0
velocityY = - velocityY
}
return {x:x, y:y, velocityX:velocityX, velocityY:velocityY }
}
update = () => {
Promise.all(this.updateCoordinates()).then((paramArray) =>{
//here is the problem
// this.state.ctx.clearRect(0,0,this.state.screenWidth, this.state.screenHeight)
this.setState({
parameters: paramArray,
});
window.requestAnimationFrame(this.update)
})
}
render(){
return(
<FogDiv>
<canvas width={this.state.screenWidth} height={this.state.screenHeight} ref = {this.canvas} >
{this.state.ctx && (
<CanvasContext.Provider value = {this.state.ctx}>
{this.state.parameters.map(param =>(
<FogPiece
x = {param.x}
y = {param.y}
/>
))}
</CanvasContext.Provider>
)}
</canvas>
</FogDiv>
)
}
}
export default Fog;
export {
CanvasContext
}
the second one:
import React from 'react';
import styled from 'styled-components';
import {CanvasContext} from './Fog.jsx';
class FogPiece extends React.Component{
constructor(props){
super(props);
this.state = {
image:'https://media.istockphoto.com/vectors/sample-red-square-grunge-textured-isolated-stamp-vector-id471401412',
}
}
random(min,max){
return Math.random()*(max - min) + min
}
render(){
return(
<CanvasContext.Consumer>
{ctx => {
console.log("x", "y", this.props)
const img = new Image();
img.src = this.state.image;
img.onload = () => {
ctx.drawImage(img,
this.props.x,
this.props.y,
40,
40)
}
}}
</CanvasContext.Consumer>
)
}
}
export default FogPiece;