更多有趣示例 盡在小紅磚社區
示例
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="theme-color" content="#ff24a3">
<meta name="application-name" content="Ready Player One">
<meta name="description" content="Threejs Audio Visualizer #6">
<meta name="keywords" content="Ready Player One, SynthWave, Threejs, Javascript, Ion Drimba Filho, WebAudio, Visualizer">
<meta name="subject" content="Threejs Audio Visualizer #6">
<meta name="copyright" content="Ion Drimba Filho">
<meta name="robots" content="index,follow">
<meta name="topic" content="">
<meta name="summary" content="ThreeJs Audio Visualizer #6">
<meta name="author" content="Ion Drimba Filho">
<meta name="url" content="http://iondrimba.github.io/threejs-audio-visualizer-six/public/index.html">
<meta name="pagename" content="Ready Player One">
<meta name="category" content="">
<meta name="coverage" content="Worldwide">
<meta name="distribution" content="Global">
<meta name="rating" content="General">
<meta name="subtitle" content="ThreeJs Audio Visualizer #6">
<meta name="target" content="all">
<meta http-equiv="cleartype" content="on">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="http://iondrimba.github.io/threejs-audio-visualizer-six/public/index.html">
<meta name="twitter:creator" content="Ion Drimba Filho">
<meta name="twitter:title" content="Ready Player One">
<meta name="twitter:description" content="ThreeJs Audio Visualizer #6">
<meta name="twitter:image:src" content="https://raw.githubusercontent.com/iondrimba/images/master/six.PNG">
<meta property="og:url" content="http://iondrimba.github.io/threejs-audio-visualizer-six/public/index.html">
<meta property="og:type" content="website">
<meta property="og:title" content="Ready Player One">
<meta property="og:image" content="https://raw.githubusercontent.com/iondrimba/images/master/six.PNG">
<meta property="og:description" content="ThreeJs Audio Visualizer #6">
<meta property="og:site_name" content="Ready Player One">
<meta property="article:author" content="https://iondrimbafilho.me/">
<meta property="article:publisher" content="https://iondrimbafilho.me/">
<meta itemprop="name" content="Ready Player One">
<meta itemprop="description" content="ThreeJs Audio Visualizer #6">
<meta itemprop="image" content="https://raw.githubusercontent.com/iondrimba/images/master/six.PNG">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<title>Ready Player One</title>
<link href="https://fonts.googleapis.com/css?family=Ropa+Sans" rel="stylesheet">
</head>
<body>
<button class="play-intro">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 32 32"
data-tags="play,media control">
<g transform="scale(0.03125 0.03125)">
<path d="M192 0v1024l640-511.264-640-512.736z" />
</g>
</svg>
</button>
<div class="credits" title="Music by Droid Bishop">
<h1>
<a href="https://droidbishop.bandcamp.com/album/lost-in-symmetry" target="_blank" rel="noopener noreferrer">Droid Bishop </a>/
<a href="https://droidbishop.bandcamp.com/track/ready-player-one" target="_blank" rel="noopener noreferrer">Ready Player One</a>
</h1>
<div class="controls">
<button class="play">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="32" height="32" viewBox="0 0 25 32"
data-tags="play,media control">
<g transform="scale(0.03125 0.03125)">
<path d="M192 0v1024l640-511.264-640-512.736z" />
</g>
</svg>
</button>
<button class="pause">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"
data-tags="pause,media control">
<g transform="scale(0.03125 0.03125)">
<path d="M352 0h-192c-17.696 0-32 14.336-32 32v960c0 17.696 14.304 32 32 32h192c17.696 0 32-14.304 32-32v-960c0-17.664-14.304-32-32-32zM864 0h-192c-17.696 0-32 14.336-32 32v960c0 17.696 14.304 32 32 32h192c17.696 0 32-14.304 32-32v-960c0-17.664-14.304-32-32-32z"
/>
</g>
</svg>
</button>
</div>
</div>
<div class="loader"></div>
<audio id="audio" crossOrigin="anonymous"></audio>
</body>
</html>
CSS
html, body {
margin: 0;
padding: 0;
font-family: 'Ropa Sans', sans-serif;
background-color: #DDA0DD;
color: #0000FF;
box-sizing: border-box;
overflow: hidden;
cursor: -webkit-grab;
cursor: -moz-grab;
}
canvas {
width: 100%;
height: 100%;
}
h1 {
padding: 0;
margin: 0;
color: inherit;
}
button {
background-color: transparent;
border: 0;
cursor: pointer;
}
.play-intro {
position: absolute;
top: 50%;
left: 50%;
display: none;
transform: translate(-50%, -50%);
height: 100px;
width: 100px;
cursor: pointer;
}
.play-intro svg {
width: 100%;
fill: #ffffff;
}
.controls {
position: absolute;
right: 30px;
top: 10px;
}
.controls svg {
fill: #ffffff;
}
.play, .pause {
display: none;
}
.control-show {
display: block;
}
.loader {
position: absolute;
width: 100%;
height: 100%;
top: 0;
z-index: 0;
transform: scale(0, 1);
transform-origin: left;
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
background: linear-gradient(to right, #0346fd, #008cff);
}
a {
color: inherit;
text-decoration: none;
}
.credits {
margin: 20px;
position: absolute;
z-index: 1;
bottom: 10px;
width: 100%;
}
.credits h1 {
font-size: 20pt;
width: 80%;
line-height: 1.4;
}
.credits .controls svg {
width: 50px;
}
.removeLoader {
transition: transform .3s .3s ease-out;
}
@media screen and (min-width: 768px) {
.credits {
margin-left: 20px;
margin-top: 20px;
position: absolute;
z-index: 1;
bottom: inherit;
width: 100%;
}
.credits h1 {
font-size: 20pt;
width: auto;
line-height: 1.4;
}
.credits .controls {
right: 50px;
}
.credits .controls svg {
width: auto;
}
}
JS
class Loader {
constructor() {
this.callback = null;
}
load(file) {
const request = new XMLHttpRequest();
request.open('GET', file, true);
request.onprogress = (evt) => {
let percent = Math.floor((evt.loaded / evt.total) * 100);
this.callback(percent);
};
request.onload = () => { this.complete(file); };
request.send();
}
progress(callback) { this.callback = callback; };
complete() { }
}
const { physics, value } = window.popmotion;
class App {
constructor() {
this.songFile = 'https://iondrimbafilho.me/ready_player_one.mp3';
this.percent = 0;
this.playing = false;
this.volume = 1;
this.sceneBackGroundColor = 0x7FFF00;
this.spotLightColor = 0x7FFF00;
this.ambientLightColor = 0xADFF2F;
this.groupSpheres = new THREE.Object3D();
this.groupCones = new THREE.Object3D();
this.tiles = [];
this.loader = new Loader();
this.loader.progress((percent) => { this.progress(percent); });
this.loaderBar = document.querySelector('.loader');
this.loader.load(this.songFile);
this.playIntro = document.querySelector('.play-intro');
this.loader.complete = this.complete.bind(this);
}
progress(percent) {
this.loaderBar.style.transform = `scale(${(percent / 100) + .1}, 1.1)`;
if (percent === 100) {
setTimeout(() => {
requestAnimationFrame(() => {
this.playIntro.classList.add('control-show');
this.loaderBar.classList.add('removeLoader');
this.loaderBar.style.transform = 'scale(1, 0)';
})
}, 800);
}
}
complete(file) {
this.setupAudio();
this.addSoundControls();
this.createScene();
this.createCamera();
this.addAmbientLight(this.ambientLightColor);
this.addSpotLight(new THREE.SpotLight(this.spotLightColor), 50, 400, 50);
this.addCameraControls();
this.playSound(file);
this.addEventListener();
this.addGrid();
this.floorShape = this.createShape();
this.createGround(this.floorShape);
this.animate();
document.addEventListener('visibilitychange', (evt) => {
if(evt.target.hidden) {
this.pause();
} else {
this.play();
}
}, false);
}
createShape() {
const size = 200;
const vectors = [
new THREE.Vector2(-size, size),
new THREE.Vector2(-size, -size),
new THREE.Vector2(size, -size),
new THREE.Vector2(size, size)
];
const shape = new THREE.Shape(vectors);
shape.autoClose = true;
this.createHoles(shape, {
cols: {
start: -4,
end: 5
},
rows: {
start: -5,
end: 0
},
elementCreate: this.createSphere,
onAdd: (element, x, y, i, j) => {
this.addPhysics(element);
element.position.set(x - (i * 1), -.5, y - (j * 1));
this.groupSpheres.add(element);
this.tiles.push(element);
}
});
this.createHoles(shape, {
cols: {
start: -4,
end: 5
},
rows: {
start: 1,
end: 6
},
elementCreate: this.createCone,
onAdd: (element, x, y, i, j) => {
element.position.set(x - (i * 1), -10, y - (j * 1));
this.groupCones.add(element);
this.tiles.push(element);
}
});
this.scene.add(this.groupSpheres);
this.scene.add(this.groupCones);
return shape;
}
createHoles(shape, props) {
const radius = .5;
const finalPos = (i) => {
return (-i * 1);
};
for (let i = props.cols.start; i < props.cols.end; i++) {
for (let j = props.rows.start; j < props.rows.end; j++) {
const holePath = new THREE.Path();
const x = finalPos(i);
const y = finalPos(j);
holePath.moveTo(x, y);
holePath.ellipse(x, y, radius, radius, 0, Math.PI * 2);
holePath.autoClose = true;
shape.holes.push(holePath);
props.onAdd(props.elementCreate(), x, y, i, j);
}
}
}
createGround(shape) {
const materials = [
new THREE.MeshStandardMaterial({
color: 0xff1876,
roughness: 1,
metalness: 0.1,
flatShading: THREE.FlatShading
}),
new THREE.MeshStandardMaterial({
color: 0xf324ff,
flatShading: THREE.FlatShading
})
];
const props = {
steps: 1,
depth: 4,
bevelEnabled: false
};
const geometry = new THREE.ExtrudeGeometry(shape, props);
const bufferGeometry = new THREE.BufferGeometry().fromGeometry(geometry);
const mesh = new THREE.Mesh(geometry, materials);
mesh.rotation.set(Math.PI * 0.5, 0, 0);
this.scene.add(mesh);
}
drawWave() {
let velocity = 0;
let freq = 0;
let scale = 0;
if (this.playing) {
this.analyser.getByteFrequencyData(this.frequencyData);
for (let i = 0; i < this.frequencyData.length; i++) {
freq = this.frequencyData[i];
if (this.tiles[i] && this.tiles[i].hasPhysics && !this.tiles[i].falling) {
velocity = this.map(freq, 0, 255, -50, 80);
this.tiles[i].gravity
.set(Math.min(0, this.tiles[i].posY.get()))
.setVelocity(-(velocity));
this.tiles[i].falling = true;
}
if (this.tiles[i] && !this.tiles[i].hasPhysics) {
scale = this.map(freq, 0, 255, 0.001, 2);
TweenMax.to(this.tiles[i].scale, .4, { y: scale });
}
}
}
}
createSphere() {
const geometry = new THREE.SphereGeometry(.5, 16, 16);
const material = new THREE.MeshPhongMaterial({
color: 0x3a88ff,
specular: 0xffffff,
shininess: 2,
emissive: 0x0b57b9,
side: THREE.DoubleSide
});
let sphere = new THREE.Mesh(geometry, material);
sphere.position.y = 0;
sphere.castShadow = true;
sphere.receiveShadow = true;
return sphere;
}
addPhysics(element) {
const posY = value(10, (v) => {
element.position.y = -Math.min(10, v);
});
element.posY = posY;
element.falling = false;
element.hasPhysics = true;
const gravity = physics({
acceleration: 250,
restSpeed: false
}).start((v) => {
if (element.falling) {
if (v < 10) {
posY.update(v);
} else {
element.falling = false;
}
}
});
element.gravity = gravity;
}
createCone() {
const geometry = new THREE.CylinderGeometry(.4, .4, 10, 59);
const material = new THREE.MeshPhongMaterial({
color: 0x12ff31,
flatShading: THREE.FlatShading,
side: THREE.DoubleSide
});
const obj = new THREE.Mesh(geometry, material);
obj.castShadow = true;
obj.receiveShadow = true;
obj.position.y = 5;
const pivot = new THREE.Object3D();
pivot.add(obj);
pivot.size = 2;
return pivot;
}
addSpotLight(spotLight, x, y, z) {
this.spotLight = spotLight;
this.spotLight.position.set(x, y, z);
this.spotLight.castShadow = true;
this.scene.add(this.spotLight);
}
addAmbientLight(color) {
const light = new THREE.AmbientLight(color, .5);
this.scene.add(light);
}
addGrid() {
const size = 25;
const divisions = size;
const gridHelper = new THREE.GridHelper(size, divisions);
gridHelper.position.set(0, 0, 0);
gridHelper.material.opacity = 0;
gridHelper.material.transparent = true;
this.scene.add(gridHelper);
}
playSound(file) {
this.audioElement.src = file;
}
map(value, start1, stop1, start2, stop2) {
return (value - start1) / (stop1 - start1) * (stop2 - start2) + start2
}
addSoundControls() {
this.btnPlay = document.querySelector('.play');
this.btnPause = document.querySelector('.pause');
this.btnPlay.addEventListener('click', this.play.bind(this));
this.btnPause.addEventListener('click', this.pause.bind(this));
}
pause() {
this.audioElement.pause();
this.btnPause.classList.remove('control-show');
this.btnPlay.classList.add('control-show');
}
play() {
this.audioCtx.resume();
this.audioElement.play();
this.btnPlay.classList.remove('control-show');
this.btnPause.classList.add('control-show');
}
createScene() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(this.sceneBackGroundColor);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.scene1 = new THREE.Scene();
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(this.renderer.domElement);
}
addEventListener() {
this.playIntro.addEventListener('click', (evt) => {
evt.currentTarget.classList.remove('control-show');
this.play();
});
document.body.addEventListener('mouseup', () => {
requestAnimationFrame(() => {
document.body.style.cursor = '-moz-grab';
document.body.style.cursor = '-webkit-grab';
});
});
document.body.addEventListener('mousedown', () => {
requestAnimationFrame(() => {
document.body.style.cursor = '-moz-grabbing';
document.body.style.cursor = '-webkit-grabbing';
});
});
document.body.addEventListener('keyup', (evt) => {
if (evt.keyCode === 32 || evt.code === 'Space') {
this.playIntro.classList.remove('control-show');
this.playing ? this.pause() : this.play();
}
});
}
createCamera() {
this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
this.camera.position.set(26, 23, -17);
this.scene.add(this.camera);
}
addCameraControls() {
this.controls = new THREE.OrbitControls(this.camera);
}
animate() {
this.controls.update();
this.renderer.render(this.scene, this.camera);
this.drawWave();
requestAnimationFrame(this.animate.bind(this));
}
radians(degrees) {
return degrees * Math.PI / 180;
}
onResize() {
const ww = window.innerWidth;
const wh = window.innerHeight;
this.camera.aspect = ww / wh;
this.camera.updateProjectionMatrix();
this.renderer.setSize(ww, wh);
}
setupAudio() {
this.audioElement = document.getElementById('audio');
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
this.analyser = this.audioCtx.createAnalyser();
this.analyser.smoothingTimeConstant = 0.4;
this.source = this.audioCtx.createMediaElementSource(this.audioElement);
this.source.connect(this.analyser);
this.source.connect(this.audioCtx.destination);
this.bufferLength = this.analyser.frequencyBinCount;
this.waveform = new Uint8Array(this.analyser.fftSize);
this.frequencyData = new Uint8Array(this.analyser.fftSize);
this.audioElement.volume = this.volume;
this.audioElement.addEventListener('playing', () => {
this.playing = true;
});
this.audioElement.addEventListener('pause', () => {
this.playing = false;
});
this.audioElement.addEventListener('ended', () => {
this.playing = false;
this.pause();
});
}
}
window.app = new App();
window.addEventListener('resize', app.onResize.bind(app));