JSFiddle - React, Tailwind, and code Playground
by RAX7
HTML
<section>
<div class="item1">item-1</div>
<div class="item2">item-2</div>
<div class="item3">item-3</div>
</section>
<section>
<div class="item4">item-4</div>
</section>
<canvas id="overlay"></canvas>
CSS
html,
body {
margin: 0;
padding: 0;
height: 100%;
}
section {
position: relative;
height: 75%;
background: url('https://images.unsplash.com/photo-1466618786657-4df462be674e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=750&q=80') center / cover;
}
.item1,
.item2,
.item3,
.item4 {
position: absolute;
width: 10%;
height: 10%;
background-color: #ffeb3b;
color: #795548;
border-radius: 5px;
text-align: center;
}
.item1 {
top: 15%;
left: 20%;
}
.item2 {
top: 55%;
left: 70%;
}
.item3 {
top: 85%;
left: 20%;
}
.item4 {
top: 50%;
left: 80%;
}
#overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9999;
}
JavaScript
const canvas = document.getElementById('overlay');
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const ctx = canvas.getContext('2d');
const targets = [];
function addTargetEl(el, radius = 100) {
targets.push({
el: el,
radius: radius,
rect: el.getBoundingClientRect()
});
}
function removeTargetEl(el) {
const index = targets.findIndex(t => t.el === el);
if (index !== -1) {
targets.splice(index, 1);
}
}
function drawOverlay() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!isOverlaid) return;
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = 'rgba(0, 0, 0, 0.75)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
for (let i = 0; i < targets.length; i++) {
let t = targets[i];
ctx.beginPath();
ctx.arc(t.rect.x + t.rect.width / 2, t.rect.y + t.rect.height / 2, t.radius, 0, 2 * Math.PI, false);
ctx.fill();
}
requestAnimationFrame(drawOverlay);
}
/* ========================================== */
function updateTargetsRect() {
targets.forEach(item => item.rect = item.el.getBoundingClientRect());
}
window.addEventListener('scroll', updateTargetsRect);
function onResize(e) {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
updateTargetsRect();
}
window.addEventListener('resize', onResize);
/* ========================================== */
let isOverlaid = true;
drawOverlay();
addTargetEl(document.querySelector('.item1'));
setTimeout(() => {
removeTargetEl(document.querySelector('.item1'));
addTargetEl(document.querySelector('.item2'), 50);
}, 2000);
setTimeout(() => {
removeTargetEl(document.querySelector('.item2'));
addTargetEl(document.querySelector('.item3'), 75);
}, 4000);
setTimeout(() => {
removeTargetEl(document.querySelector('.item3'));
addTargetEl(document.querySelector('.item4'), 100);
}, 6000);
setTimeout(() =>...