forked from BRETT/BRETT
111 lines
2.7 KiB
Vue
111 lines
2.7 KiB
Vue
<script setup lang="ts">
|
|
import { hexToRgbTuple } from '@/lib/color'
|
|
|
|
// Portiert aus Vue Bits (DavidHDev/vue-bits), Plane+Camera auf Fullscreen-Triangle
|
|
// vereinfacht, nur color-Prop
|
|
const props = withDefaults(defineProps<{ color?: string }>(), {
|
|
color: '#6366f1',
|
|
})
|
|
|
|
const VERT = `
|
|
attribute vec2 uv;
|
|
attribute vec2 position;
|
|
varying vec2 vUv;
|
|
void main() {
|
|
vUv = uv;
|
|
gl_Position = vec4(position, 0.0, 1.0);
|
|
}
|
|
`
|
|
|
|
const FRAG = `
|
|
precision highp float;
|
|
|
|
varying vec2 vUv;
|
|
|
|
uniform float uTime;
|
|
uniform vec3 uColor;
|
|
uniform float uSpeed;
|
|
uniform float uScale;
|
|
uniform float uRotation;
|
|
uniform float uNoiseIntensity;
|
|
uniform vec2 uCardRes;
|
|
uniform float uRadius;
|
|
|
|
const float e = 2.71828182845904523536;
|
|
|
|
float noise(vec2 texCoord) {
|
|
float G = e;
|
|
vec2 r = (G * sin(G * texCoord));
|
|
return fract(r.x * r.y * (1.0 + texCoord.x));
|
|
}
|
|
|
|
vec2 rotateUvs(vec2 uv, float angle) {
|
|
float c = cos(angle);
|
|
float s = sin(angle);
|
|
mat2 rot = mat2(c, -s, s, c);
|
|
return rot * uv;
|
|
}
|
|
|
|
float cardMask(vec2 fc) {
|
|
vec2 p = fc - uCardRes * 0.5;
|
|
vec2 hb = uCardRes * 0.5 - vec2(uRadius);
|
|
float rd = length(max(abs(p) - hb, 0.0)) - uRadius;
|
|
return 1.0 - smoothstep(-0.75, 0.75, rd);
|
|
}
|
|
|
|
void main() {
|
|
float rnd = noise(gl_FragCoord.xy);
|
|
// Pixelkonsistent: Muster-Koordinaten auf eine Referenzgröße von 180px
|
|
// normieren, damit flache Container den Effekt nicht vertikal stauchen
|
|
vec2 suv = vUv * (uCardRes / 180.0);
|
|
vec2 uv = rotateUvs(suv * uScale, uRotation);
|
|
vec2 tex = uv * uScale;
|
|
float tOffset = uSpeed * uTime;
|
|
|
|
tex.y += 0.03 * sin(8.0 * tex.x - tOffset);
|
|
|
|
float pattern = 0.6 +
|
|
0.4 * sin(5.0 * (tex.x + tex.y +
|
|
cos(3.0 * tex.x + 5.0 * tex.y) +
|
|
0.02 * tOffset) +
|
|
sin(20.0 * (tex.x + tex.y - 0.1 * tOffset)));
|
|
|
|
vec4 col = vec4(uColor, 1.0) * vec4(pattern) - rnd / 15.0 * uNoiseIntensity;
|
|
|
|
float m = cardMask(gl_FragCoord.xy);
|
|
float a = clamp(pattern * 0.45, 0.0, 1.0) * m;
|
|
gl_FragColor = vec4(col.rgb * a, a);
|
|
}
|
|
`
|
|
|
|
const ctnDom = ref<HTMLDivElement | null>(null)
|
|
|
|
let elapsed = 0
|
|
let lastT = 0
|
|
|
|
useCardShader(ctnDom, {
|
|
vertex: VERT,
|
|
fragment: FRAG,
|
|
uniforms: {
|
|
uTime: { value: 0 },
|
|
uColor: { value: new Float32Array(hexToRgbTuple(props.color)) },
|
|
uSpeed: { value: 5 },
|
|
uScale: { value: 1 },
|
|
uNoiseIntensity: { value: 1.5 },
|
|
uRotation: { value: 0 },
|
|
},
|
|
color: () => props.color,
|
|
colorUniforms: { uColor: 0 },
|
|
frame: ({ t, program }) => {
|
|
const delta = Math.min((t - lastT) * 0.001, 0.1)
|
|
lastT = t
|
|
elapsed += 0.1 * delta
|
|
program.uniforms.uTime.value = elapsed
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="ctnDom" class="relative size-full overflow-hidden rounded-[inherit]" />
|
|
</template>
|