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

View File

@ -1,7 +1,10 @@
export function Toggle() {
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" />
</button>
</>

View File

@ -72,6 +72,10 @@ a {
}
}
p a {
text-decoration: underline;
}
.dinkus {
display: block;
text-align: center;
@ -89,10 +93,10 @@ ul {
}
}
#toggle-physics {
#toggle-physics,
#toggle-canvas {
position: absolute;
z-index: 99999;
top: 10px;
right: 10px;
width: 50px;
height: 50px;
@ -108,6 +112,20 @@ ul {
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) {
main {
@ -140,16 +158,11 @@ ul {
}
}
.fade-out {
.transparent {
opacity: 0 !important;
transition: opacity 0.5s ease-in-out;
transition-delay: 0.25s;
/* visibility: hidden; */
/* display: none; */
transition: opacity 0.25s ease-in-out;
}
.fade-in {
opacity: 1;
transition: opacity 0.5s ease-in-out;
/* visibility: visible; */
.hidden {
display: none;
}

View File

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

View File

@ -1,17 +1,48 @@
import { TLUnknownShape, useEditor } from "@tldraw/tldraw";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { usePhysicsSimulation } from "./simulation";
export const SimController = ({ shapes }: { shapes: TLUnknownShape[] }) => {
const editor = useEditor();
const [isPhysicsActive, setIsPhysicsActive] = useState(false);
const { addShapes, destroy } = usePhysicsSimulation(editor);
useEffect(() => {
editor.createShapes(shapes)
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 (<></>);
};

View File

@ -9,8 +9,6 @@ type BodyWithShapeData = RAPIER.RigidBody & {
};
type RigidbodyLookup = { [key: TLShapeId]: RAPIER.RigidBody };
const START_DELAY = 500;
export class PhysicsWorld {
private editor: Editor;
private world: RAPIER.World;
@ -30,8 +28,6 @@ export class PhysicsWorld {
public start() {
this.world = new RAPIER.World(GRAVITY);
this.addShapes(this.editor.getCurrentPageShapes());
const simLoop = () => {
this.world.step();
this.updateCharacterControllers();
@ -50,7 +46,10 @@ export class PhysicsWorld {
}
public addShapes(shapes: TLShape[]) {
console.log('adding shapesss');
for (const shape of shapes) {
console.log('shape');
if ('color' in shape.props && shape.props.color === "violet") {
this.createCharacter(shape as TLGeoShape);
continue;
@ -152,8 +151,9 @@ export class PhysicsWorld {
const rigidbody = this.createRigidbody(drawShape);
const drawnGeo = this.editor.getShapeGeometry(drawShape);
const verts = drawnGeo.vertices;
const isRb =
"color" in drawShape.props && isRigidbody(drawShape.props.color);
// const isRb =
// "color" in drawShape.props && isRigidbody(drawShape.props.color);
const isRb = true;
verts.forEach((point) => {
if (isRb) this.createColliderAtPoint(point, drawShape, rigidbody);
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));
useEffect(() => {
if (enabled) {
setTimeout(() => sim.current.start(), START_DELAY);
return () => sim.current.stop();
}
}, [enabled, sim]);
sim.current.start()
}, []);
useEffect(() => {
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 {
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({
id: createShapeId(),
type: 'geo',
x: 0,
x: -extend,
y: window.innerHeight,
props: {
w: window.innerWidth,
w: window.innerWidth + (extend * 2),
h: 50,
color: 'grey',
fill: 'solid'