[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
0

Configure Feed

Select the types of activity you want to include in your feed.

make planet rotatable, add vr-code-editor project

+130 -67
+1 -1
package.json
··· 3 3 "version": "0.0.1", 4 4 "private": true, 5 5 "scripts": { 6 - "dev": "vite dev", 6 + "dev": "vite dev --host", 7 7 "build": "vite build", 8 8 "preview": "vite preview", 9 9 "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+99 -47
src/lib/3D/Scene.svelte
··· 1 1 <script lang="ts"> 2 2 import { T, useTask, useThrelte } from '@threlte/core'; 3 3 import { interactivity, useCursor } from '@threlte/extras'; 4 - import { Color } from 'three'; 4 + import { Quaternion, Euler, Vector2, Group } from 'three'; 5 5 import { onMount } from 'svelte'; 6 6 import { spring } from './Utils'; 7 - import { Text3DGeometry } from '@threlte/extras' 8 7 9 8 import Stars from './Stars.svelte'; 10 9 import Nebula from './Nebula.svelte'; 11 - // import Planet from './planet/Planet.svelte'; 12 10 import PlanetModel from './PlanetModel.svelte'; 13 11 14 12 const { onPointerEnter, onPointerLeave } = useCursor(); 15 13 interactivity(); 16 14 17 - let rotate = 0; 18 - let rotateSpring = spring<number>(rotate, 0.1, 0.05); 19 15 let size = 2; 20 16 let sizeSpring = spring<number>(size, 0.1, 0.5); 21 17 22 18 let distance = 1; 19 + const maxSpeed = 0.05; 20 + const acceleration = 0.001; 21 + const damping = 0.98; 23 22 24 - let rotationSpeed = 0.2; 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 + 25 30 useTask((delta) => { 26 - // rotation += delta * rotationSpeed; 31 + size = sizeSpring.update(delta); 32 + 33 + velocity.multiplyScalar(damping); 27 34 28 - rotate = rotateSpring.update(delta); 29 - size = sizeSpring.update(delta); 35 + let rotate = isDragging ? 0.0 : 0.001; 30 36 31 - rotateSpring.set(rotate + delta * rotationSpeed); 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(); 32 51 33 - if (window.innerWidth < 768) { 34 - distance = 6; 52 + let clientX, clientY; 53 + if (event instanceof TouchEvent) { 54 + clientX = event.touches[0].clientX; 55 + clientY = event.touches[0].clientY; 35 56 } else { 36 - distance = 1; 57 + clientX = event.clientX; 58 + clientY = event.clientY; 37 59 } 38 - }); 39 60 40 - const { scene, renderer } = useThrelte(); 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(); 41 99 42 100 onMount(() => { 43 - scene.background = new Color(0x000000); 44 - renderer.setClearColor(0x000000, 1); 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 + }; 45 114 }); 46 115 47 116 export let pos = 0; ··· 54 123 ref.lookAt(0, 1, 0); 55 124 }} 56 125 far={100} 57 - ></T.PerspectiveCamera> 126 + /> 58 127 59 128 <T.DirectionalLight intensity={3} position={[-pos * 10 + 5, 2 + pos * 3, 2]} /> 60 129 61 - <PlanetModel 62 - on:click={() => { 63 - rotateSpring.set(rotate + 2); 130 + <T.Group 131 + bind:ref={planet} 132 + on:pointerdown={(evt) => { 133 + onPointerDown(evt.nativeEvent); 64 134 }} 65 135 on:pointerleave={() => { 66 136 sizeSpring.set(2); 67 137 onPointerLeave(); 68 138 }} 69 139 on:pointerenter={() => { 70 - sizeSpring.set(2.2); 140 + sizeSpring.set(2.1); 71 141 onPointerEnter(); 72 142 }} 73 - scale={size} 74 - rotation.y={rotate} 75 - rotation.z={0.1} 76 - position.x={pos * 4 - 2} 77 - position.z={-distance} 78 - position.y={0.5} 79 - /> 80 - 81 - <!-- <Planet 82 - on:click={() => { 83 - rotateSpring.set(rotate + 2); 84 - }} 85 - on:pointerleave={() => { 86 - sizeSpring.set(2); 87 - onPointerLeave(); 143 + on:touchstart={(evt) => { 144 + onPointerDown(evt.nativeEvent); 88 145 }} 89 - on:pointerenter={() => { 90 - sizeSpring.set(2.2); 91 - onPointerEnter(); 146 + on:touchend={() => { 147 + onPointerUp(); 92 148 }} 93 149 scale={size} 94 - rotation.y={rotate} 95 - rotation.z={0.1} 96 - position.x={pos * 4 - 2} 97 - position.z={-distance} 98 - position.y={0.5} 99 - ></Planet> --> 150 + > 151 + <PlanetModel /> 152 + </T.Group> 100 153 101 154 <Stars /> 102 - 103 155 <Nebula />
+30 -19
src/lib/components/Projects.svelte
··· 8 8 import autostereogramrenderer from '$lib/images/projects-new/autostereogram-renderer-demo.mp4'; 9 9 import mandala from '$lib/images/projects-new/mandala-demo.mp4'; 10 10 11 + import threltevrcodeeditor from '$lib/images/projects-new/threlte-vr-code-editor-demo.mp4'; 11 12 12 13 let projects: { 13 14 src: string; ··· 44 45 description: 45 46 "light-up clothes that make you feel like you're in a sci-fi movie. perfect for raves." 46 47 }, 48 + 49 + { 50 + src: mandala, 51 + alt: 'svg mandala drawer', 52 + href: 'https://flo-bit.github.io/mandala/', 53 + aspect: 'aspect-square', 54 + name: 'svg mandala drawer', 55 + description: 'draw svg mandalas in the browser.' 56 + }, 47 57 { 48 58 src: fake3d, 49 59 alt: 'Image2Fake3D', ··· 72 82 name: 'old coding projects', 73 83 code: 'https://github.com/flo-bit/old-code' 74 84 }, 75 - // { 76 - // src: mandala, 77 - // alt: 'mandala', 78 - // href: 'https://flo-bit.github.io/mandala/', 79 - // aspect: 'aspect-square', 80 - // name: 'mandala', 81 - // description: 'draw svg mandalas in the browser.' 82 - // }, 83 - // { 84 - // src: autostereogramrenderer, 85 - // alt: 'autostereogram renderer', 86 - // href: 'https://flo-bit.github.io/autostereogram-renderer/', 87 - // aspect: 'aspect-square', 88 - // name: 'autostereogram renderer', 89 - // description: 'rendering a 3d scene as an autostereogram.' 90 - // }, 85 + { 86 + src: threltevrcodeeditor, 87 + alt: 'threlte vr code editor', 88 + href: 'https://github.com/flo-bit/threlte-vr-code-editor', 89 + aspect: 'aspect-[16/11]', 90 + name: 'threlte vr code editor', 91 + description: 'a virtual reality code editor made with threlte.', 92 + code: 'https://github.com/flo-bit/threlte-vr-code-editor' 93 + }, 94 + { 95 + src: autostereogramrenderer, 96 + alt: 'autostereogram renderer', 97 + href: 'https://flo-bit.github.io/autostereogram-renderer/', 98 + aspect: 'aspect-square', 99 + name: 'autostereogram renderer', 100 + description: 'rendering a 3d scene as an autostereogram.' 101 + } 91 102 ]; 92 103 </script> 93 104 ··· 107 118 <div class="columns-2 md:columns-4 gap-4"> 108 119 {#each projects as project, index} 109 120 <div 110 - class="group relative {project.aspect ?? 121 + class="group relative break-inside-avoid-column {project.aspect ?? 111 122 'aspect-square'} mb-4 w-full flex-none overflow-hidden rounded-xl ring-1 ring-white/10 bg-zinc-100 dark:bg-zinc-800 sm:rounded-2xl" 112 123 > 113 124 {#if project.src.endsWith('.mp4')} ··· 153 164 {project.name} 154 165 </div> 155 166 156 - {#if project.description} 167 + <!-- {#if project.description} 157 168 <div class="text-sm font-normal mt-2"> 158 169 {project.description} 159 170 </div> 160 - {/if} 171 + {/if} --> 161 172 </div> 162 173 </div> 163 174
src/lib/images/projects-new/threlte-vr-code-editor-demo.mp4

This is a binary file and will not be displayed.

src/lib/images/projects-new/threlte-vr-code-editor-image.png

This is a binary file and will not be displayed.