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