Three.js 3D时钟(源码)

[复制链接]
七夏(UID:1) 发表于 2025-1-1 09:03:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

使用 Three.js 的 WebGL 小实验。可交互的3D时钟。

图片

实现代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      *,
      *::before,
      *::after {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        touch-action: none;
      }




      body {
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
        background-color: #222;
        overflow: hidden;
      }
</style>
  </head>
  <body>
    <script type="module">
      import * as THREE from "https://cdn.skypack.dev/three@0.140.0";
      import { OrbitControls } from "https://cdn.skypack.dev/three@0.120.0/examples/jsm/controls/OrbitControls.js";
      import * as dat from "https://cdn.skypack.dev/dat.gui@0.7.9";
      class Ring {
        constructor(options) {
          var _a, _b, _c, _d;
          const size = (_a = options.size) !== null && _a !== void 0 ? _a : 2;
          const thickness = (_b = options.thickness) !== null && _b !== void 0 ? _b : 0.75;
          const depth = (_c = options.depth) !== null && _c !== void 0 ? _c : 0.5;
          const extrudeOptions = { depth, bevelEnabled: false, curveSegments: 500 };
          const arc = new THREE.Shape();
          arc.absarc(0, 0, size, 0, Math.PI * 2, false);
          const hole = new THREE.Path();
          hole.absarc(0, 0, size - thickness, 0, Math.PI * 2, true);
          arc.holes.push(hole);
          const geometry = new THREE.ExtrudeGeometry(arc, extrudeOptions);
          const material = new THREE.MeshStandardMaterial({
            side: THREE.DoubleSide,
            metalness: 1,
            roughness: 0.15,
            wireframe: (_d = options.wireframe) !== null && _d !== void 0 ? _d : false,
          });
          const mesh = new THREE.Mesh(geometry, material);
          mesh.castShadow = true;
          mesh.receiveShadow = true;
          this._mesh = mesh;
        }
        get mesh() {
          return this._mesh;
        }
      }
      class ClockLine {
        constructor(options) {
          var _a;
          const shape = new THREE.Shape();
          shape.moveTo(0, 0);
          shape.lineTo(0, 1);
          shape.lineTo(1, 1);
          shape.lineTo(1, 0);
          shape.lineTo(0, 0);
          const geometry = new THREE.ExtrudeBufferGeometry(shape);
          const material = new THREE.MeshStandardMaterial({
            side: THREE.DoubleSide,
            metalness: 1,
            roughness: 1,
            wireframe: (_a = options.wireframe) !== null && _a !== void 0 ? _a : false,
          });
          const mesh = new THREE.Mesh(geometry, material);
          mesh.scale.set(options.width, options.height, options.depth);
          mesh.position.z = options.depth;
          mesh.castShadow = true;
          mesh.receiveShadow = true;
          this._mesh = mesh;
        }
        get mesh() {
          return this._mesh;
        }
      }
      //#endregion
      //#region GUI
      const gui = new dat.GUI();
      //#endregion
      //#region Renderer
      const renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.toneMapping = THREE.ACESFilmicToneMapping;
      renderer.outputEncoding = THREE.sRGBEncoding;
      renderer.shadowMap.enabled = false;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      renderer.setSize(innerWidth, innerHeight);
      // renderer.setClearColor("#fff");
      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
      document.body.appendChild(renderer.domElement);
      //#endregion
      //#region Camera
      const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 2000);
      camera.position.set(0, 0, 15);
      //#endregion
      //#region Controls
      const controls = new OrbitControls(camera, renderer.domElement);
      controls.update();
      gui.add(controls, "enabled").name("Orbit controls").setValue(false).onChange(controls.reset);
      //#endregion
      //#region Scene
      const scene = new THREE.Scene();
      //#endregion
      //#region Lights
      const lightOne = new THREE.SpotLight(new THREE.Color("#0ff"), 1);
      lightOne.position.z = 30;
      const lightTwo = new THREE.SpotLight(new THREE.Color("#009795"), 1);
      lightTwo.position.set(0, 2, 20);
      [lightOne, lightTwo].map((light, i) => {
        light.castShadow = true;
        light.shadow.mapSize.width = 512;
        light.shadow.mapSize.height = 512;
        gui.addColor(light, "color").name(`Light ${i + 1} color`);
        scene.add(light);
      });
      //#endregion
      //#region Resize handler
      window.addEventListener("resize", () => {
        camera.aspect = innerWidth / innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(innerWidth, innerHeight);
      });
      //#endregion
      //#region Mouse positioning
      const mousePosition = new THREE.Vector2(0, 0);
      function updateMousePosition(e) {
        const x = e.clientX - innerWidth * 0.5;
        const y = e.clientY - innerHeight * 0.5;
        mousePosition.x = x * 0.001;
        mousePosition.y = y * 0.001;
      }
      window.addEventListener("pointermove", updateMousePosition);
      //#endregion
      //#region Rings
      const ringsConfiguration = { amount: 4, innerSpace: 3 };
      const ringsThickness = 0.75;
      function setRings({ amount, innerSpace }) {
        const newRings = [];
        for (let size = innerSpace; size <= amount + innerSpace; size++) {
          newRings.push(new Ring({ size: size + 1, thickness: ringsThickness, color: "#eee" }).mesh);
        }
        newRings.forEach((ring) => scene.add(ring));
        return newRings;
      }
      const rings = setRings(ringsConfiguration);
      //#endregion
      //#region Clock hands
      const hoursHand = new ClockLine({ width: 0.075, height: 1, depth: 0.1 }).mesh;
      const minutesHand = new ClockLine({ width: 0.075, height: 1.75, depth: 0.1 }).mesh;
      const secondsHand = new ClockLine({ width: 0.025, height: 1.5, depth: 0.1 }).mesh;
      const clockCenter = new THREE.Mesh(new THREE.SphereGeometry(0.2), new THREE.MeshStandardMaterial({ metalness: 1, roughness: 1 }));
      clockCenter.position.z = 0.15;
      scene.add(hoursHand, minutesHand, secondsHand, clockCenter);
      //#endregion
      //#region Initialization
      function init() {
        // Rings animation
        // ------------------------------
        rings.forEach((ring, i) => {
          ring.rotation.x = ring.rotation.x * 0.95 + mousePosition.y * 0.025 * (i + 0.05);
          ring.rotation.y = ring.rotation.y * 0.95 + mousePosition.x * 0.025 * (i + 0.05);
        });
        // Clock hands rotation
        // ------------------------------
        const date = new Date();
        const hours = date.getHours();
        const minutes = date.getMinutes();
        const seconds = date.getSeconds();
        const milliseconds = date.getMilliseconds();
        const smoothSeconds = seconds + milliseconds / 1000;
        hoursHand.rotation.z = -THREE.MathUtils.degToRad(0.5 * (60 * hours + minutes));
        minutesHand.rotation.z = -THREE.MathUtils.degToRad(6 * minutes);
        secondsHand.rotation.z = -THREE.MathUtils.degToRad(6 * smoothSeconds);
        // Renderer & controls
        // ------------------------------
        controls.update();
        renderer.render(scene, camera);
        requestAnimationFrame(init);
      }
      init();
      //#endregion
</script>
  </body>
</html>

小时候,看腻了农村的牛和马,长大后,来到了城里,才知道原来到处都是牛马!
全部回复0 显示全部楼层
暂无回复,精彩从你开始!

快速回帖

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于楼主

管理员
  • 主题

    898
  • 回答

    341
  • 积分

    2448
虚位以待,此位置招租

商务推广

    此位置招租 黑粉猫影院-免费看电影 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租