Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React中使用声明式操作canvas #21

Open
OPY-bbt opened this issue Nov 23, 2019 · 0 comments
Open

React中使用声明式操作canvas #21

OPY-bbt opened this issue Nov 23, 2019 · 0 comments
Labels

Comments

@OPY-bbt
Copy link
Owner

OPY-bbt commented Nov 23, 2019

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;
@OPY-bbt OPY-bbt added the react label Nov 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant