Open OPY-bbt opened 4 years ago
import React, { useRef, useState, useEffect } from 'react'; const { Provider, Consumer } = React.createContext(); function App() { const mouseIsDown = useRef(false); const initialX = useRef(null); const initialY = useRef(null); const downX = useRef(null); const downY = useRef(null); const [pos, setPos] = useState({ x: 5, y: 5 }); function handleMouseDown(ev) { mouseIsDown.current = true; initialX.current = pos.x; initialY.current = pos.y; downX.current = ev.clientX; downY.current = ev.clientY; } function handleMouseMove(ev) { if (mouseIsDown.current) { setPos({ x: initialX.current + ev.clientX - downX.current, y: initialY.current + ev.clientY - downY.current, }); } } function handleMouseUp() { mouseIsDown.current = false; } return ( <Canvas width={400} height={600} > <Box width={100} height={100} x={pos.x} y={pos.y} mousedown={handleMouseDown} mousemove={handleMouseMove} mouseup={handleMouseUp} /> </Canvas> ); } function Canvas(props) { const canvasRef = useRef(null); const elements = useRef({}); useEffect(function() { draw(); }); function draw() { const ca = canvasRef.current; const ctx = ca.getContext('2d'); ctx.clearRect(0,0,props.width, props.height); Object.values(elements.current).forEach(function(element) { element.draw(ctx); }); } function respondToEvent(ev) { Object.values(elements.current).forEach(function(element) { const { props } = element; if (ev.type in props) { const pos = relativeMousePos(ev); const isInside = getIsInside(pos, props); if (isInside) { props[ev.type](ev); } } }); } function getIsInside(pos, props) { const { x, y } = pos; const { x: ex, y: ey, width, height } = props; return x >= ex && x <= ex + width && y>= ey && y <= ey + height; } function relativeMousePos(ev) { const ca = canvasRef.current; const rect = ca.getBoundingClientRect(); return { x: ev.clientX - rect.left, y: ev.clientY - rect.top, } } function updateElement(payload) { elements.current = { ...elements.current, [payload.id]: payload, }; } const {width, height} = props; return ( <Provider value={{updateElement}}> <canvas ref={canvasRef} width={width} height={height} onMouseDown={respondToEvent} onMouseMove={respondToEvent} onMouseUp={respondToEvent} >{props.children}</canvas> </Provider> ); } const genID = () => '_' + Math.random() .toString(36) .substr(2, 9); function Box(props) { return ( <Consumer> { ({updateElement}) => ( <BoxElement {...props} updateElement={updateElement} ></BoxElement> ) } </Consumer> ); } function BoxElement(props) { const id = useRef(genID()); function draw(ctx) { const { color, height, width, x, y } = props; ctx.fillStyle = color || 'green'; ctx.fillRect(x, y, width, height); ctx.save(); ctx.beginPath(); ctx.rect(x, y, width, height); ctx.restore(); } useEffect(function() { props.updateElement({ id: id.current, draw, props, }); }); return null; } export default App;