JSFiddle - React, Tailwind, and code Playground
Intersection Observer test with animated clip-path
by Travis Almand
HTML
<input type="range" id="horizontal" min="-350" max="350" value="0">
<div id="intersectionRect" class="hide"></div>
<div id="container">
<div id="contained"></div>
</div>
<div id="panel">
<div id="intersecting">isIntersecting: <span></span></div>
<div id="ratio">intersection ratio: <span></span></div>
<div class="buttons">
<button id="clip">clip-path</button>
<button id="animate">animate</button>
</div>
</div>
SCSS
body {
align-items: center;
background-color: #E0E0E0;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
justify-content: center;
}
#intersectionRect {
background-color: #307B21;
position: absolute;
z-index: 10;
&.hide {
display: none !important;
}
}
#container {
align-items: center;
background-color: #fff;
border: 3px solid rebeccapurple;
display: flex;
height: 400px;
justify-content: center;
overflow: hidden;
width: 400px;
}
#contained {
background-color: rebeccapurple;
height: 300px;
width: 300px;
&.clip-path {
clip-path: circle(25%);
}
&.animate {
animation: 2s grow alternate infinite;
}
}
#panel {
margin: 10px;
width: 400px;
.buttons {
margin: 10px 0;
}
}
#horizontal {
width: 400px;
}
@keyframes grow {
0% {
clip-path: circle(100%);
}
100% {
clip-path: circle(25%);
}
}
JavaScript
console.clear();
let sliderH = document.querySelector('#horizontal');
let container = document.querySelector('#container');
let contained = document.querySelector('#contained');
let isIntersecting = document.querySelector('#intersecting span');
let intersectionRatio = document.querySelector('#ratio span');
let clip = document.querySelector('#clip');
let animate = document.querySelector('#animate');
let intersectionRect = document.querySelector('#intersectionRect');
let threshold = [...Array(100).keys()].map(x => x / 100);
let options = {
root: container,
rootMargin: '0px',
threshold: threshold
}
let observer = new IntersectionObserver(callback, options);
observer.observe(contained);
sliderH.addEventListener('input', function (e) {
contained.style.transform = `translate3d(${e.target.value}px, 0, 0`;
});
clip.addEventListener('click', function () {
contained.classList.remove('animate');
contained.classList.toggle('clip-path');
});
animate.addEventListener('click', function () {
contained.classList.remove('clip-path');
contained.classList.toggle('animate');
});
function callback (entries, observer) {
entries.forEach(entry => {
isIntersecting.innerText = entry.isIntersecting;
intersectionRatio.innerText = entry.intersectionRatio;
intersectionRect.style.display = entry.intersectionRatio > 0.99 ? 'none': 'block';
intersectionRect.style.height = entry.intersectionRect.height + 'px';
intersectionRect.style.width = entry.intersectionRect.width + 'px';
intersectionRect.style.top = entry.intersectionRect.top + 'px';
intersectionRect.style.left = entry.intersectionRect.left + 'px';
});
}