React 不会在 useEffect 的 canvas 中绘制任何东西
React won't draw anything in the canvas in useEffect
我用 matter.js 构建了一个 React 网站。我正在使用 useEffect
挂钩将内容渲染到 canvas 和 matter.js(我发现大部分代码是 here)。但是,当我尝试向 canvas 绘制任何其他内容时,什么也没有出现。所有 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>
非常感谢任何形式的帮助!
目前,您在 MJS wipes per frame 的同一个 canvas 上预先绘制了一次。因此,您绘制圆圈,然后 MJS 在呈现第一帧时立即擦除 canvas。随着引擎和渲染器 运行 向前,您永远不会尝试再次绘制。
我看到了一些解决方案(至少!)。哪个最合适取决于您的具体用例。
- 在
afterRender
callback for the render
object 内的每一帧上绘制 canvas。这是您现在所在位置的最简单、最直接的解决方案,我在下面提供了一个示例。
- 在您提供给 MJS 的那个之上添加一个 second transparent canvas。这是通过绝对定位完成的。现在你可以在这个叠加层中做任何你想做的事情而不受 MJS 的干扰。
- 运行 MJS headlessly,这给了你最大的控制权,因为 MJS built-in 渲染器是 mostly intended for protoyping (it's a physics engine, not a graphics engine). Once you're headless, you can render whatever you want, whenever you want, however you want. I have many demos of headless rendering in my other answers, both to the plain DOM and with React and p5.js.
顺便说一句,这似乎与 React 或 useEffect
没有任何关系;即使你在 vanilla JS 中注入一个普通的 canvas 也会出现同样的问题。
此外,请记住,无论您绘制什么,除了碰巧在同一个 canvas 上之外,都与 MJS 完全无关。不要指望物理学能对它起作用,除非你使用的是 body MJS 知道的坐标。
虽然您没有提供完整的组件,但这里是上述 afterRender
方法的最小 proof-of-concept:
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",
}
});
Events.on(render, "afterRender", () => {
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();
});
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 () => {
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>;
};
ReactDOM.render(<Scene />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<div id="app"></div>
我用 matter.js 构建了一个 React 网站。我正在使用 useEffect
挂钩将内容渲染到 canvas 和 matter.js(我发现大部分代码是 here)。但是,当我尝试向 canvas 绘制任何其他内容时,什么也没有出现。所有 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>
非常感谢任何形式的帮助!
目前,您在 MJS wipes per frame 的同一个 canvas 上预先绘制了一次。因此,您绘制圆圈,然后 MJS 在呈现第一帧时立即擦除 canvas。随着引擎和渲染器 运行 向前,您永远不会尝试再次绘制。
我看到了一些解决方案(至少!)。哪个最合适取决于您的具体用例。
- 在
afterRender
callback for therender
object 内的每一帧上绘制 canvas。这是您现在所在位置的最简单、最直接的解决方案,我在下面提供了一个示例。 - 在您提供给 MJS 的那个之上添加一个 second transparent canvas。这是通过绝对定位完成的。现在你可以在这个叠加层中做任何你想做的事情而不受 MJS 的干扰。
- 运行 MJS headlessly,这给了你最大的控制权,因为 MJS built-in 渲染器是 mostly intended for protoyping (it's a physics engine, not a graphics engine). Once you're headless, you can render whatever you want, whenever you want, however you want. I have many demos of headless rendering in my other answers, both to the plain DOM and with React and p5.js.
顺便说一句,这似乎与 React 或 useEffect
没有任何关系;即使你在 vanilla JS 中注入一个普通的 canvas 也会出现同样的问题。
此外,请记住,无论您绘制什么,除了碰巧在同一个 canvas 上之外,都与 MJS 完全无关。不要指望物理学能对它起作用,除非你使用的是 body MJS 知道的坐标。
虽然您没有提供完整的组件,但这里是上述 afterRender
方法的最小 proof-of-concept:
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",
}
});
Events.on(render, "afterRender", () => {
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();
});
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 () => {
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>;
};
ReactDOM.render(<Scene />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<div id="app"></div>