Separated out Background.vue
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 58s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 58s
That'll make using it in the homepage easier
This commit is contained in:
parent
831060a57e
commit
411b2ac293
4 changed files with 61 additions and 86 deletions
|
@ -1,58 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<TresCanvas>
|
<TresGroup ref="groupRef" v-if="renderer">
|
||||||
<TresOrthographicCamera ref="camera" :position="[0, 0, 10]" />
|
<TresMesh v-for="i in rows * cols" :position="[((i % cols) - cols / 2) * 304, (Math.floor((i - 1) / cols) - rows / 2) * 304, 0]" >
|
||||||
<TresGroup ref="blobRef">
|
<TresShapeGeometry :args="[shapes]" />
|
||||||
<TresMesh v-for="i in rows * cols" :position="[((i % cols) - cols / 2) * 304, (Math.floor((i - 1) / cols) - rows / 2) * 304, 0]" >
|
<TresShaderMaterial :vertexShader="vertexShader" :fragmentShader="fragmentShader" :uniforms="uniforms" :blending="AdditiveBlending" />
|
||||||
<TresShapeGeometry :args="[shapes]" />
|
</TresMesh>
|
||||||
<TresShaderMaterial :vertexShader="vertexShader" :fragmentShader="fragmentShader" :uniforms="uniforms" :blending="AdditiveBlending" />
|
</TresGroup>
|
||||||
</TresMesh>
|
|
||||||
</TresGroup>
|
|
||||||
<TresAmbientLight :intensity="1" />
|
|
||||||
</TresCanvas>
|
|
||||||
<div ref="resizeListener" class="resize-listener" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, shallowRef, onMounted, onUnmounted, watch } from "vue";
|
import { useLoader, useRenderLoop, useTresContext } from '@tresjs/core';
|
||||||
import { TresCanvas, useLoader, useRenderLoop } from '@tresjs/core';
|
import { AdditiveBlending, Group, Vector2 } from "three";
|
||||||
import { SVGLoader } from "three/examples/jsm/loaders/SVGLoader.js";
|
import { SVGLoader } from "three/examples/jsm/loaders/SVGLoader.js";
|
||||||
|
import { computed, onMounted, onUnmounted, ref, shallowRef } from "vue";
|
||||||
import noise from "./noise.glsl?raw";
|
import noise from "./noise.glsl?raw";
|
||||||
import { AdditiveBlending, Vector2 } from "three";
|
|
||||||
|
|
||||||
const camera = ref();
|
const { renderer, sizes } = useTresContext();
|
||||||
|
|
||||||
// Load SVG
|
// Load SVG
|
||||||
const { paths } = await useLoader(SVGLoader, '/circuit-board.svg');
|
const { paths } = await useLoader(SVGLoader, '/circuit-board.svg');
|
||||||
const shapes = paths.map(path => SVGLoader.createShapes(path)).reduce((acc, curr) => [...acc, ...curr]);
|
const shapes = paths.map(path => SVGLoader.createShapes(path)).reduce((acc, curr) => [...acc, ...curr]);
|
||||||
|
|
||||||
// Handle canvas size
|
const rows = computed(() => Math.ceil(sizes.height.value / 304));
|
||||||
const width = ref(0);
|
const cols = computed(() => Math.ceil(sizes.width.value / 304));
|
||||||
const height = ref(0);
|
|
||||||
const rows = computed(() => Math.ceil(height.value / 304));
|
|
||||||
const cols = computed(() => Math.ceil(width.value / 304));
|
|
||||||
const resizeObserver = new ResizeObserver(updateSize);
|
|
||||||
const resizeListener = ref<Element | null>(null);
|
|
||||||
function updateSize() {
|
|
||||||
const rect = resizeListener.value?.getBoundingClientRect();
|
|
||||||
width.value = rect?.width ?? 0;
|
|
||||||
height.value = rect?.height ?? 0;
|
|
||||||
}
|
|
||||||
watch([width, height, camera], ([width, height, camera]) => {
|
|
||||||
if (camera) {
|
|
||||||
camera.left = 0;
|
|
||||||
camera.bottom = 0
|
|
||||||
camera.right = width;
|
|
||||||
camera.top = height;
|
|
||||||
camera.updateProjectionMatrix();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle mouse position
|
// Handle mouse position
|
||||||
const mousePos = ref(new Vector2(Infinity, Infinity));
|
const mousePos = ref(new Vector2(Infinity, Infinity));
|
||||||
function updateMousePos(event: MouseEvent) {
|
function updateMousePos(event: MouseEvent) {
|
||||||
mousePos.value = new Vector2(event.screenX, window.screen.availHeight - event.screenY);
|
mousePos.value = new Vector2(event.screenX, window.screen.availHeight - event.screenY);
|
||||||
if (blobRef.value) {
|
if (groupRef.value) {
|
||||||
blobRef.value.children.forEach(child => {
|
groupRef.value.children.forEach(child => {
|
||||||
child.material.uniforms.uMouse.value = mousePos.value;
|
child.material.uniforms.uMouse.value = mousePos.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -60,8 +36,8 @@ function updateMousePos(event: MouseEvent) {
|
||||||
function handleMouseLeave(event: MouseEvent) {
|
function handleMouseLeave(event: MouseEvent) {
|
||||||
if (!event.relatedTarget) {
|
if (!event.relatedTarget) {
|
||||||
mousePos.value = new Vector2(Infinity, Infinity);
|
mousePos.value = new Vector2(Infinity, Infinity);
|
||||||
if (blobRef.value) {
|
if (groupRef.value) {
|
||||||
blobRef.value.children.forEach(child => {
|
groupRef.value.children.forEach(child => {
|
||||||
child.material.uniforms.uMouse.value = mousePos.value;
|
child.material.uniforms.uMouse.value = mousePos.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -70,19 +46,16 @@ function handleMouseLeave(event: MouseEvent) {
|
||||||
|
|
||||||
// Setup window listeners
|
// Setup window listeners
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (resizeListener.value != null) {
|
window.addEventListener("mousemove", updateMousePos);
|
||||||
resizeObserver.observe(resizeListener.value);
|
window.addEventListener("mouseout", handleMouseLeave);
|
||||||
}
|
|
||||||
window.addEventListener("mousemove", updateMousePos);
|
|
||||||
window.addEventListener("mouseout", handleMouseLeave);
|
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener("mousemove", updateMousePos);
|
window.removeEventListener("mousemove", updateMousePos);
|
||||||
window.removeEventListener("mouseout", handleMouseLeave);
|
window.removeEventListener("mouseout", handleMouseLeave);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Shaders
|
// Shaders
|
||||||
const blobRef = shallowRef<Element | null>(null);
|
const groupRef = shallowRef<Group | null>(null);
|
||||||
|
|
||||||
const uniforms = {
|
const uniforms = {
|
||||||
uTime: { value: 0 },
|
uTime: { value: 0 },
|
||||||
|
@ -98,8 +71,7 @@ void main() {
|
||||||
gl_Position = projectionMatrix * viewPosition;
|
gl_Position = projectionMatrix * viewPosition;
|
||||||
vUv = uv;
|
vUv = uv;
|
||||||
}
|
}
|
||||||
`
|
`;
|
||||||
|
|
||||||
const fragmentShader = noise + `
|
const fragmentShader = noise + `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
uniform float uTime;
|
uniform float uTime;
|
||||||
|
@ -112,26 +84,14 @@ void main() {
|
||||||
alpha += max(0., snoise(vec3(gl_FragCoord.xy / 304., uTime / 4.)));
|
alpha += max(0., snoise(vec3(gl_FragCoord.xy / 304., uTime / 4.)));
|
||||||
gl_FragColor = vec4(0, 0, 0, alpha);
|
gl_FragColor = vec4(0, 0, 0, alpha);
|
||||||
}
|
}
|
||||||
`
|
`;
|
||||||
const { onLoop } = useRenderLoop();
|
|
||||||
|
|
||||||
|
const { onLoop } = useRenderLoop();
|
||||||
onLoop(({ elapsed }) => {
|
onLoop(({ elapsed }) => {
|
||||||
if (blobRef.value) {
|
if (groupRef.value) {
|
||||||
blobRef.value.children.forEach(child => {
|
groupRef.value.children.forEach(child => {
|
||||||
child.material.uniforms.uTime.value = elapsed;
|
child.material.uniforms.uTime.value = elapsed;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.resize-listener {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0;
|
|
||||||
right: -4px;
|
|
||||||
bottom: 5px;
|
|
||||||
z-index: -10;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
21
site/.vitepress/theme/Camera.vue
Normal file
21
site/.vitepress/theme/Camera.vue
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<TresOrthographicCamera :position="[0, 0, 10]" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useTresContext } from '@tresjs/core';
|
||||||
|
import { OrthographicCamera } from 'three';
|
||||||
|
import { watch } from "vue";
|
||||||
|
|
||||||
|
const context = useTresContext();
|
||||||
|
watch([context.sizes.width, context.sizes.height, context.camera], ([width, height, camera]) => {
|
||||||
|
const cam = camera as OrthographicCamera;
|
||||||
|
if (cam) {
|
||||||
|
cam.left = 0;
|
||||||
|
cam.bottom = 0
|
||||||
|
cam.right = width;
|
||||||
|
cam.top = height;
|
||||||
|
cam.updateProjectionMatrix();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -3,11 +3,15 @@
|
||||||
<template #layout-top>
|
<template #layout-top>
|
||||||
<NolebaseHighlightTargetedHeading />
|
<NolebaseHighlightTargetedHeading />
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<Suspense>
|
<div class="background">
|
||||||
<div class="background">
|
<TresCanvas>
|
||||||
<Background />
|
<Camera />
|
||||||
</div>
|
<TresAmbientLight :intensity="1" />
|
||||||
</Suspense>
|
<Suspense>
|
||||||
|
<Background />
|
||||||
|
</Suspense>
|
||||||
|
</TresCanvas>
|
||||||
|
</div>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</template>
|
</template>
|
||||||
<template #layout-bottom>
|
<template #layout-bottom>
|
||||||
|
@ -22,11 +26,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import DefaultTheme from 'vitepress/theme'
|
|
||||||
import { NolebaseHighlightTargetedHeading } from '@nolebase/vitepress-plugin-highlight-targeted-heading/client'
|
import { NolebaseHighlightTargetedHeading } from '@nolebase/vitepress-plugin-highlight-targeted-heading/client'
|
||||||
|
import { TresCanvas } from '@tresjs/core'
|
||||||
import '@nolebase/vitepress-plugin-highlight-targeted-heading/client/style.css'
|
import '@nolebase/vitepress-plugin-highlight-targeted-heading/client/style.css'
|
||||||
import './custom.css'
|
import DefaultTheme from 'vitepress/theme'
|
||||||
import Background from './Background.vue'
|
import Background from './Background.vue'
|
||||||
|
import Camera from './Camera.vue'
|
||||||
|
import OrbitControls from './OrbitControls.vue'
|
||||||
|
import './custom.css'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -12,16 +12,3 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||||
extend({ OrbitControls });
|
extend({ OrbitControls });
|
||||||
const { camera, renderer } = useTresContext();
|
const { camera, renderer } = useTresContext();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: -1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue