.cover
.main
.balls
- for (var i=0; i<12; i++)
.ball
.drop
.control
svg(xmlns='http://www.w3.org/2000/svg', version='1.1')
defs
filter#goo
fegaussianblur(in='SourceGraphic', stddeviation='10', result='blur')
fecolormatrix(in='blur', mode='matrix', values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -5', result='goo')
feblend(in='SourceGraphic', in2='goo')
View Compiled
$mainSize: 200px;
$ballSize: 30px;
$ballCount: 12;
$rotateTime: 5s;
$bgcolor:#0d0b0c;
$goocolor:#c00d1e;
$controlcolor:#f6f6f6;
$border: none;
html, body {
width:100%;
height:100%;
}
body {
background:$bgcolor;
display: flex;
align-items: center;
justify-content: center;
overflow:hidden;
}
svg {
position:absolute;
z-index:-100;
pointer-events:none;
}
.main {
padding:$mainSize;
position:relative;
filter:url('#goo');
opacity:0.85;
transition:0.5s opacity;
transition-delay:1s;
&:hover {
opacity:0.975;
transition-delay:0s;
}
.control {
position:relative;
width:$mainSize;
height:$mainSize;
background:$goocolor;
border:$border;
border-radius:50%;
cursor:pointer;
text-align:center;
color:$controlcolor;
font-size:$mainSize/2;
line-height:$mainSize;
font-family:FontAwesome;
&::after {
content:'\f04b';
position:relative;
left:8px;
}
&.playing::after {
content:'\f04c';
left:0;
}
&.loading::after {
display: block;
content:'\f110';
left:0;
animation:spin linear 1s infinite;
}
}
.balls {
position:absolute;
top:0;left:0;bottom:0;right:0;
animation:spin linear $rotateTime infinite;
.ball {
position:absolute;
top: $mainSize / 2;
left: 3*$mainSize/2 - $ballSize/2;
width:$ballSize;
height:$ballSize;
transform-origin: $ballSize/2 $mainSize;
.drop {
content: '';
display: inline-block;
width: $ballSize;
height: $ballSize;
border-radius: 50%;
background-color: $goocolor;
border:$border;
transform:translateY($mainSize/2);
}
@for $i from 1 through $ballCount {
&:nth-child(#{$i}) {
transform:rotate(#{round(360/$ballCount * ($i - 1))}deg);
}
}
}
}
}
.cover {
&::before, &::after {
position:absolute;
top:0;left:0;bottom:0;right:0;
content:'';
display:block;
background:url('https://reneroth.xyz/files/codepen/dornendiamant.jpg');
background-position:center center;
background-repeat:no-repeat;
}
&::before {
background-size:cover;
filter:blur(10px);
}
&::after {
background-size:contain;
}
}
@keyframes spin {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
View Compiled
var minY = 135;
var maxY = 10;
var pausePosition = 0.75;
var reverse = false;
var offsetBalls = true;
var smoothFac = 0.85;
var smoothFacPause = 0.975;
var cutoffLow = 0.1;
var cutoffHigh = 0.65;
var songUrl = "https://reneroth.xyz/files/codepen/frozen.mp3";
var $c = $('.control');
var isLoaded = false;
var $d = $('.drop');
if (reverse) {
var tmp = minY;
minY = maxY;
maxY = tmp;
}
$c.click(function(e) {
e.preventDefault();
if (!isLoaded) {
if ($c.hasClass('loading')) {
return;
}
$c.addClass('loading');
eq.play(songUrl, function() {
$c.removeClass('loading').addClass('playing');
isLoaded = true;
});
} else {
if ($c.hasClass('playing')) {
$c.removeClass('playing');
eq.stopSound();
} else {
$c.addClass('playing');
eq.replaySound();
}
}
});
var offsets = [];
function doRender() {
requestAnimationFrame(doRender);
$d.each(function(i) {
if(offsetBalls) {
if(i%2 != 0) {
i = Math.ceil($d.length/2) + (i-1)/2;
} else {
i /= 2;
}
}
if (typeof offsets[i]=='undefined') {
offsets[i] = 0;
}
var a = eq.getSpectrumByPercentage((i + 0) / ($d.length)) / 255;
if ($c.hasClass('playing')) {
offsets[i] = a * (1 - smoothFac) + offsets[i] * smoothFac;
} else {
a = pausePosition;
offsets[i] = a * (1 - smoothFacPause) + offsets[i] * smoothFacPause;
}
var y = minY - (minY - maxY) * offsets[i];
$(this).css('transform', 'translateY(' + y + 'px)');
});
}
var Equalizer = new Function();
if (!window.AudioContext) {
if (!window.webkitAudioContext) {
alert('no audiocontext found');
}
window.AudioContext = window.webkitAudioContext;
}
Equalizer.prototype.context = new AudioContext();
Equalizer.prototype.audioBuffer = [];
Equalizer.prototype.sourceNode = {};
Equalizer.prototype.analyser = {};
Equalizer.prototype.javascriptNode = {};
Equalizer.prototype.audioData = [];
Equalizer.prototype.fftSize = 512;
Equalizer.prototype.playStart = 0;
Equalizer.prototype.playResume = 0;
Equalizer.prototype.buffer = [];
Equalizer.prototype.play = function(url, loadCallback) {
var self = this;
this.javascriptNode = this.context.createScriptProcessor(2048, 1, 1);
this.javascriptNode.connect(this.context.destination);
this.javascriptNode.onaudioprocess = function() {
self.processAudio();
};
this.analyser = this.context.createAnalyser();
this.analyser.smoothingTimeConstant = 0.2;
this.analyser.fftSize = this.fftSize;
this.sourceNode = this.context.createBufferSource();
this.sourceNode.connect(this.analyser);
this.analyser.connect(this.javascriptNode);
this.sourceNode.connect(this.context.destination);
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function() {
self.context.decodeAudioData(request.response, function(buffer) {
self.buffer = buffer;
self.playSound(buffer);
if (typeof(loadCallback) !== "undefined") {
loadCallback();
}
});
}
request.send();
}
Equalizer.prototype.playSound = function(buffer) {
this.sourceNode.buffer = buffer;
this.sourceNode.loop = true;
this.sourceNode.start(0);
this.playStart = new Date().getTime();
}
Equalizer.prototype.stopSound = function() {
this.playResume= new Date().getTime();
this.playResume-=this.playStart;
this.playResume/=1000;
this.playResume%=this.sourceNode.buffer.duration;
this.sourceNode.stop();
}
Equalizer.prototype.replaySound = function() {
this.sourceNode = this.context.createBufferSource();
this.sourceNode.connect(this.analyser);
this.sourceNode.connect(this.context.destination);
this.sourceNode.buffer = this.buffer;
this.sourceNode.loop = true;
this.sourceNode.start(0,this.playResume);
this.playStart = new Date().getTime()-this.playResume*1000;
}
Equalizer.prototype.processAudio = function() {
this.audioData = new Uint8Array(this.analyser.frequencyBinCount);
this.analyser.getByteFrequencyData(this.audioData);
}
Equalizer.prototype.getSpectrum = function() {
return this.audioData;
}
Equalizer.prototype.getSpectrumAt = function(i) {
return typeof(this.audioData[i]) !== "undefined" ? this.audioData[i] : 0;
}
Equalizer.prototype.getSpectrumByPercentage = function(p) {
var modLength = this.audioData.length - Math.floor(cutoffLow * this.audioData.length + cutoffHigh * this.audioData.length);
var i = Math.floor(p * modLength + cutoffLow * this.audioData.length);
return typeof(this.audioData[i]) !== "undefined" ? this.audioData[i] : 0;
}
var eq = new Equalizer();
doRender();