Untitled
unknown
plain_text
2 years ago
7.3 kB
13
Indexable
<script lang="ts" setup>
import * as THREE from "three";
import { onMounted, onUnmounted, ref } from "vue";
import skoda from "../assets/images/magnet.png";
import { TextureLoader } from "three";
import Typography from "./Typography.vue";
import gsap from "gsap";
import { navigate } from "astro:transitions/client";
const imageHeight = 1.35;
const imageWidth = 1;
const referenceCanvas = ref<HTMLDivElement>();
const textContainer = ref<HTMLDivElement>(); // Reference to the text container
const disableMouse = ref(false);
const createScene = (width: number, height: number) => {
const scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.z = 2;
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(width, height);
referenceCanvas.value?.appendChild(renderer.domElement);
return { scene, camera, renderer };
};
// on click event, we should pass all values affecting flag animation as uniform with gsap animation
const createPlane = (scene: THREE.Scene, textureLoader: THREE.TextureLoader) => {
const texture = textureLoader.load(skoda.src);
// Vertex shader: Determines the position of each vertex
const vShader = `
varying vec2 vUv;
uniform sampler2D uTexture;
uniform float uTime;
void main() {
vUv = uv;
vec3 position = position;
float waveSpeed = 0.25;
float waveDistance = 0.02;
float waveDensity = 3.0;
position.z += sin(uv.x * waveDensity + uTime * waveSpeed) * waveDistance;
position.y += sin(uv.x * waveDensity + uTime * waveSpeed) * waveDistance;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
// Fragment shader: Determines the color of each pixel
const fShader = `
precision mediump float;
varying vec2 vUv;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture, vUv);
}
`;
const geometry = new THREE.PlaneGeometry(imageWidth, imageHeight, 16, 16);
const material = new THREE.ShaderMaterial({
uniforms: {
uTexture: {
value: texture,
},
uTime: {
value: 0,
},
},
vertexShader: vShader,
fragmentShader: fShader,
});
const plane = new THREE.Mesh(geometry, material);
plane.rotation.z = -0.25;
scene.add(plane);
return { plane, material };
};
let plane: { plane: THREE.Mesh; material: THREE.ShaderMaterial };
let camera: THREE.PerspectiveCamera;
let transformX: gsap.QuickToFunc;
let transformY: gsap.QuickToFunc;
let rotateX: gsap.QuickToFunc;
let rotateY: gsap.QuickToFunc;
const handleClick = () => {
disableMouse.value = !disableMouse.value;
transformX.tween.kill();
transformY.tween.kill();
rotateX.tween.kill();
rotateY.tween.kill();
gsap.to(plane.plane.position, {
x: 0,
y: 0,
duration: 0.5,
});
gsap.to(plane.plane.rotation, {
x: 0,
y: 0,
z: 0,
duration: 0.5,
onComplete: () => {
const fovRadians = THREE.MathUtils.degToRad(camera.fov);
const visibleHeight = 2 * Math.tan(fovRadians / 2) * camera.position.z;
const aspectRatio = camera.aspect;
const visibleWidth = visibleHeight * aspectRatio;
const planeWidthPx = (1 / visibleWidth) * window.innerWidth;
const planeHeightPx = (1.35 / visibleHeight) * window.innerHeight;
gsap.set(".img", {
width: `${planeWidthPx}px`,
height: `${planeHeightPx}px`,
opacity: 1,
});
navigate("/about");
},
});
};
onMounted(() => {
// gsap text
const tl = gsap.timeline({ repeat: -1, defaults: { ease: "linear" } });
// three animation
const width = referenceCanvas.value?.clientWidth || 0;
const height = referenceCanvas.value?.clientHeight || 0;
const { scene, camera, renderer } = createScene(width, height);
const textureLoader = new TextureLoader();
plane = createPlane(scene, textureLoader);
transformX = gsap.quickTo(plane.plane.position, "x", { duration: 1 });
transformY = gsap.quickTo(plane.plane.position, "y", { duration: 1 });
rotateX = gsap.quickTo(plane.plane.rotation, "x", { duration: 0.1 });
rotateY = gsap.quickTo(plane.plane.rotation, "y", { duration: 0.1 });
const textWidth = textContainer.value?.offsetWidth || 0;
const containerWidth = referenceCanvas.value?.clientWidth || 0;
const duration = textWidth / 100; // Adjust the divisor to control the speed
tl.to(textContainer.value!, {
x: -textWidth,
duration: duration,
ease: "none",
}).set(textContainer.value!, { x: containerWidth });
const handleResize = () => {
const width = referenceCanvas.value?.clientWidth || 0;
const height = referenceCanvas.value?.clientHeight || 0;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
};
plane.plane.addEventListener("click", handleClick);
const handleMouseMove = (event: MouseEvent) => {
if (disableMouse.value) return;
const normalisedX = event.clientX / window.innerWidth - 0.5;
const normalisedY = event.clientY / window.innerHeight - 0.5;
transformX(normalisedX / 1.5);
transformY(-normalisedY / 1.5);
rotateX(-normalisedY);
rotateY(-normalisedX);
};
window.addEventListener("resize", handleResize);
window.addEventListener("mousemove", handleMouseMove);
const animate = () => {
// if (disableMouse.value) {
// plane.material.uniforms.uTime.value = 0;
// }
plane.material.uniforms.uTime.value += 0.1;
renderer.render(scene, camera);
requestAnimationFrame(animate);
};
animate();
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
window.removeEventListener("mousemove", handleMouseMove);
renderer.dispose();
});
});
</script>
<template>
<div class="root" @click="handleClick">
<img :src="skoda.src" class="img" />
<div class="text" ref="textContainer">
<Typography variant="h1" color="midnight">UNIDIR</Typography>
<Typography variant="h1" color="midnight">UNIDIR</Typography>
<Typography variant="h1" color="midnight">UNIDIR</Typography>
<Typography variant="h1" color="midnight">UNIDIR</Typography>
<Typography variant="h1" color="midnight">UNIDIR</Typography>
<Typography variant="h1" color="midnight">UNIDIR</Typography>
</div>
<div ref="referenceCanvas" class="reference-canvas"></div>
</div>
</template>
<style scoped lang="scss">
@use "../styles/_variables" as *;
.root {
background: $color-ghost;
color: #000;
position: relative;
height: 100vh;
}
.text {
position: absolute;
display: flex;
gap: 5rem;
top: 50%;
left: 0;
width: 100%;
transform: translateY(-50%);
z-index: 1;
}
.img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
view-transition-name: test-transition;
view-transition-duration: 55s;
opacity: 0;
pointer-events: none;
z-index: -1;
}
.reference-canvas {
position: relative;
width: 100%;
height: 100vh;
z-index: 2;
}
</style>
Editor is loading...
Leave a Comment