React 不会在 useEffect 中的画布上绘制任何内容

问题描述 投票:0回答:1

我用 Matter.js 构建了一个 React 网站。我正在使用

useEffect
钩子通过 Matter.js 将内容渲染到画布上(我在here找到了大部分代码)。但是,当我尝试在画布上绘制其他任何内容时,什么也没有出现。与 Matter.js 相关的所有内容都有效。

const scene = useRef()
const isDragging = useRef(false)
const engine = useRef(Engine.create())

useEffect(() => {
  const cw = 1100;
  const ch = 700;
  const render = Render.create({
    canvas: scene.current,
    engine: engine.current,
    options: {
      width: cw,
      height: ch,
      wireframes: false,
      background: 'transparent'
    }
  })

  console.log("gravity " + engine.current.gravity.y + "x : " + engine.current.gravity.x)

  let mouse = Mouse.create(render.canvas);
  let mouseConstraint = MouseConstraint.create(engine.current, {
    mouse: mouse,
    constraint: {
      render: {
        visible: false
      }
    }
  })
  render.mouse = mouse;

  World.add(engine.current.world, [
    Bodies.rectangle(cw / 2, 0, cw, 20, {
      isStatic: true,
      density: 1
    }),
    Bodies.rectangle(cw / 2, ch, cw, 20, {
      isStatic: true,
      density: 1
    }),
    Bodies.rectangle(0, ch / 2, 20, ch, {
      isStatic: true,
      density: 1
    }),
    Bodies.rectangle(cw, ch / 2, 20, ch, {
      isStatic: true,
      density: 1
    }),

    mouseConstraint,
  ])

  Runner.run(engine.current)
  Render.run(render)

  Events.on(mouseConstraint, "mousedown", function(event) {
    handleSelections(mouseConstraint.body)
  })
  Events.on(mouseConstraint, "startdrag", function(event) {
    isDragging.current = true
  })
  Events.on(mouseConstraint, "enddrag", function() {
    isDragging.current = false
  })
  Events.on(engine.current, 'afterUpdate', function() {
    countTen.current = countTen.current + 1;

    if (countTen.current == 10) {
      countTen.current = 0;

      if (selectedObjectRef.current != null) {

        setGraphingPos(selectedObjectRef.current.velocity.y * -1);

        setTicks(ticks + 1)
      }
    }
  })

  // ******************* This part doesn't work **************************
  scene.current.getContext('2d').beginPath();
  scene.current.getContext('2d').arc(100, 100, 20, 0, 2 * Math.PI, false);
  scene.current.getContext('2d').fillStyle = 'red';
  scene.current.getContext('2d').fill();
  // **********************************************************************
  return () => {
    Render.stop(render)
    World.clear(engine.current.world)
    Engine.clear(engine.current)
    render.canvas.remove()
    render.canvas = null
    render.context = null
    render.textures = {}
  }
}, [])
<canvas ref={scene} onClick={ handleMouseDown} className='main-canvas'></canvas>

非常感谢任何形式的帮助!

javascript html reactjs react-hooks matter.js
1个回答
0
投票

目前,您正在预先在 MJS 每帧擦拭的同一画布上绘制一次。因此,您绘制圆圈,然后 MJS 在渲染第一帧时立即擦除画布。当引擎和渲染器继续运行时,您永远不会尝试再次绘制。

我看到了一些解决方案(至少!)。哪个最合适取决于您的具体用例。

  1. afterRender
    回调内的每个帧上为
    render
    对象
    绘制。这是您现在所处的最简单、最直接的解决方案,我在下面提供了一个示例。
  2. 在您提供给 MJS 的画布上添加
  3. 第二个透明画布。这是通过绝对定位完成的。现在你可以在这个覆盖层中做任何你想做的事情,而不受 MJS 的干扰。
  4. 无头运行 MJS,这为您提供最大程度的控制,因为 MJS 内置渲染器
  5. 主要用于原型制作(它是物理引擎,而不是图形引擎)。一旦你无头了,你就可以随时随地渲染任何你想要的东西。我的其他答案中有很多无头渲染的演示,包括 plain DOM 以及 Reactp5.js
顺便说一句,这似乎与 React 或

useEffect

 没有任何关系;即使您在 vanilla JS 中注入普通画布,也会出现同样的问题。

另外,请记住,无论你画什么,除了恰好在同一个画布上之外,都与 MJS 完全无关。不要指望物理学会对其起作用,除非您使用 MJS 知道的物体坐标。

虽然您尚未提供完整的组件,但这是上述

afterRender

 方法的最小概念验证:

const {useEffect, useRef} = React; const {Bodies, Engine, Events, Render, Runner, Composite} = Matter; const Scene = () => { const canvasRef = useRef(); useEffect(() => { const cw = 200; const ch = 200; const engine = Engine.create(); const ctx = canvasRef.current.getContext("2d"); const render = Render.create({ canvas: canvasRef.current, engine, options: { width: cw, height: ch, wireframes: false, background: "transparent", } }); const handleAfterRender = () => { const x = Math.cos(Date.now() / 300) * 80 + 100; const y = Math.sin(Date.now() / 299) * 80 + 100; ctx.beginPath(); ctx.arc(x, y, 20, 0, 2 * Math.PI); ctx.fillStyle = "red"; ctx.fill(); }; Events.on(render, "afterRender", handleAfterRender); Composite.add(engine.world, [ Bodies.rectangle(cw / 2, 0, cw, 20, { isStatic: true, density: 1, }), Bodies.rectangle(cw / 2, ch, cw, 20, { isStatic: true, density: 1, }), Bodies.rectangle(0, ch / 2, 20, ch, { isStatic: true, density: 1, }), Bodies.rectangle(cw, ch / 2, 20, ch, { isStatic: true, density: 1, }), ]); Runner.run(engine); Render.run(render); return () => { Events.off("afterRender", handleAfterRender); Render.stop(render); Composite.clear(engine.world); Engine.clear(engine); render.canvas.remove(); render.canvas = null; render.context = null; render.textures = {}; }; }, []); return <canvas ref={canvasRef}></canvas>; }; const root = document.querySelector("#app"); ReactDOM.createRoot(root).render(<Scene />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.min.js"></script>
<div id="app"></div>

© www.soinside.com 2019 - 2024. All rights reserved.