react+three打造支持键盘和声音输出的三维汉堡架子鼓弹奏效果代码

代码语言:html

所属分类:三维

代码描述:react+three打造支持键盘和声音输出的三维汉堡架子鼓效果代码,从面包汉堡拆封成架子鼓动画后,点击鼠标或键盘即可进行弹奏,有声音输出

代码标签: 键盘 声音 输出 三维 汉堡 架子 弹奏 效果

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>

  <meta charset="UTF-8">

  
  
  
<style>
@import url("https://fonts.googleapis.com/css2?family=Metal+Mania&display=swap");
.App {
  text-align: center;
  display: flex;
  width: 100%;
  height: 100%;
}

.component-carousel {
  position: absolute;
  top: 20px;
  right: 20px;
  flex-direction: column;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  text-decoration: none;
  transition: transform 0.2s ease-in-out;
}

.component-carousel img {
  width: 187px;
  margin-top: 10px;
}

.component-carousel:hover {
  text-decoration: underline;
}

.component-carousel:hover img {
  transform: translate(2px, 2px);
}

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  background-color: #142522;
  font-family: sans-serif;
}

.burger-drum {
  overflow: hidden;
  position: relative;
}

.burger-drum .container {
  width: 100vw;
  max-width: 1800px;
  height: 60vw;
  min-height: 400px;
  max-height: 100vh;
}

.info {
  position: absolute;
  bottom: 40px;
  left: 40px;
  width: 40vw;
  max-width: 500px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  flex-direction: column;
  transform: translateX(-50vw);
  transition: transform 0.5s ease-in-out;
}

.presents {
  margin-bottom: 20px;
}

.burger .info {
  transform: translateX(0);
}

h1 {
  font-family: "Metal Mania", cursive;
  font-size: clamp(40px, 6vw, 100px);
  letter-spacing: 5px;
  margin: 0;
  white-space: nowrap;
}

p {
  text-align: left;
  font-size: 20px;
  margin-bottom: 40px;
  /* letter-spacing: 0.03em; */
  line-height: 130%;
}

button {
  margin-right: 40px;
  background-color: rgba(0, 0, 0, 0.3);
  padding: 10px 30px;
  border: solid 1px white;
  color: white;
  font-size: 30px;
  /* text-transform: uppercase; */
  letter-spacing: 3px;
  font-family: "Metal Mania", cursive;
  cursor: pointer;
  outline: solid 1px transparent;
  outline-offset: 0px;
  transition-property: background-color, outline-offset, outline;
  transition-duration: 0.1s;
  transition-timing-function: ease-in-out;
}

button:hover:not(:disabled) {
  background-color: darkred;
  outline: solid 1px white;
  outline-offset: 2px;
}

button:disabled {
  opacity: 0.3;
}

.controls {
  position: absolute;
  bottom: 40px;
  right: 40px;
  width: calc(100% - 80px);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  transform: translateY(200%);
  transition: transform 0.5s ease-in-out;
}

.drums .controls {
  transform: translateY(0%);
}

.loader {
  position: absolute;
  top: 50%;
  left: 50%;
  font-size: 50px;
  font-family: "Metal Mania", cursive;
  transform: translate(-50%, -50%);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease-in-out;
}

.loading .loader {
  opacity: 10;
}

.controls span {
  text-transform: uppercase;
  padding: 2px;
  display: inline-block;
  border: 2px solid white;
  border-bottom: 4px solid white;
  border-radius: 6px;
  width: 20px;
  height: 20px;
  margin-left: 10px;
}
</style>




</head>

<body >
  <div id="root"></div>
  <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/babel.min.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/react.production.17.1.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/react-dom.production.17.1.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/gsap.3.5.2.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/MotionPathPlugin.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/three.126.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/GLTFLoader.js"></script>
      <script  type="text/babel">

 const {useEffect, useRef, useState } = React;
//import React, { useEffect, useRef, useState } from 'https://cdn.skypack.dev/react@17';
//import ReactDOM from 'https://cdn.skypack.dev/react-dom@17';
//import gsap from "https://cdn.skypack.dev/gsap@3.6.1";
//import { MotionPathPlugin } from "https://cdn.skypack.dev/gsap@3.6.1/MotionPathPlugin";
//import * as THREE from "https://cdn.skypack.dev/three@0.129.0";
//import { GLTFLoader } from 'https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders/GLTFLoader.js';

const FILES = {
  drum: "//repo.bfw.wiki/bfwrepo/threemodel/drums.glb",
  burger: "//repo.bfw.wiki/bfwrepo/threemodel/burger.glb",
  snare: "//repo.bfw.wiki/bfwrepo/sound/drums/snare.mp3",
  bass: "//repo.bfw.wiki/bfwrepo/sound/drums/bass.mp3",
  tom1: "//repo.bfw.wiki/bfwrepo/sound/drums/tom-1.mp3",
  tom2: "//repo.bfw.wiki/bfwrepo/sound/drums/tom-2.mp3",
  tom3: "//repo.bfw.wiki/bfwrepo/sound/drums/tom-3.mp3",
  cymbal1: "//repo.bfw.wiki/bfwrepo/sound/drums/cymbal-1.mp3",
  cymbal2: "//repo.bfw.wiki/bfwrepo/sound/drums/cymbal-2.mp3" };


gsap.registerPlugin(MotionPathPlugin);

function App() {
  return /*#__PURE__*/(
    React.createElement("div", { className: "App" }, /*#__PURE__*/
    React.createElement(BurgerDrum, null), /*#__PURE__*/
));



}

