Untitled
unknown
plain_text
8 months ago
7.3 kB
4
Indexable
Never
<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>
Leave a Comment