tangled
alpha
login
or
join now
moth11.net
/
xcvr
2
fork
atom
frontend for xcvr appview
2
fork
atom
overview
issues
pulls
pipelines
maybe this should work?
moth11.net
6 months ago
fa5e9406
b4e42e7c
+211
-13
6 changed files
expand all
collapse all
unified
split
package-lock.json
package.json
src
app.html
lib
components
CursorEffect.svelte
routes
+layout.svelte
static
main.css
+8
-1
package-lock.json
···
12
12
"@protobuf-ts/runtime": "^2.11.0",
13
13
"@rachel-mp4/lrcproto": "^1.0.4",
14
14
"fast-diff": "^1.3.0",
15
15
-
"linkifyjs": "^4.3.2"
15
15
+
"linkifyjs": "^4.3.2",
16
16
+
"ogl": "^1.0.11"
16
17
},
17
18
"devDependencies": {
18
19
"@sveltejs/adapter-auto": "^6.0.0",
···
1291
1292
"engines": {
1292
1293
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1293
1294
}
1295
1295
+
},
1296
1296
+
"node_modules/ogl": {
1297
1297
+
"version": "1.0.11",
1298
1298
+
"resolved": "https://registry.npmjs.org/ogl/-/ogl-1.0.11.tgz",
1299
1299
+
"integrity": "sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA==",
1300
1300
+
"license": "Unlicense"
1294
1301
},
1295
1302
"node_modules/picocolors": {
1296
1303
"version": "1.1.1",
+2
-1
package.json
···
26
26
"@protobuf-ts/runtime": "^2.11.0",
27
27
"@rachel-mp4/lrcproto": "^1.0.4",
28
28
"fast-diff": "^1.3.0",
29
29
-
"linkifyjs": "^4.3.2"
29
29
+
"linkifyjs": "^4.3.2",
30
30
+
"ogl": "^1.0.11"
30
31
}
31
32
}
+2
-1
src/app.html
···
74
74
</filter>
75
75
</defs>
76
76
</svg>
77
77
-
<div style="display: flex; flex-direction:column; justify-content: space-between; height:100vh; height: 100dvh;">%sveltekit.body%</div><script type="module">
77
77
+
<div style="display: flex; flex-direction:column; justify-content: space-between; height:100vh; height: 100dvh;">%sveltekit.body%</div>
78
78
+
<script type="module">
78
79
import {
79
80
Polyline,
80
81
Renderer,
+197
src/lib/components/CursorEffect.svelte
···
1
1
+
<script lang="ts">
2
2
+
import { onMount, onDestroy } from "svelte";
3
3
+
import { browser } from "$app/environment";
4
4
+
import { Polyline, Renderer, Transform, Vec3, Color } from "ogl";
5
5
+
interface Props {
6
6
+
enabled: boolean;
7
7
+
colors?: Array<string>;
8
8
+
}
9
9
+
10
10
+
let { enabled, colors = ["#e18f39", "#c5c042", "#387f4d", "#1d4633"] } =
11
11
+
$props();
12
12
+
13
13
+
let canvas: undefined | HTMLCanvasElement = $state();
14
14
+
const thickness: [number, number] = [14, 35];
15
15
+
const spring: [number, number] = [0.02, 0.1];
16
16
+
const friction: [number, number] = [0.7, 0.95];
17
17
+
let animationId: null | number = null;
18
18
+
let renderer: null | Renderer = null;
19
19
+
let scene: Transform;
20
20
+
let lines = [] as {
21
21
+
points: Vec3[];
22
22
+
polyline: Polyline;
23
23
+
spring: number;
24
24
+
friction: number;
25
25
+
mouseVelocity: Vec3;
26
26
+
mouseOffset: Vec3;
27
27
+
}[];
28
28
+
let mouse: Vec3;
29
29
+
let tmp: Vec3;
30
30
+
const vertex = `
31
31
+
attribute vec3 position;
32
32
+
attribute vec3 next;
33
33
+
attribute vec3 prev;
34
34
+
attribute vec2 uv;
35
35
+
attribute float side;
36
36
+
37
37
+
uniform vec2 uResolution;
38
38
+
uniform float uDPR;
39
39
+
uniform float uThickness;
40
40
+
41
41
+
vec4 getPosition() {
42
42
+
vec2 aspect = vec2(uResolution.x / uResolution.y, 1);
43
43
+
vec2 nextScreen = next.xy * aspect;
44
44
+
vec2 prevScreen = prev.xy * aspect;
45
45
+
46
46
+
vec2 tangent = normalize(nextScreen - prevScreen);
47
47
+
vec2 normal = vec2(-tangent.y, tangent.x);
48
48
+
normal /= aspect;
49
49
+
normal *= 1.0 - pow(abs(uv.y - 0.5) * 1.9, 2.0);
50
50
+
51
51
+
float pixelWidth = 1.0 / (uResolution.y / uDPR);
52
52
+
normal *= pixelWidth * uThickness;
53
53
+
54
54
+
float dist = length(nextScreen - prevScreen);
55
55
+
normal *= smoothstep(0.0, 0.02, dist);
56
56
+
57
57
+
vec4 current = vec4(position, 1);
58
58
+
current.xy -= normal * side;
59
59
+
return current;
60
60
+
}
61
61
+
62
62
+
void main() {
63
63
+
gl_Position = getPosition();
64
64
+
}
65
65
+
`;
66
66
+
const random = (array: [number, number]): number => {
67
67
+
const alpha = Math.random();
68
68
+
return array[0] * (1.0 - alpha) + array[1] * alpha;
69
69
+
};
70
70
+
function initializeCursorEffect() {
71
71
+
renderer = new Renderer({
72
72
+
dpr: 2,
73
73
+
alpha: true,
74
74
+
premultipliedAlpha: true,
75
75
+
canvas,
76
76
+
});
77
77
+
if (!browser || !enabled) return;
78
78
+
const gl = renderer.gl;
79
79
+
gl.clearColor(0, 0, 0, 0);
80
80
+
gl.enable(gl.BLEND);
81
81
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
82
82
+
scene = new Transform();
83
83
+
mouse = new Vec3();
84
84
+
tmp = new Vec3();
85
85
+
colors.forEach((color) => {
86
86
+
const count = 20;
87
87
+
const points = [] as Vec3[];
88
88
+
for (let i = 0; i < count; i++) {
89
89
+
points.push(new Vec3());
90
90
+
}
91
91
+
const line = {
92
92
+
points: points,
93
93
+
polyline: new Polyline(gl, {
94
94
+
points,
95
95
+
vertex,
96
96
+
uniforms: {
97
97
+
uColor: { value: new Color(color) },
98
98
+
uThickness: { value: random(thickness) },
99
99
+
},
100
100
+
}),
101
101
+
spring: random(spring),
102
102
+
friction: random(friction),
103
103
+
mouseVelocity: new Vec3(),
104
104
+
mouseOffset: new Vec3(random([-1, 1]) * 0.02),
105
105
+
};
106
106
+
line.polyline.mesh.setParent(scene);
107
107
+
lines.push(line);
108
108
+
});
109
109
+
resize();
110
110
+
startAnimation();
111
111
+
}
112
112
+
function resize() {
113
113
+
renderer?.setSize(window.innerWidth, window.innerHeight);
114
114
+
lines.forEach((line) => line.polyline.resize());
115
115
+
}
116
116
+
function updateMouse(e: MouseEvent) {
117
117
+
if (!mouse) return;
118
118
+
const x = e.pageX;
119
119
+
const y = e.pageY;
120
120
+
mouse.set(
121
121
+
(x / (renderer?.gl.renderer.width ?? 1)) * 2 - 1,
122
122
+
(y / (renderer?.gl.renderer.height ?? 1)) * -2 + 1,
123
123
+
0,
124
124
+
);
125
125
+
}
126
126
+
function update() {
127
127
+
if (!enabled) {
128
128
+
animationId = null;
129
129
+
return;
130
130
+
}
131
131
+
animationId = requestAnimationFrame(update);
132
132
+
lines.forEach((line) => {
133
133
+
for (let i = line.points.length - 1; i >= 0; i--) {
134
134
+
if (!i) {
135
135
+
tmp
136
136
+
.copy(mouse)
137
137
+
.add(line.mouseOffset)
138
138
+
.sub(line.points[i])
139
139
+
.multiply(line.spring);
140
140
+
line.mouseVelocity.add(tmp).multiply(line.friction);
141
141
+
line.points[i].add(line.mouseVelocity);
142
142
+
} else {
143
143
+
line.points[i].lerp(line.points[i - 1], 0.9);
144
144
+
}
145
145
+
}
146
146
+
line.polyline.updateGeometry();
147
147
+
});
148
148
+
renderer?.render({ scene });
149
149
+
}
150
150
+
function startAnimation() {
151
151
+
if (!animationId) {
152
152
+
requestAnimationFrame(update);
153
153
+
}
154
154
+
}
155
155
+
function stopAnimation() {
156
156
+
if (animationId) {
157
157
+
cancelAnimationFrame(animationId);
158
158
+
animationId = null;
159
159
+
}
160
160
+
}
161
161
+
$effect(() => {
162
162
+
if (enabled) {
163
163
+
startAnimation();
164
164
+
} else {
165
165
+
stopAnimation();
166
166
+
}
167
167
+
});
168
168
+
onMount(() => {
169
169
+
if (!browser) return;
170
170
+
initializeCursorEffect();
171
171
+
window.addEventListener("resize", resize);
172
172
+
window.addEventListener("mousemove", updateMouse);
173
173
+
});
174
174
+
onDestroy(() => {
175
175
+
if (!browser) return;
176
176
+
stopAnimation();
177
177
+
window.removeEventListener("resize", resize);
178
178
+
window.removeEventListener("mousemove", updateMouse);
179
179
+
});
180
180
+
</script>
181
181
+
182
182
+
{#if enabled}
183
183
+
<canvas bind:this={canvas} id="cursor-canvas"></canvas>
184
184
+
{/if}
185
185
+
186
186
+
<style>
187
187
+
#cursor-canvas {
188
188
+
position: fixed;
189
189
+
top: 0;
190
190
+
left: 0;
191
191
+
width: 100vw;
192
192
+
height: 100vw;
193
193
+
pointer-events: none;
194
194
+
z-index: -1;
195
195
+
filter: blur(1rem);
196
196
+
}
197
197
+
</style>
+2
src/routes/+layout.svelte
···
4
4
import type { LayoutProps } from "./$types";
5
5
import Spectrum from "$lib/components/Spectrum.svelte";
6
6
import { browser } from "$app/environment";
7
7
+
import CursorEffect from "$lib/components/CursorEffect.svelte";
7
8
let { data, children }: LayoutProps = $props();
8
9
let innerWidth = $state(0);
9
10
let isDesktop = $derived(innerWidth > 1000);
···
167
168
<label for="ccccc">ccccc</label>
168
169
</div>
169
170
</div>
171
171
+
<CursorEffect enabled={true} />
170
172
{/if}
171
173
172
174
<style>
-10
static/main.css
···
100
100
#content:not(.desktop) .desktop {
101
101
display: none;
102
102
}
103
103
-
#cursor-canvas {
104
104
-
position: fixed;
105
105
-
top: 0;
106
106
-
left: 0;
107
107
-
width: 100vw;
108
108
-
height: 100vw;
109
109
-
pointer-events: none;
110
110
-
z-index: -1;
111
111
-
filter: blur(1rem);
112
112
-
}