function BurgerDrum() {

  const mount = useRef();
  const [view, setView] = useState('loading');
  const [manager, setManager] = useState(null);

  const init = () => {
    if (mount.current)
    {
      const stage = new Stage(mount.current);
      const _manager = new Manager(stage, view, setView);

      setManager(_manager);

      return () => {
        stage.destroy();
        _manager.fire();
      };
    }
  };

  const toggleView = () => {setView(view === 'burger' ? 'drums' : 'burger');};

  useEffect(init, [mount]);
  useEffect(() => {if (manager) manager.updateView(view);}, [view, manager]);

  return /*#__PURE__*/(
    React.createElement("div", { className: `burger-drum ${view}` }, /*#__PURE__*/
    React.createElement("div", { className: "container", ref: mount }), /*#__PURE__*/
    React.createElement("div", { className: "info" }, /*#__PURE__*/
    React.createElement("p", { className: "presents" }, "Buns N' Roses presents:"), /*#__PURE__*/
    React.createElement("h1", null, "Beat Burger"), /*#__PURE__*/
    React.createElement("p", null, "Our signature burger, inspired by legendary drummer ", /*#__PURE__*/React.createElement("i", null, "[your favorite drummer here]"), ". Order online now ", /*#__PURE__*/React.createElement("i", null, "(or don't because this is all pretend)"), " or transform this burger into a drum kit and play some sweet beats!"), /*#__PURE__*/
    React.createElement("div", { className: "buttons" }, /*#__PURE__*/
    React.createElement("button", { disabled: true }, "Order now"), /*#__PURE__*/
    React.createElement("button", { onClick: toggleView }, "Play"))), /*#__PURE__*/


    React.createElement("div", { className: "controls" }, /*#__PURE__*/
    React.createElement("button", { onClick: toggleView }, "Back"), /*#__PURE__*/
    React.createElement("div", null, "Tap the drums or use these keyboard keys: ", /*#__PURE__*/React.createElement("span", null, "Q"), /*#__PURE__*/React.createElement("span", null, "W"), /*#__PURE__*/React.createElement("span", null, "E"), /*#__PURE__*/React.createElement("span", null, "R"), /*#__PURE__*/React.createElement("span", null, "T"), /*#__PURE__*/React.createElement("span", null, "Y"), /*#__PURE__*/React.createElement("span", null, "U"))), /*#__PURE__*/
    React.createElement("div", { className: "loader" }, "\uD83E\uDD18 Loading \uD83E\uDD18")));


}

class Manager {

  constructor(stage, view, setView) {
    this.stage = stage;
    this.setView = setView;
    this.debug = false;
    this.sounds = {};
    this.raycaster = new THREE.Raycaster();
    this.view = view;

    this.models = {
      burger: {
        file: FILES.burger,
        items: {} },

      drumkit: {
        file: FILES.drum,
        items: {} } };



    this.setupSpotLights();
    this.loadModels();
  }

  setupSpotLights() {
    this.spotlights = {
      left: { light: new THREE.SpotLight('white', 0), target: new THREE.Object3D() },
      right: { light: new THREE.SpotLight('white', 0), target: new THREE.Object3D() } };


    const sides = ['left', 'right'];
    sides.forEach(side => {
      const spotLight = this.spotlights[side].light;
      const target = this.spotlights[side].target;

      spotLight.penumbra = 0.1;
      spotLight.angle = 0.6;

      spotLight.castShadow = true;
      spotLight.shadow.mapSize.width = 1024;
      spotLight.shadow.mapSize.height = 1024;
      spotLight.shadow.camera.near = 1;
      spotLight.shadow.camera.far = 10;
      spotLight.shadow.camera.fov = 50;

      spotLight.target = target;

      this.stage.add(spotLight);
      this.stage.add(target);
    });

    this.spotlights.left.light.position.set(-3, 5, 1);
    this.spotlights.right.light.position.set(3, 5, 1);

  }

  playSound(id) {
    const sound = this.sounds[id];
    if (this.view === 'drums' && sound)
    {
      sound.audio.currentTime = 0;
      sound.audio.play();
      gsap.fromTo(sound.object.position, { ...sound.from }, { ...sound.to, ease: 'elastic' });
    }
  }

  setupSounds() {

    const testObjects = [];

    for (const [name, drum] of Object.entries(drumSettings)) {
      if (drum.sound) {

        const sound = {
          audio: new Audio(drum.sound),
          object: this.models.burger.items[name],
          from: { [drum.direction]: drum.position[drum.direction] - 0.3 },
          to: { [drum.direction]: drum.position[drum.direction] } };


        // test doesn't work on groups, so need to add children and rename
        if (sound.object instanceof THREE.Mesh)
        {
          testObjects.push(sound.object);
        } else
        {
          sound.object.children.forEach(obj => {
            obj.name = sound.object.name;
            testObjects.push(obj);
          });
        }

        this.sounds[drum.key] = sound;
        this.sounds[name] = sound;
      }
    }

    document.addEventListener("keydown", event => {this.playSound(event.key);});

    this.stage.container.addEventListener('click', (event) =>
    {
      const mouse = {
        x: event.offsetX / this.stage.size.width * 2 - 1,
        y: -(event.offsetY / this.stage.size.height) * 2 + 1 };


      this.raycaster.setFromCamera(mouse, this.stage.camera);
      const intersects = this.raycaster.intersectObjects(testObjects);

      if (intersects.length) {
        this.playSound(intersects[0].object.name);
      }
    });

  }

  loadModels() {
    const loadingManager = new THREE.LoadingManager(() => {
      this.setupSounds();
      this.setView('bu.........完整代码请登录后点击上方下载按钮下载查看

网友评论0