Separate canvas and physics buttons

This commit is contained in:
Orion Reed 2024-03-25 04:25:31 -07:00
parent b0bcda407a
commit e063e63aa1
9 changed files with 127 additions and 46 deletions

View File

@ -1,11 +1,11 @@
import "@tldraw/tldraw/tldraw.css"; import "@tldraw/tldraw/tldraw.css";
import "@/css/style.css" import "@/css/style.css"
import React, { } from "react"; import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import { Default } from "@/components/Default"; import { Default } from "@/components/Default";
import { Canvas } from "@/components/Canvas"; import { Canvas } from "@/components/Canvas";
import { Toggle } from "@/components/Toggle"; import { Toggle } from "@/components/Toggle";
import { usePhysics } from "@/hooks/usePhysics" import { useCanvas } from "@/hooks/useCanvas"
import { createShapes } from "@/utils"; import { createShapes } from "@/utils";
import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Contact } from "@/components/Contact"; import { Contact } from "@/components/Contact";
@ -28,12 +28,26 @@ function App() {
}; };
function Home() { function Home() {
const { isPhysicsEnabled, elementsInfo, fadeClass } = usePhysics(); const { isCanvasEnabled, elementsInfo } = useCanvas();
const shapes = createShapes(elementsInfo) const shapes = createShapes(elementsInfo)
const [isEditorMounted, setIsEditorMounted] = useState(false);
useEffect(() => {
const handleEditorDidMount = () => {
setIsEditorMounted(true);
};
window.addEventListener('editorDidMountEvent', handleEditorDidMount);
return () => {
window.removeEventListener('editorDidMountEvent', handleEditorDidMount);
};
}, []);
return ( return (
<><Toggle /> <><Toggle />
<div style={{ zIndex: 999999 }} className={`default-component ${fadeClass}`}> <div style={{ zIndex: 999999 }} className={`${isCanvasEnabled && isEditorMounted ? 'transparent' : ''}`}>
{<Default />} {<Default />}
</div> </div>
{isPhysicsEnabled && elementsInfo.length > 0 ? <Canvas shapes={shapes} /> : null}</>) {isCanvasEnabled && elementsInfo.length > 0 ? <Canvas shapes={shapes} /> : null}</>)
} }

View File

