Untitled

mail@pastecode.io avatar
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