Красивая анимация (яркие линии) вполне может подойти как абстракция пожалуй к любого стиля музыки и будет не плохо гармонировать с музыкальным проигрывателем.
Пишем стиль:
Пишем jquery:
Смотрим результат:
Пишем стиль:
CSS:
html, body {
margin: 0;
width: 100%;
height: 100%;
}
canvas {
position: fixed;
width: 100%;
height: 100%;
}
JavaScript:
const simplex = new SimplexNoise();
const { PI } = Math;
function App() {
const conf = {
fov: 75,
cameraZ: 140,
numLines: 500,
lineLength: 100,
lineRes: 30,
noiseCoef: 50,
timeCoef: 20,
mouseCoef: 30,
delta: 20,
animate: true,
colors: [0x1be73d, 0xc7ec2f, 0xe72e48, 0x000000, 0x5b3231, 0x37bdf1, 0x000000],
postProcessing: false };
let renderer, scene, camera, cameraCtrl, composer;
let width, height, cx, cy;
let object;
let lines = [];
const mouse = new THREE.Vector2();
init();
function init() {
renderer = new THREE.WebGLRenderer({ antialias: true });
document.body.appendChild(renderer.domElement);
composer = new THREE.EffectComposer(renderer);
camera = new THREE.PerspectiveCamera(conf.fov);
camera.position.set(0, 0, conf.cameraZ);
cameraCtrl = new THREE.OrbitControls(camera);
// cameraCtrl.autoRotate = true;
// cameraCtrl.autoRotateSpeed = 5;
updateSize();
initScene();
initGUI();
initEventHandlers();
animate();
}
function initScene() {
scene = new THREE.Scene();
if (conf.background) scene.background = new THREE.Color(conf.background);
composer.addPass(new THREE.RenderPass(scene, camera));
composer.addPass(new THREE.UnrealBloomPass(new THREE.Vector2(width, height), 0.8, 1, 0));
object = new THREE.Object3D();
const cscale = chroma.scale(conf.colors);
const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors, blending: THREE.AdditiveBlending, depthTest: false, depthWrite: false });
const points = getFibonacciSpherePoints(conf.numLines, conf.lineLength);
for (let i = 0; i < points.length; i++) {
let p = points[i];
let line = new Line(new THREE.Vector3(0, 0, 0), new THREE.Vector3(p.x, p.y, p.z), conf.lineRes, cscale, material);
lines.push(line);
object.add(line.mesh);
}
// object.rotation.x = PI / 4;
// object.rotation.y = -PI / 4;
scene.add(object);
}
function initGUI() {
const gui = new dat.GUI();
gui.add(conf, 'noiseCoef', 0, 100, 1);
gui.add(conf, 'timeCoef', 0, 100, 1);
gui.add(conf, 'mouseCoef', 0, 100, 1);
gui.add(conf, 'delta', 0, 50, 1);
gui.add(conf, 'animate');
gui.add(conf, 'postProcessing');
gui.add({ randomColors }, 'randomColors');
gui.close();
}
function initEventHandlers() {
window.addEventListener('resize', updateSize);
document.addEventListener('mousemove', e => {
mouse.x = e.clientX / width * 2 - 1;
mouse.y = -(e.clientY / height) * 2 + 1;
});
renderer.domElement.addEventListener('click', onClick);
}
function animate() {
requestAnimationFrame(animate);
if (conf.animate) {
const noise = {
coef: conf.noiseCoef * 0.0002,
time: Date.now() * conf.timeCoef * 0.00001,
mouse: (mouse.x + mouse.y) * conf.mouseCoef * 0.01,
delta: conf.delta };
for (let i = 0; i < conf.numLines; i++) {
lines[i].animate(noise);
}
}
cameraCtrl.update();
if (conf.postProcessing) composer.render();else
renderer.render(scene, camera);
}
function onClick() {
if (onClick.tween && onClick.tween.isActive()) return;
randomColors();
onClick.tween = TweenMax.to(conf, 1, {
noiseCoef: conf.noiseCoef * 1.5,
// delta: conf.delta * 1.5,
yoyo: true,
repeat: true });
}
function randomColors() {
if (randomColors.tween && randomColors.tween.isActive()) return;
const colors = [chroma.random(), chroma.random(), chroma.random(), 0, chroma.random(), chroma.random(), 0];
const cscales = [];
for (let i = 0; i < conf.colors.length; i++) {
cscales.push(chroma.scale([conf.colors[i], colors[i]]));
}
const o = { i: 0 };
randomColors.tween = TweenMax.to(o, 1, {
i: 1,
onUpdate: function () {
for (let i = 0; i < conf.colors.length; i++) {
conf.colors[i] = cscales[i](o.i);
}
updateColors(chroma.scale(conf.colors));
} });
}
function updateColors(cscale) {
for (let i = 0; i < conf.numLines; i++) {
lines[i].updateColors(cscale);
}
}
function updateSize() {
width = window.innerWidth;cx = width / 2;
height = window.innerHeight;cy = height / 2;
if (renderer && camera) {
renderer.setSize(width, height);
composer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
}
class Line {
constructor(v1, v2, res, cscale, material) {
this.res = res;
this.points = [];
const positions = [],colors = [];
const dv = v2.clone().sub(v1).multiplyScalar(1 / res);
for (let i = 0; i < res; i++) {
const v = new THREE.Vector3(v1.x + i * dv.x, v1.y + i * dv.y, v1.z + i * dv.z);
this.points.push(v);
positions.push(v.x, v.y, v.z);
const color = cscale(i * 1 / res);
colors.push(color.get('rgb.r') / 255, color.get('rgb.g') / 255, color.get('rgb.b') / 255);
}
this.geometry = new THREE.BufferGeometry();
this.geometry.addAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
this.geometry.addAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
// this.geometry.computeBoundingSphere();
this.mesh = new THREE.Line(this.geometry, material);
this.mesh.matrixAutoUpdate = false;
}
animate(noise) {
const gArray = this.mesh.geometry.attributes.position.array;
let j, p, nx, ny, nz, n1, n2, n3;
for (let i = 0; i < this.res; i++) {
j = i * 3;
p = this.points[i];
nx = p.x * noise.coef + noise.time;
ny = p.y * noise.coef - noise.time;
nz = p.z * noise.coef + noise.time;
n1 = simplex.noise3D(ny, nz, noise.mouse);
n2 = simplex.noise3D(nz, nx, noise.mouse);
n3 = simplex.noise3D(nx, ny, noise.mouse);
gArray[j] = p.x + n1 * noise.delta;
gArray[j + 1] = p.y + n2 * noise.delta;
gArray[j + 2] = p.z + n3 * noise.delta;
}
this.mesh.geometry.attributes.position.needsUpdate = true;
}
updateColors(cscale) {
const gArray = this.mesh.geometry.attributes.color.array;
let j, color;
for (let i = 0; i < this.res; i++) {
j = i * 3;
color = cscale(i * 1 / this.res);
gArray[j] = color.get('rgb.r') / 255;
gArray[j + 1] = color.get('rgb.g') / 255;
gArray[j + 2] = color.get('rgb.b') / 255;
}
this.mesh.geometry.attributes.color.needsUpdate = true;
}}
function getFibonacciSpherePoints(samples, radius, randomize) {
samples = samples || 1;
radius = radius || 1;
randomize = randomize || true;
let random = 1;
if (randomize) {
random = Math.random() * samples;
}
let points = [];
let offset = 2 / samples;
let increment = Math.PI * (3 - Math.sqrt(5));
for (let i = 0; i < samples; i++) {
let y = i * offset - 1 + offset / 2;
let distance = Math.sqrt(1 - Math.pow(y, 2));
let phi = (i + random) % samples * increment;
let x = Math.cos(phi) * distance;
let z = Math.sin(phi) * distance;
x = x * radius;
y = y * radius;
z = z * radius;
points.push({ x, y, z });
}
return points;
}
App();