@ -29,6 +29,9 @@ export function Canvas({ shapes }: { shapes: TLShape[]; }) {
<Tldraw <Tldraw
components={components} components={components}
shapeUtils={[HTMLShapeUtil]} shapeUtils={[HTMLShapeUtil]}
onMount={() => {
window.dispatchEvent(new CustomEvent('editorDidMountEvent'));
}}
> >
<SimController shapes={shapes} /> <SimController shapes={shapes} />
</Tldraw> </Tldraw>

View File

@ -1,7 +1,10 @@
export function Toggle() { export function Toggle() {
return ( return (
<> <>
<button id="toggle-physics" onClick={() => window.dispatchEvent(new CustomEvent('togglePhysicsEvent'))}> <button id="toggle-canvas" onClick={() => window.dispatchEvent(new CustomEvent('toggleCanvasEvent'))}>
<img src="/canvas-button.svg" alt="Toggle Canvas" />
</button>
<button id="toggle-physics" className="hidden" onClick={() => window.dispatchEvent(new CustomEvent('togglePhysicsEvent'))}>
<img src="/gravity-button.svg" alt="Toggle Physics" /> <img src="/gravity-button.svg" alt="Toggle Physics" />
</button> </button>
</> </>

View File

@ -72,6 +72,10 @@ a {
} }
} }
p a {
text-decoration: underline;
}
.dinkus { .dinkus {
display: block; display: block;
text-align: center; text-align: center;
@ -89,10 +93,10 @@ ul {
} }
} }
#toggle-physics { #toggle-physics,
#toggle-canvas {
position: absolute; position: absolute;
z-index: 99999; z-index: 99999;
top: 10px;
right: 10px; right: 10px;
width: 50px; width: 50px;
height: 50px; height: 50px;
@ -108,6 +112,20 @@ ul {
height: 100%; height: 100%;
} }
} }
#toggle-canvas {
top: 10px;
& img {
width: 50%;
height: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
#toggle-physics {
top: 60px;
}
@media (max-width: 600px) { @media (max-width: 600px) {
main { main {
@ -140,16 +158,11 @@ ul {
} }
} }
.fade-out { .transparent {
opacity: 0 !important; opacity: 0 !important;
transition: opacity 0.5s ease-in-out; transition: opacity 0.25s ease-in-out;
transition-delay: 0.25s;
/* visibility: hidden; */
/* display: none; */
} }
.fade-in { .hidden {
opacity: 1; display: none;
transition: opacity 0.5s ease-in-out;
/* visibility: visible; */
} }

View File

@ -9,33 +9,39 @@ interface ElementInfo {
html: string; html: string;
} }
export function usePhysics() { export function useCanvas() {
const [isPhysicsEnabled, setIsPhysicsEnabled] = useState(false); const [isCanvasEnabled, setIsCanvasEnabled] = useState(false);
const [elementsInfo, setElementsInfo] = useState<ElementInfo[]>([]); const [elementsInfo, setElementsInfo] = useState<ElementInfo[]>([]);
const [fadeClass, setFadeClass] = useState('');
useEffect(() => { useEffect(() => {
const togglePhysics = async () => { const toggleCanvas = async () => {
if (!isPhysicsEnabled) { if (!isCanvasEnabled) {
const info = await gatherElementsInfo(); const info = await gatherElementsInfo();
setElementsInfo(info); setElementsInfo(info);
setIsPhysicsEnabled(true); setIsCanvasEnabled(true);
setFadeClass('fade-out'); document.getElementById('toggle-physics')?.classList.remove('hidden');
} else { } else {
setIsPhysicsEnabled(false);
setElementsInfo([]); setElementsInfo([]);
setFadeClass(''); setIsCanvasEnabled(false);
document.getElementById('toggle-physics')?.classList.add('hidden');
} }
}; };
// const enableCanvas = async () => {
// const info = await gatherElementsInfo();
// setElementsInfo(info);
// setIsCanvasEnabled(true);
// };
window.addEventListener('togglePhysicsEvent', togglePhysics); window.addEventListener('toggleCanvasEvent', toggleCanvas);
// window.addEventListener('togglePhysicsEvent', enableCanvas);
return () => { return () => {
window.removeEventListener('togglePhysicsEvent', togglePhysics); window.removeEventListener('toggleCanvasEvent', toggleCanvas);
// window.removeEventListener('togglePhysicsEvent', enableCanvas);
}; };
}, [isPhysicsEnabled]); }, [isCanvasEnabled]);
return { isPhysicsEnabled, elementsInfo, fadeClass }; return { isCanvasEnabled, elementsInfo };
} }
async function gatherElementsInfo() { async function gatherElementsInfo() {

View File

@ -1,17 +1,48 @@
import { TLUnknownShape, useEditor } from "@tldraw/tldraw"; import { TLUnknownShape, useEditor } from "@tldraw/tldraw";
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { usePhysicsSimulation } from "./simulation"; import { usePhysicsSimulation } from "./simulation";
export const SimController = ({ shapes }: { shapes: TLUnknownShape[] }) => { export const SimController = ({ shapes }: { shapes: TLUnknownShape[] }) => {
const editor = useEditor(); const editor = useEditor();
const [isPhysicsActive, setIsPhysicsActive] = useState(false);
const { addShapes, destroy } = usePhysicsSimulation(editor);
useEffect(() => { useEffect(() => {
editor.createShapes(shapes) editor.createShapes(shapes)
return () => { editor.deleteShapes(editor.getCurrentPageShapes()) } return () => { editor.deleteShapes(editor.getCurrentPageShapes()) }
}, []); }, []);
useEffect(() => {
const togglePhysics = () => {
setIsPhysicsActive((currentIsPhysicsActive) => {
if (currentIsPhysicsActive) {
console.log('destroy');
destroy();
return false;
} else {
console.log('activate');
return true;
}
});
};
const { addShapes } = usePhysicsSimulation(editor, true); // Listen for the togglePhysicsEvent to enable/disable physics simulation
window.addEventListener('togglePhysicsEvent', togglePhysics);
return () => {
window.removeEventListener('togglePhysicsEvent', togglePhysics);
};
}, []);
useEffect(() => {
if (isPhysicsActive) {
console.log('adding shapes');
addShapes(editor.getCurrentPageShapes()); // Activate physics simulation
} else {
destroy(); // Deactivate physics simulation
}
}, [isPhysicsActive, addShapes, shapes]);
return (<></>); return (<></>);
}; };

View File

@ -9,8 +9,6 @@ type BodyWithShapeData = RAPIER.RigidBody & {
}; };
type RigidbodyLookup = { [key: TLShapeId]: RAPIER.RigidBody }; type RigidbodyLookup = { [key: TLShapeId]: RAPIER.RigidBody };
const START_DELAY = 500;
export class PhysicsWorld { export class PhysicsWorld {
private editor: Editor; private editor: Editor;
private world: RAPIER.World; private world: RAPIER.World;
@ -30,8 +28,6 @@ export class PhysicsWorld {
public start() { public start() {
this.world = new RAPIER.World(GRAVITY); this.world = new RAPIER.World(GRAVITY);
this.addShapes(this.editor.getCurrentPageShapes());
const simLoop = () => { const simLoop = () => {
this.world.step(); this.world.step();
this.updateCharacterControllers(); this.updateCharacterControllers();
@ -50,7 +46,10 @@ export class PhysicsWorld {
} }
public addShapes(shapes: TLShape[]) { public addShapes(shapes: TLShape[]) {
console.log('adding shapesss');
for (const shape of shapes) { for (const shape of shapes) {
console.log('shape');
if ('color' in shape.props && shape.props.color === "violet") { if ('color' in shape.props && shape.props.color === "violet") {
this.createCharacter(shape as TLGeoShape); this.createCharacter(shape as TLGeoShape);
continue; continue;
@ -152,8 +151,9 @@ export class PhysicsWorld {
const rigidbody = this.createRigidbody(drawShape); const rigidbody = this.createRigidbody(drawShape);
const drawnGeo = this.editor.getShapeGeometry(drawShape); const drawnGeo = this.editor.getShapeGeometry(drawShape);
const verts = drawnGeo.vertices; const verts = drawnGeo.vertices;
const isRb = // const isRb =
"color" in drawShape.props && isRigidbody(drawShape.props.color); // "color" in drawShape.props && isRigidbody(drawShape.props.color);
const isRb = true;
verts.forEach((point) => { verts.forEach((point) => {
if (isRb) this.createColliderAtPoint(point, drawShape, rigidbody); if (isRb) this.createColliderAtPoint(point, drawShape, rigidbody);
else this.createColliderAtPoint(point, drawShape); else this.createColliderAtPoint(point, drawShape);
@ -375,15 +375,12 @@ export class PhysicsWorld {
} }
} }
export function usePhysicsSimulation(editor: Editor, enabled: boolean) { export function usePhysicsSimulation(editor: Editor) {
const sim = useRef<PhysicsWorld>(new PhysicsWorld(editor)); const sim = useRef<PhysicsWorld>(new PhysicsWorld(editor));
useEffect(() => { useEffect(() => {
if (enabled) { sim.current.start()
setTimeout(() => sim.current.start(), START_DELAY); }, []);
return () => sim.current.stop();
}
}, [enabled, sim]);
useEffect(() => { useEffect(() => {
sim.current.setEditor(editor); sim.current.setEditor(editor);
@ -392,5 +389,10 @@ export function usePhysicsSimulation(editor: Editor, enabled: boolean) {
// Return any values or functions that the UI components might need // Return any values or functions that the UI components might need
return { return {
addShapes: (shapes: TLShape[]) => sim.current.addShapes(shapes), addShapes: (shapes: TLShape[]) => sim.current.addShapes(shapes),
destroy: () => {
sim.current.stop()
sim.current = new PhysicsWorld(editor); // Replace with a new instance
sim.current.start()
}
}; };
} }

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by Pixelmator Pro 3.5.6 -->
<svg width="450" height="450" viewBox="0 0 450 450" xmlns="http://www.w3.org/2000/svg">
<path id="Rounded-Rectangle" fill="none" stroke="#000000" stroke-width="34" stroke-linecap="round"
stroke-linejoin="round"
d="M 112.785156 427.964844 C 79.383881 427.964844 62.682236 427.963867 50.160156 420.951172 C 41.310253 415.994995 34.005013 408.689758 29.048828 399.839844 C 22.036131 387.317749 22.035156 370.616119 22.035156 337.214844 L 22.035156 298.828125 L 40.128906 298.828125 L 40.128906 350.470703 C 40.128906 372.333344 40.128616 383.264679 44.71875 391.460938 C 47.962795 397.253601 52.746403 402.037201 58.539063 405.28125 C 66.735329 409.871368 77.666649 409.871094 99.529297 409.871094 L 151.171875 409.871094 L 151.171875 427.964844 L 112.785156 427.964844 Z M 297.949219 427.964844 L 297.949219 409.871094 L 349.599609 409.871094 C 371.462219 409.871094 382.393555 409.871399 390.589844 405.28125 C 396.382538 402.037201 401.166107 397.253601 404.410156 391.460938 C 409.000305 383.264679 409 372.333344 409 350.470703 L 409 298.828125 L 427.09375 298.828125 L 427.09375 337.214844 C 427.09375 370.616119 427.092804 387.317749 420.080078 399.839844 C 415.123901 408.689758 407.818665 415.994995 398.96875 420.951172 C 386.446625 427.963867 369.745026 427.964844 336.34375 427.964844 L 297.949219 427.964844 Z M 22.035156 152.050781 L 22.035156 113.65625 C 22.035156 80.254974 22.036131 63.553375 29.048828 51.03125 C 34.005013 42.181335 41.310253 34.876129 50.160156 29.919922 C 62.682236 22.907196 79.383881 22.90625 112.785156 22.90625 L 151.171875 22.90625 L 151.171875 41 L 99.529297 41 C 77.666649 41 66.735329 40.999695 58.539063 45.589844 C 52.746407 48.833893 47.962795 53.617493 44.71875 59.410156 C 40.12862 67.606445 40.128906 78.537781 40.128906 100.400391 L 40.128906 152.050781 L 22.035156 152.050781 Z M 409 152.050781 L 409 100.400391 C 409 78.537781 409.000305 67.606445 404.410156 59.410156 C 401.166107 53.617462 396.382507 48.833893 390.589844 45.589844 C 382.393555 40.999695 371.462219 41 349.599609 41 L 297.949219 41 L 297.949219 22.90625 L 336.34375 22.90625 C 369.745026 22.90625 386.446625 22.907196 398.96875 29.919922 C 407.818665 34.876099 415.123871 42.181335 420.080078 51.03125 C 427.092804 63.553375 427.09375 80.254974 427.09375 113.65625 L 427.09375 152.050781 L 409 152.050781 Z" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -13,13 +13,15 @@ export function createShapes(elementsInfo: any) {
} }
})); }));
const extend = 150;
shapes.push({ shapes.push({
id: createShapeId(), id: createShapeId(),
type: 'geo', type: 'geo',
x: 0, x: -extend,
y: window.innerHeight, y: window.innerHeight,
props: { props: {
w: window.innerWidth, w: window.innerWidth + (extend * 2),
h: 50, h: 50,
color: 'grey', color: 'grey',
fill: 'solid' fill: 'solid'