[READ-ONLY] Mirror of https://github.com/flo-bit/flo-bit.github.io. my personal website, w/ astro, svelte, tailwind, typescript, threlte
flo-bit.dev/
portfolio
portfolio-website
svelte
sveltekit
tailwind
threejs
threlte
typescript
3.8 kB
155 lines
1<script lang="ts">
2 import { T, useTask, useThrelte } from '@threlte/core';
3 import { interactivity, useCursor } from '@threlte/extras';
4 import { Quaternion, Euler, Vector2, Group } from 'three';
5 import { onMount } from 'svelte';
6 import { spring } from './Utils';
7
8 import Stars from './Stars.svelte';
9 import Nebula from './Nebula.svelte';
10 import PlanetModel from './PlanetModel.svelte';
11
12 const { onPointerEnter, onPointerLeave } = useCursor();
13 interactivity();
14
15 let size = 2;
16 let sizeSpring = spring<number>(size, 0.1, 0.5);
17
18 let distance = 1;
19 const maxSpeed = 0.05;
20 const acceleration = 0.001;
21 const damping = 0.98;
22
23 let isDragging = false;
24 let previousPointerPosition = new Vector2();
25 let velocity = new Vector2();
26
27 let quaternion = new Quaternion();
28 let planet: Group;
29
30 useTask((delta) => {
31 size = sizeSpring.update(delta);
32
33 velocity.multiplyScalar(damping);
34
35 let rotate = isDragging ? 0.0 : 0.001;
36
37 const deltaRotationQuaternion = new Quaternion().setFromEuler(
38 new Euler(velocity.y * delta * 120, (velocity.x + rotate) * delta * 120, 0, 'XYZ')
39 );
40 quaternion.multiplyQuaternions(deltaRotationQuaternion, quaternion);
41
42 if (planet) {
43 planet.quaternion.copy(quaternion);
44 planet.position.set(pos * 4 - 2, 0.5, -distance);
45 }
46 });
47
48 const onPointerMove = (event: PointerEvent | TouchEvent) => {
49 if (!isDragging) return;
50 event.preventDefault();
51
52 let clientX, clientY;
53 if (event instanceof TouchEvent) {
54 clientX = event.touches[0].clientX;
55 clientY = event.touches[0].clientY;
56 } else {
57 clientX = event.clientX;
58 clientY = event.clientY;
59 }
60
61 const deltaMove = new Vector2(
62 clientX - previousPointerPosition.x,
63 clientY - previousPointerPosition.y
64 );
65
66 velocity.x += deltaMove.x * acceleration;
67 velocity.y += deltaMove.y * acceleration;
68
69 // Limit the speed
70 if (velocity.length() > maxSpeed) {
71 velocity.normalize().multiplyScalar(maxSpeed);
72 }
73
74 previousPointerPosition.set(clientX, clientY);
75 };
76
77 const onPointerDown = (event: PointerEvent | TouchEvent) => {
78 isDragging = true;
79 let clientX, clientY;
80 if (event instanceof TouchEvent) {
81 clientX = event.touches[0].clientX;
82 clientY = event.touches[0].clientY;
83 } else {
84 clientX = event.clientX;
85 clientY = event.clientY;
86 }
87 previousPointerPosition.set(clientX, clientY);
88
89 if (event instanceof TouchEvent) {
90 event.preventDefault(); // Prevent scrolling
91 }
92 };
93
94 const onPointerUp = () => {
95 isDragging = false;
96 };
97
98 const { renderer } = useThrelte();
99
100 onMount(() => {
101 const canvas = renderer.domElement;
102
103 canvas.addEventListener('pointermove', onPointerMove, {passive: false});
104 canvas.addEventListener('pointerup', onPointerUp, false);
105 canvas.addEventListener('touchmove', onPointerMove, {passive: false});
106 canvas.addEventListener('touchend', onPointerUp, false);
107
108 return () => {
109 canvas.removeEventListener('pointermove', onPointerMove, false);
110 canvas.removeEventListener('pointerup', onPointerUp, false);
111 canvas.removeEventListener('touchmove', onPointerMove, false);
112 canvas.removeEventListener('touchend', onPointerUp, false);
113 };
114 });
115
116 export let pos = 0;
117</script>
118
119<T.PerspectiveCamera
120 makeDefault
121 position={[0, 0, 5]}
122 on:create={({ ref }) => {
123 ref.lookAt(0, 1, 0);
124 }}
125 far={100}
126/>
127
128<T.DirectionalLight intensity={3} position={[-pos * 10 + 5, 2 + pos * 3, 2]} />
129
130<T.Group
131 bind:ref={planet}
132 on:pointerdown={(evt) => {
133 onPointerDown(evt.nativeEvent);
134 }}
135 on:pointerleave={() => {
136 sizeSpring.set(2);
137 onPointerLeave();
138 }}
139 on:pointerenter={() => {
140 sizeSpring.set(2.1);
141 onPointerEnter();
142 }}
143 on:touchstart={(evt) => {
144 onPointerDown(evt.nativeEvent);
145 }}
146 on:touchend={() => {
147 onPointerUp();
148 }}
149 scale={size}
150>
151 <PlanetModel />
152</T.Group>
153
154<Stars />
155<Nebula />