get routing working, add index for poetry, list first 6 poems on index, let each take you to their page with the poem's content
This commit is contained in:
41
src/lib/components/scenes/app/App.svelte
Normal file
41
src/lib/components/scenes/app/App.svelte
Normal file
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { T, useTask, useThrelte } from '@threlte/core';
|
||||
import { World } from '@threlte/rapier';
|
||||
import { Inspector } from 'three-inspect';
|
||||
import { dev } from '$app/environment';
|
||||
import SpaceSkysphere from './SpaceSkysphere.svelte';
|
||||
import { Group, type Object3DEventMap } from 'three';
|
||||
import DollyCam from './DollyCam.svelte';
|
||||
import Planet from '$lib/components/scenes/app/Planet.svelte';
|
||||
|
||||
const { invalidate } = useThrelte();
|
||||
let spaceObjects: Group<Object3DEventMap>;
|
||||
|
||||
useTask(
|
||||
'updateSpaceObjects',
|
||||
(delta) => {
|
||||
if (!spaceObjects) return;
|
||||
spaceObjects.rotation.y += 0.3 * delta;
|
||||
spaceObjects.position.y = Math.sin(spaceObjects.rotation.y);
|
||||
|
||||
invalidate();
|
||||
},
|
||||
{ autoInvalidate: false }
|
||||
);
|
||||
</script>
|
||||
|
||||
<World gravity={[0, 0, 0]}>
|
||||
<DollyCam />
|
||||
|
||||
<T.Group bind:ref={spaceObjects}>
|
||||
<SpaceSkysphere size={100} count={500} />
|
||||
|
||||
<Planet />
|
||||
|
||||
<slot />
|
||||
</T.Group>
|
||||
</World>
|
||||
|
||||
{#if false}
|
||||
<Inspector />
|
||||
{/if}
|
26
src/lib/components/scenes/app/CanvasContainer.svelte
Normal file
26
src/lib/components/scenes/app/CanvasContainer.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<!-- src/lib/CanvasLayout.svelte -->
|
||||
<script lang="ts">
|
||||
import { Canvas } from '@threlte/core';
|
||||
</script>
|
||||
|
||||
<div class="canvas">
|
||||
<Canvas>
|
||||
<slot />
|
||||
</Canvas>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgb(0, 36, 6);
|
||||
background: linear-gradient(180deg, rgba(0, 36, 6, 1) 0%, rgba(0, 0, 0, 1) 100%);
|
||||
position: absolute;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
44
src/lib/components/scenes/app/DollyCam.svelte
Normal file
44
src/lib/components/scenes/app/DollyCam.svelte
Normal file
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
import { T, useTask, useThrelte } from '@threlte/core';
|
||||
import { onMount } from 'svelte';
|
||||
import { PerspectiveCamera, Vector3 } from 'three';
|
||||
const { invalidate } = useThrelte();
|
||||
|
||||
let camLookatPosition: Vector3 = new Vector3();
|
||||
let camCurrentPosition: Vector3 = new Vector3();
|
||||
let camDamping: number = 1;
|
||||
let camera: PerspectiveCamera;
|
||||
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
// normalize the mouse position to [-1, 1], and smoothly interpolate the camera's lookAt position
|
||||
camLookatPosition.set(
|
||||
(event.clientX / window.innerWidth) * 2 - 1,
|
||||
-(event.clientY / window.innerHeight) * 2 + 1,
|
||||
0
|
||||
);
|
||||
};
|
||||
|
||||
useTask(
|
||||
'dollyCam',
|
||||
(delta) => {
|
||||
if (!camera) return;
|
||||
camCurrentPosition.lerp(camLookatPosition, camDamping * delta);
|
||||
camera.lookAt(camCurrentPosition);
|
||||
|
||||
if (camera && camCurrentPosition.distanceTo(camLookatPosition) > 0.1) {
|
||||
invalidate();
|
||||
}
|
||||
},
|
||||
{ autoInvalidate: false }
|
||||
);
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<T.PerspectiveCamera position.z={25} makeDefault fov={50} far={10000} bind:ref={camera} />
|
51
src/lib/components/scenes/app/MenuItem.svelte
Normal file
51
src/lib/components/scenes/app/MenuItem.svelte
Normal file
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { HTML } from '@threlte/extras';
|
||||
import { Attractor } from '@threlte/rapier';
|
||||
|
||||
export let position: [number, number, number] = [0, 0, 0];
|
||||
export let range: number = 100;
|
||||
export let clickHandler: (() => void) | undefined = undefined;
|
||||
export let htmlContent: HTMLElement | string = 'Hello!';
|
||||
export let href: string = '/';
|
||||
|
||||
let isHovering = false;
|
||||
let isPointerDown = false;
|
||||
|
||||
const onClick = () => {
|
||||
console.log('clicked!');
|
||||
if (clickHandler) {
|
||||
clickHandler();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<HTML position.x={position[0]} position.y={position[1]} position.z={position[2]}>
|
||||
<a
|
||||
{href}
|
||||
on:pointerenter={() => (isHovering = true)}
|
||||
on:pointerleave={() => {
|
||||
isPointerDown = false;
|
||||
isHovering = false;
|
||||
}}
|
||||
on:pointerdown={() => (isPointerDown = true)}
|
||||
on:pointerup={() => (isPointerDown = false)}
|
||||
on:pointercancel={() => {
|
||||
isPointerDown = false;
|
||||
isHovering = false;
|
||||
}}
|
||||
on:click={onClick}
|
||||
class="bg-green-700 px-3 py-3 text-white opacity-50 hover:opacity-90 active:opacity-100"
|
||||
style="transform: translate(-50%, 50%); display: block;"
|
||||
>
|
||||
{htmlContent}
|
||||
</a>
|
||||
</HTML>
|
||||
|
||||
<Attractor
|
||||
position.x={position[0]}
|
||||
position.y={position[1]}
|
||||
position.z={position[2]}
|
||||
{range}
|
||||
strength={isHovering ? 1 : 0}
|
||||
gravityType={'static'}
|
||||
/>
|
19
src/lib/components/scenes/app/Planet.svelte
Normal file
19
src/lib/components/scenes/app/Planet.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts" context="module">
|
||||
import { T } from '@threlte/core';
|
||||
import { Attractor, Collider, RigidBody } from '@threlte/rapier';
|
||||
import { MeshBasicMaterial, SphereGeometry } from 'three';
|
||||
const geometry = new SphereGeometry(1, 8, 4);
|
||||
const material = new MeshBasicMaterial({ color: '#339933', wireframe: true });
|
||||
const position: [number, number, number] = [0, 0, 0];
|
||||
const scale: number = 2;
|
||||
const offset = position.map((el) => el + 1.5);
|
||||
</script>
|
||||
|
||||
<Attractor {position} range={100} strength={0.3} gravityType={'linear'} />
|
||||
|
||||
<T.Group position.x={-offset[0]} position.y={-offset[1]} position.z={offset[2]}>
|
||||
<RigidBody linearDamping={0.3}>
|
||||
<Collider shape="ball" args={[scale]} mass={scale} />
|
||||
<T.Mesh scale={[scale, scale, scale]} {geometry} {material} />
|
||||
</RigidBody>
|
||||
</T.Group>
|
@@ -5,7 +5,7 @@
|
||||
import { Studio } from '@threlte/theatre';
|
||||
</script>
|
||||
|
||||
<Studio enabled={false} />
|
||||
<Studio enabled={dev} />
|
||||
|
||||
<Canvas>
|
||||
<Spinners />
|
||||
|
@@ -8,18 +8,18 @@
|
||||
|
||||
let sequence: SequenceController;
|
||||
|
||||
let box: Mesh;
|
||||
let ball: Mesh;
|
||||
let spotlight: SpotLight;
|
||||
let lastBoxPosition = new Vector3();
|
||||
let currentBoxPosition = new Vector3();
|
||||
let lastBallPosition = new Vector3();
|
||||
let currentBallPosition = new Vector3();
|
||||
let config = spinnersJson as IProjectConfig;
|
||||
|
||||
const boxMoved: any = (props: { position: { x: number; y: number; z: number } }) => {
|
||||
if (box && spotlight) {
|
||||
const ballMoved: any = (props: { position: { x: number; y: number; z: number } }) => {
|
||||
if (ball && spotlight) {
|
||||
const { x, y, z } = props.position;
|
||||
currentBoxPosition.set(x, y, z);
|
||||
spotlight.lookAt(currentBoxPosition);
|
||||
lastBoxPosition.copy(currentBoxPosition);
|
||||
currentBallPosition.set(x, y, z);
|
||||
spotlight.lookAt(currentBallPosition);
|
||||
lastBallPosition.copy(currentBallPosition);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -33,10 +33,10 @@
|
||||
<!-- create a T.SpotLight that looks at box-->
|
||||
<T.SpotLight position={[0, 5, 3]} intensity={10} bind:ref={spotlight}></T.SpotLight>
|
||||
|
||||
<SheetObject key="Box" let:Transform let:Sync on:change={boxMoved}>
|
||||
<SheetObject key="Box" let:Transform let:Sync on:change={ballMoved}>
|
||||
<Transform>
|
||||
<T.Mesh position.y={0.5} bind:ref={box}>
|
||||
<T.BoxGeometry args={[1, 1, 1]} />
|
||||
<T.Mesh position.y={0.5} bind:ref={ball}>
|
||||
<T.SphereGeometry args={[1, 8, 4]} />
|
||||
<T.MeshStandardMaterial color="#b00d03">
|
||||
<Sync color roughness metalness />
|
||||
</T.MeshStandardMaterial>
|
||||
|
@@ -1,32 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Canvas, T } from '@threlte/core';
|
||||
import { OrbitControls } from '@threlte/extras';
|
||||
import OrbitingSpheres from '$lib/components/scenes/home/OrbitingSpheres.svelte';
|
||||
import { World } from '@threlte/rapier';
|
||||
import { Inspector } from 'three-inspect';
|
||||
import { dev } from '$app/environment';
|
||||
import SpaceSkysphere from './SpaceSkysphere.svelte';
|
||||
</script>
|
||||
|
||||
<Canvas>
|
||||
<World gravity={[0, 0, 0]}>
|
||||
<T.PerspectiveCamera position.y={15} position.z={25} makeDefault fov={70} far={10000}>
|
||||
<OrbitControls
|
||||
enableZoom={false}
|
||||
enablePan={false}
|
||||
enableRotate={false}
|
||||
enableDamping
|
||||
target.y={0}
|
||||
autoRotate
|
||||
/>
|
||||
</T.PerspectiveCamera>
|
||||
|
||||
<SpaceSkysphere size={100} count={500} />
|
||||
|
||||
<OrbitingSpheres position={[0, 0, 0]} range={10} />
|
||||
</World>
|
||||
|
||||
{#if dev}
|
||||
<Inspector />
|
||||
{/if}
|
||||
</Canvas>
|
@@ -1,86 +0,0 @@
|
||||
<script lang="ts" context="module">
|
||||
const geometry = new SphereGeometry(1, 8, 4);
|
||||
const material = new MeshBasicMaterial({ color: '#339933' });
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { T } from '@threlte/core';
|
||||
import { HTML } from '@threlte/extras';
|
||||
import { Attractor, Collider, RigidBody } from '@threlte/rapier';
|
||||
import { MeshBasicMaterial, SphereGeometry, Vector3 } from 'three';
|
||||
|
||||
export let range: number = 10;
|
||||
export let position: [number, number, number] = [0, 0, 0];
|
||||
let isHovering = false;
|
||||
let isPointerDown = false;
|
||||
const bodyPositions = [
|
||||
new Vector3(position[0] - range, position[0] - range, position[0] - range).toArray(),
|
||||
new Vector3(position[1] + range / 2, position[1] + range, position[1] + range).toArray(),
|
||||
new Vector3(position[2] + range, position[2] - range / 2, position[2] + range).toArray()
|
||||
];
|
||||
|
||||
const getId = () => {
|
||||
return Math.random().toString(16).slice(2);
|
||||
};
|
||||
|
||||
const getRandomSize = (): number => {
|
||||
return Math.random() / 4 + 0.25;
|
||||
};
|
||||
|
||||
const generateBodies = (c: number) => {
|
||||
return Array(c)
|
||||
.fill('x')
|
||||
.map((_, index) => {
|
||||
return {
|
||||
id: getId(),
|
||||
position: bodyPositions[index],
|
||||
size: getRandomSize()
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onClick = () => {
|
||||
console.log('clicked');
|
||||
};
|
||||
|
||||
$: bodies = generateBodies(2);
|
||||
</script>
|
||||
|
||||
<HTML position.x={position[0]} position.y={position[1]} position.z={position[2]}>
|
||||
<button
|
||||
on:pointerenter={() => (isHovering = true)}
|
||||
on:pointerleave={() => {
|
||||
isPointerDown = false;
|
||||
isHovering = false;
|
||||
}}
|
||||
on:pointerdown={() => (isPointerDown = true)}
|
||||
on:pointerup={() => (isPointerDown = false)}
|
||||
on:pointercancel={() => {
|
||||
isPointerDown = false;
|
||||
isHovering = false;
|
||||
}}
|
||||
on:click={onClick}
|
||||
class="rounded-full bg-orange-500 px-3 text-white hover:opacity-90 active:opacity-70"
|
||||
>
|
||||
Hello!
|
||||
</button>
|
||||
</HTML>
|
||||
|
||||
<Attractor
|
||||
position.x={position[0]}
|
||||
position.y={position[1]}
|
||||
position.z={position[2]}
|
||||
range={200}
|
||||
strength={isHovering ? 1 : 0.1}
|
||||
gravityType={'linear'}
|
||||
/>
|
||||
|
||||
{#each bodies as body (body.id)}
|
||||
<T.Group position={body.position}>
|
||||
<RigidBody>
|
||||
<Collider shape="ball" args={[body.size]} mass={body.size} />
|
||||
<Attractor range={50} strength={1} gravityType={'newtonian'} />
|
||||
<T.Mesh scale={[body.size, body.size, body.size]} {geometry} {material} />
|
||||
</RigidBody>
|
||||
</T.Group>
|
||||
{/each}
|
Reference in New Issue
Block a user