Separate canvas and physics buttons
This commit is contained in:
parent
b0bcda407a
commit
e063e63aa1
24
src/App.tsx
24
src/App.tsx
|
|
@ -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}</>)
|
||||||
}
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -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; */
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
@ -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 (<></>);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -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 |
|
|
@ -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'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue