前端嘛 Logo
前端嘛
害羞鸡

害羞鸡

2026-01-29
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Paranoid vs shy birds</title>
  <style>
    #world {
      background: #e0dacd;
      position: absolute;
      width: 100%;
      height: 100%;
      overflow: hidden;
    }

    #instructions {
      position: absolute;
      width: 100%;
      top: 50%;
      margin: auto;
      margin-top: 50px;
      font-family: 'Open Sans', sans-serif;
      color: #b75505;
      font-size: .9em;
      text-transform: uppercase;
      text-align: center;
    }

    .lightInstructions {
      color: #b59b63;
      font-size: .8em;
    }

    #credits {
      position: absolute;
      width: 100%;
      margin: auto;
      bottom: 0;
      margin-bottom: 20px;
      font-family: 'Open Sans', sans-serif;
      color: #b59b63;
      font-size: 0.7em;
      text-transform: uppercase;
      text-align: center;
    }

    #credits a {
      color: #b59b63;
    }
  </style>

</head>

<body>
  <div id="world"></div>


  <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js'></script>
  <script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/OrbitControls.js'></script>
  <script>
    //THREEJS RELATED VARIABLES 

    var scene,
      camera,
      controls,
      fieldOfView,
      aspectRatio,
      nearPlane,
      farPlane,
      shadowLight,
      backLight,
      light,
      renderer,
      container;

    //SCENE
    var floor, brid1, bird2;

    //SCREEN VARIABLES

    var HEIGHT,
      WIDTH,
      windowHalfX,
      windowHalfY,
      mousePos = { x: 0, y: 0 };


    //INIT THREE JS, SCREEN AND MOUSE EVENTS

    function init() {
      scene = new THREE.Scene();
      HEIGHT = window.innerHeight;
      WIDTH = window.innerWidth;
      aspectRatio = WIDTH / HEIGHT;
      fieldOfView = 60;
      nearPlane = 1;
      farPlane = 2000;
      camera = new THREE.PerspectiveCamera(
        fieldOfView,
        aspectRatio,
        nearPlane,
        farPlane);
      camera.position.z = 1000;
      camera.position.y = 300;
      camera.lookAt(new THREE.Vector3(0, 0, 0));
      renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(WIDTH, HEIGHT);
      renderer.shadowMapEnabled = true;

      container = document.getElementById('world');
      container.appendChild(renderer.domElement);

      windowHalfX = WIDTH / 2;
      windowHalfY = HEIGHT / 2;

      window.addEventListener('resize', onWindowResize, false);
      document.addEventListener('mousemove', handleMouseMove, false);
      document.addEventListener('touchstart', handleTouchStart, false);
      document.addEventListener('touchend', handleTouchEnd, false);
      document.addEventListener('touchmove', handleTouchMove, false);
      /*
      controls = new THREE.OrbitControls( camera, renderer.domElement);
      //*/
    }

    function onWindowResize() {
      HEIGHT = window.innerHeight;
      WIDTH = window.innerWidth;
      windowHalfX = WIDTH / 2;
      windowHalfY = HEIGHT / 2;
      renderer.setSize(WIDTH, HEIGHT);
      camera.aspect = WIDTH / HEIGHT;
      camera.updateProjectionMatrix();
    }

    function handleMouseMove(event) {
      mousePos = { x: event.clientX, y: event.clientY };
    }

    function handleTouchStart(event) {
      if (event.touches.length > 1) {
        event.preventDefault();
        mousePos = { x: event.touches[0].pageX, y: event.touches[0].pageY };
      }
    }

    function handleTouchEnd(event) {
      mousePos = { x: windowHalfX, y: windowHalfY };
    }

    function handleTouchMove(event) {
      if (event.touches.length == 1) {
        event.preventDefault();
        mousePos = { x: event.touches[0].pageX, y: event.touches[0].pageY };
      }
    }

    function createLights() {
      light = new THREE.HemisphereLight(0xffffff, 0xffffff, .5)

      shadowLight = new THREE.DirectionalLight(0xffffff, .8);
      shadowLight.position.set(200, 200, 200);
      shadowLight.castShadow = true;
      shadowLight.shadowDarkness = .2;

      backLight = new THREE.DirectionalLight(0xffffff, .4);
      backLight.position.set(-100, 200, 50);
      backLight.shadowDarkness = .1;
      backLight.castShadow = true;

      scene.add(backLight);
      scene.add(light);
      scene.add(shadowLight);
    }

    //BIRD

    Bird = function () {

      this.rSegments = 4;
      this.hSegments = 3;
      this.cylRay = 120;
      this.bodyBirdInitPositions = [];
      this.vAngle = this.hAngle = 0;
      this.normalSkin = { r: 255 / 255, g: 222 / 255, b: 121 / 255 };
      this.shySkin = { r: 255 / 255, g: 157 / 255, b: 101 / 255 };
      this.color = { r: this.normalSkin.r, g: this.normalSkin.g, b: this.normalSkin.b };
      this.side = "left";

      this.shyAngles = { h: 0, v: 0 };
      this.behaviourInterval;
      this.intervalRunning = false;

      this.threegroup = new THREE.Group();

      // materials
      this.yellowMat = new THREE.MeshLambertMaterial({
        color: 0xffde79,
        shading: THREE.FlatShading
      });
      this.whiteMat = new THREE.MeshLambertMaterial({
        color: 0xffffff,
        shading: THREE.FlatShading
      });
      this.blackMat = new THREE.MeshLambertMaterial({
        color: 0x000000,
        shading: THREE.FlatShading
      });
      this.orangeMat = new THREE.MeshLambertMaterial({
        color: 0xff5535,
        shading: THREE.FlatShading
      });

      //WINGS

      this.wingLeftGroup = new THREE.Group();
      this.wingRightGroup = new THREE.Group();

      var wingGeom = new THREE.BoxGeometry(60, 60, 5);
      var wingLeft = new THREE.Mesh(wingGeom, this.yellowMat);
      this.wingLeftGroup.add(wingLeft);
      this.wingLeftGroup.position.x = 70;
      this.wingLeftGroup.position.z = 0;
      this.wingLeftGroup.rotation.y = Math.PI / 2;
      wingLeft.rotation.x = -Math.PI / 4;
      var wingRight = new THREE.Mesh(wingGeom, this.yellowMat);
      this.wingRightGroup.add(wingRight);
      this.wingRightGroup.position.x = -70;
      this.wingRightGroup.position.z = 0;
      this.wingRightGroup.rotation.y = -Math.PI / 2;
      wingRight.rotation.x = -Math.PI / 4;

      //BODY

      var bodyGeom = new THREE.CylinderGeometry(40, 70, 200, this.rSegments, this.hSegments);
      this.bodyBird = new THREE.Mesh(bodyGeom, this.yellowMat);
      this.bodyBird.position.y = 70;

      this.bodyVerticesLength = (this.rSegments + 1) * (this.hSegments);
      for (var i = 0; i < this.bodyVerticesLength; i++) {
        var tv = this.bodyBird.geometry.vertices[i];
        this.bodyBirdInitPositions.push({ x: tv.x, y: tv.y, z: tv.z });
      }

      this.threegroup.add(this.bodyBird);
      this.threegroup.add(this.wingLeftGroup);
      this.threegroup.add(this.wingRightGroup);


      // EYES

      this.face = new THREE.Group();
      var eyeGeom = new THREE.BoxGeometry(60, 60, 10);
      var irisGeom = new THREE.BoxGeometry(10, 10, 10);

      this.leftEye = new THREE.Mesh(eyeGeom, this.whiteMat);
      this.leftEye.position.x = -30;
      this.leftEye.position.y = 120;
      this.leftEye.position.z = 35;
      this.leftEye.rotation.y = -Math.PI / 4;

      this.leftIris = new THREE.Mesh(irisGeom, this.blackMat);
      this.leftIris.position.x = -30;
      this.leftIris.position.y = 120;
      this.leftIris.position.z = 40;
      this.leftIris.rotation.y = -Math.PI / 4;


      this.rightEye = new THREE.Mesh(eyeGeom, this.whiteMat);
      this.rightEye.position.x = 30;
      this.rightEye.position.y = 120;
      this.rightEye.position.z = 35;
      this.rightEye.rotation.y = Math.PI / 4;

      this.rightIris = new THREE.Mesh(irisGeom, this.blackMat);
      this.rightIris.position.x = 30;
      this.rightIris.position.y = 120;
      this.rightIris.position.z = 40;
      this.rightIris.rotation.y = Math.PI / 4;

      // BEAK

      var beakGeom = new THREE.CylinderGeometry(0, 20, 20, 4, 1);
      this.beak = new THREE.Mesh(beakGeom, this.orangeMat);
      this.beak.position.z = 65;
      this.beak.position.y = 70;
      this.beak.rotation.x = Math.PI / 2;

      this.face.add(this.rightEye);
      this.face.add(this.rightIris);
      this.face.add(this.leftEye);
      this.face.add(this.leftIris);
      this.face.add(this.beak);

      //FEATHERS

      var featherGeom = new THREE.BoxGeometry(10, 20, 5);
      this.feather1 = new THREE.Mesh(featherGeom, this.yellowMat);
      this.feather1.position.z = 55;
      this.feather1.position.y = 185;
      this.feather1.rotation.x = Math.PI / 4;
      this.feather1.scale.set(1.5, 1.5, 1);

      this.feather2 = new THREE.Mesh(featherGeom, this.yellowMat);
      this.feather2.position.z = 50;
      this.feather2.position.y = 180;
      this.feather2.position.x = 20;
      this.feather2.rotation.x = Math.PI / 4;
      this.feather2.rotation.z = -Math.PI / 8;

      this.feather3 = new THREE.Mesh(featherGeom, this.yellowMat);
      this.feather3.position.z = 50;
      this.feather3.position.y = 180;
      this.feather3.position.x = -20;
      this.feather3.rotation.x = Math.PI / 4;
      this.feather3.rotation.z = Math.PI / 8;

      this.face.add(this.feather1);
      this.face.add(this.feather2);
      this.face.add(this.feather3);
      this.threegroup.add(this.face);

      this.threegroup.traverse(function (object) {
        if (object instanceof THREE.Mesh) {
          object.castShadow = true;
          object.receiveShadow = true;
        }
      });

    }

    Bird.prototype.look = function (hAngle, vAngle) {
      this.hAngle = hAngle;
      this.vAngle = vAngle;

      this.leftIris.position.y = 120 - this.vAngle * 30;
      this.leftIris.position.x = -30 + this.hAngle * 10;
      this.leftIris.position.z = 40 + this.hAngle * 10;

      this.rightIris.position.y = 120 - this.vAngle * 30;
      this.rightIris.position.x = 30 + this.hAngle * 10;
      this.rightIris.position.z = 40 - this.hAngle * 10;

      this.leftEye.position.y = this.rightEye.position.y = 120 - this.vAngle * 10;

      this.beak.position.y = 70 - this.vAngle * 20;
      this.beak.rotation.x = Math.PI / 2 + this.vAngle / 3;

      this.feather1.rotation.x = (Math.PI / 4) + (this.vAngle / 2);
      this.feather1.position.y = 185 - this.vAngle * 10;
      this.feather1.position.z = 55 + this.vAngle * 10;

      this.feather2.rotation.x = (Math.PI / 4) + (this.vAngle / 2);
      this.feather2.position.y = 180 - this.vAngle * 10;
      this.feather2.position.z = 50 + this.vAngle * 10;

      this.feather3.rotation.x = (Math.PI / 4) + (this.vAngle / 2);
      this.feather3.position.y = 180 - this.vAngle * 10;
      this.feather3.position.z = 50 + this.vAngle * 10;


      for (var i = 0; i < this.bodyVerticesLength; i++) {
        var line = Math.floor(i / (this.rSegments + 1));
        var tv = this.bodyBird.geometry.vertices[i];
        var tvInitPos = this.bodyBirdInitPositions[i];
        var a, dy;
        if (line >= this.hSegments - 1) {
          a = 0;
        } else {
          a = this.hAngle / (line + 1);
        }
        var tx = tvInitPos.x * Math.cos(a) + tvInitPos.z * Math.sin(a);
        var tz = -tvInitPos.x * Math.sin(a) + tvInitPos.z * Math.cos(a);
        tv.x = tx;
        tv.z = tz;
      }
      this.face.rotation.y = this.hAngle;
      this.bodyBird.geometry.verticesNeedUpdate = true;

    }
    Bird.prototype.lookAway = function (fastMove) {
      var speed = fastMove ? .4 : 2;
      var ease = fastMove ? Strong.easeOut : Strong.easeInOut;
      var delay = fastMove ? .2 : 0;
      var col = fastMove ? this.shySkin : this.normalSkin;
      var tv = (-1 + Math.random() * 2) * Math.PI / 3;
      var beakScaleX = .75 + Math.random() * .25;
      var beakScaleZ = .5 + Math.random() * .5;

      if (this.side == "right") {
        var th = (-1 + Math.random()) * Math.PI / 4;
      } else {
        var th = Math.random() * Math.PI / 4;
      }
      _this = this;
      TweenMax.killTweensOf(this.shyAngles);
      TweenMax.to(this.shyAngles, speed, { v: tv, h: th, ease: ease, delay: delay });
      TweenMax.to(this.color, speed, { r: col.r, g: col.g, b: col.b, ease: ease, delay: delay });
      TweenMax.to(this.beak.scale, speed, { z: beakScaleZ, x: beakScaleX, ease: ease, delay: delay });

    }

    Bird.prototype.stare = function () {
      _this = this;
      var col = this.normalSkin;
      if (this.side == "right") {
        var th = Math.PI / 3;
      } else {
        var th = -Math.PI / 3;
      }
      TweenMax.to(this.shyAngles, 2, { v: -.5, h: th, ease: Strong.easeInOut });
      TweenMax.to(this.color, 2, { r: col.r, g: col.g, b: col.b, ease: Strong.easeInOut });
      TweenMax.to(this.beak.scale, 2, { z: .8, x: 1.5, ease: Strong.easeInOut });

    }

    //*   
    function createFloor() {
      floor = new THREE.Mesh(new THREE.PlaneBufferGeometry(1000, 1000), new THREE.MeshBasicMaterial({ color: 0xe0dacd }));
      floor.rotation.x = -Math.PI / 2;
      floor.position.y = -33;
      floor.receiveShadow = true;
      scene.add(floor);
    }

    function createBirds() {
      bird1 = new Bird();
      bird1.threegroup.position.x = 0;
      scene.add(bird1.threegroup);

      bird2 = new Bird();
      bird2.threegroup.position.x = -250;
      bird2.side = "right";
      bird2.threegroup.scale.set(.8, .8, .8);
      bird2.threegroup.position.y = -8;
      scene.add(bird2.threegroup);

      bird3 = new Bird();
      bird3.threegroup.position.x = 250;
      bird3.side = "left";
      bird3.threegroup.scale.set(.8, .8, .8);
      bird3.threegroup.position.y = -8;
      scene.add(bird3.threegroup);
    }


    function loop() {
      var tempHA = (mousePos.x - windowHalfX) / 200;
      var tempVA = (mousePos.y - windowHalfY) / 200;
      var userHAngle = Math.min(Math.max(tempHA, -Math.PI / 3), Math.PI / 3);
      var userVAngle = Math.min(Math.max(tempVA, -Math.PI / 3), Math.PI / 3);
      bird1.look(userHAngle, userVAngle);

      if (bird1.hAngle < -Math.PI / 5 && !bird2.intervalRunning) {
        bird2.lookAway(true);
        bird2.intervalRunning = true;
        bird2.behaviourInterval = setInterval(function () {
          bird2.lookAway(false);
        }, 1500);
      } else if (bird1.hAngle > 0 && bird2.intervalRunning) {
        bird2.stare();
        clearInterval(bird2.behaviourInterval);
        bird2.intervalRunning = false;

      } else if (bird1.hAngle > Math.PI / 5 && !bird3.intervalRunning) {
        bird3.lookAway(true);
        bird3.intervalRunning = true;
        bird3.behaviourInterval = setInterval(function () {
          bird3.lookAway(false);
        }, 1500);
      } else if (bird1.hAngle < 0 && bird3.intervalRunning) {
        bird3.stare();
        clearInterval(bird3.behaviourInterval);
        bird3.intervalRunning = false;
      }

      bird2.look(bird2.shyAngles.h, bird2.shyAngles.v);
      bird2.bodyBird.material.color.setRGB(bird2.color.r, bird2.color.g, bird2.color.b);

      bird3.look(bird3.shyAngles.h, bird3.shyAngles.v);
      bird3.bodyBird.material.color.setRGB(bird3.color.r, bird3.color.g, bird3.color.b);

      render();
      requestAnimationFrame(loop);
    }

    function render() {
      //controls.update();
      renderer.render(scene, camera);
    }


    init();
    createLights();
    createFloor();
    createBirds();
    loop();
  </script>

</body>

</html>