<div class="dot"></div>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
body, html {
width: 100%;
height: 100%;
background-color: #1E1B16;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.container {
width: 140vmin;
height: 90vmin;
margin: 5vmin;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.dot {
position: fixed;
width: 6vmin;
height: 6vmin;
margin-top: 0vmin;
margin-bottom: 0vmin;
margin-left: 0vmin;
background-color: #FFBB00;
border-radius: 50%;
z-index: 2;
box-shadow: 0 0 5px #FFBB00, 0 0 20px 10px rgba(255, 187, 0, 0.3);
}
.box {
background-color: #423F3B;
width: 10vmin;
height: 10vmin;
margin: 2.5vmin;
border-radius: 16%;
box-shadow: 0 0 10px rgba(66, 63, 59, 0.5);
}
const boxes = [...document.querySelectorAll('.box')];
const dot = document.querySelector('.dot');
const container = document.querySelector('.container');
const rect = container.getBoundingClientRect();
const coords = {
x: [0, Math.round(rect.width / 4), Math.round(3 * rect.width / 4), rect.width],
y: [0, Math.round(rect.height / 2), rect.height]
};
const pos = {x: coords.x[0] + rect.left, y: coords.y[0] + rect.top};
// Move the dot to the given coordinates, rotating
// all the boxes so that they are facing the given coordinates
const moveDot = (x, y) => {
// Position the dot
dot.style.top = `${y}px`;
dot.style.left = `${x}px`;
pos.x = x;
pos.y = y;
// Rotate all the boxes
boxes.forEach(box => {
const {top, left, width, height} = box.getBoundingClientRect();
const adjacent = x - left - width / 2;
const opposite = y - top - height / 2;
// arctan gives us the degree based on the opposite & adjacent edges
// of the triangle created between the box' center and the given
// coordinates
const radians = Math.atan(opposite / adjacent);
box.style.transform = `rotate(${radians}rad)`;
});
};
const generateArray = length => [...new Array(length)].map((_, i) => i);
const random = arr => arr[Math.floor(Math.random() * arr.length)];
let animation;
let coordinate = 'x';
const prev = {x: 0, y: 0};
// Recursively animate the dot the a random set of coordinates
const animate = () => {
const pool = generateArray(coords[coordinate].length);
pool.splice(prev[coordinate], 1);
const next = random(pool);
prev[coordinate] = next;
animation = TweenMax.to(pos, 1.8, {
[coordinate]: coords[coordinate][next] + rect[coordinate],
ease: Power2.easeInOut,
onUpdate: function() {
moveDot(pos.x, pos.y);
},
onComplete: animate,
});
// Toggle the animated coordinate (animate a differen axis every time)
coordinate = coordinate === 'x' ? 'y' : 'x';
}
animate();
const dAnimate = _.debounce(animate, 1000);
// Stop animation on mouse move and follow the cursor until
// it rests for 500ms, at which point the animation should
// start again
window.addEventListener('mousemove', ({x, y}) => {
animation.kill();
dAnimate();
window.requestAnimationFrame(() => {
moveDot(x, y);
});
});
This Pen doesn't use any external CSS resources.