Skip to content

Instantly share code, notes, and snippets.

@JonathanDn
Last active June 9, 2023 07:34
Show Gist options
  • Save JonathanDn/09c3e0f037c4c52efcf56ee0878d5e4c to your computer and use it in GitHub Desktop.
Save JonathanDn/09c3e0f037c4c52efcf56ee0878d5e4c to your computer and use it in GitHub Desktop.
(moved to a repo https://github.com/JonathanDn/mediumclap ) Medium Clap Reproduction - My take on it by looking, researching and trial & error. Demo available --> https://jsfiddle.net/urft14zr/425/
<div class="canvas">
<div id="totalCounter" class="total-counter"></div>
<div id="clap" class="clap-container">
<i class="clap-icon fa fa-hand-paper-o"></i>
</div>
<div id="clicker" class="click-counter">
<span class="counter"></span>
</div>
<div id="sonar-clap" class="clap-container-sonar"></div>
<div id="particles" class="particles-container">
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
</div>
<div id="particles-2" class="particles-container">
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
</div>
<div id="particles-3" class="particles-container">
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
<div class="triangle">
<div class="square"></div>
</div>
</div>
</div>
let accCounter = 0;
let totalCount = 127;
const minDeg = 1;
const maxDeg = 72;
const particlesClasses = [
{
class: "pop-top"
},
{
class: "pop-top-left"
},
{
class: "pop-top-right"
},
{
class: "pop-bottom-right"
},
{
class: "pop-bottom-left"
},
];
document.getElementById('totalCounter').innerText = totalCount;
document.getElementById('clap').onmouseover = function() {
let sonarClap = document.getElementById('sonar-clap');
sonarClap.classList.add('hover-active');
setTimeout(() => {
sonarClap.classList.remove('hover-active');
}, 2000);
}
document.getElementById('clap').onclick = function() {
const clap = document.getElementById('clap');
const clickCounter = document.getElementById("clicker");
const particles = document.getElementById('particles');
const particles2 = document.getElementById('particles-2');
const particles3 = document.getElementById('particles-3');
clap.classList.add('clicked');
upClickCounter();
runAnimationCycle(clap, 'scale');
if (!particles.classList.contains('animating')) {
animateParticles(particles, 700);
} else if(!particles2.classList.contains('animating')){
animateParticles(particles2, 700);
} else if(!particles3.classList.contains('animating')) {
animateParticles(particles3, 700);
}
}
function upClickCounter() {
const clickCounter = document.getElementById("clicker");
const totalClickCounter = document.getElementById('totalCounter');
accCounter ++;
clickCounter.children[0].innerText = '+' + accCounter;
totalClickCounter.innerText = totalCount + accCounter;
if (clickCounter.classList.contains('first-active')) {
runAnimationCycle(clickCounter, 'active');
} else {
runAnimationCycle(clickCounter, 'first-active');
}
runAnimationCycle(totalClickCounter, 'fader');
}
function runAnimationCycle(el, className, duration) {
if (el && !el.classList.contains(className)) {
el.classList.add(className);
} else {
el.classList.remove(className);
void el.offsetWidth; // Trigger a reflow in between removing and adding the class name
el.classList.add(className);
}
}
function runParticleAnimationCycle(el, className, duration) {
if (el && !el.classList.contains(className)) {
el.classList.add(className);
setTimeout(() => {
el.classList.remove(className);
}, duration);
}
}
function animateParticles(particles, dur) {
addRandomParticlesRotation(particles.id, minDeg, maxDeg);
for(let i = 0; i < particlesClasses.length; i++) {
runParticleAnimationCycle(particles.children[i], particlesClasses[i].class, dur);
}
// Boolean functionality only to activate particles2, particles3 when needed
particles.classList.add('animating');
setTimeout(() => {
particles.classList.remove('animating');
}, dur);
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function addRandomParticlesRotation(particlesName, minDeg, maxDeg) {
const particles = document.getElementById(particlesName);
const randomRotationAngle = getRandomInt(minDeg, maxDeg) + 'deg';
particles.style.transform = `rotate(${randomRotationAngle})`;
}
$default-clap-color: #03a87c;
.canvas {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 300px;
position: relative;
.total-counter {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
position: absolute;
margin-top: -45px;
color: gray;
font-family: sans-serif;
font-size: 16px;
}
.total-counter.fader {
animation: fade-in 1400ms forwards;
}
.clap-container {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
width: 60px;
height: 60px;
border: 1px solid rgba(0,0,0,.15);
border-radius: 50%;
z-index: 2;
background: #fff;
cursor: pointer;
.clap-icon {
font-size: 30px;
color: $default-clap-color;
width: 30px;
height: 30px;
}
}
.clap-container:hover {
border: 1px solid $default-clap-color;
}
.clap-container.scale {
animation: scaleAndBack 700ms forwards;
}
.click-counter {
display: flex;
justify-content: center;
align-items: center;
width: 35px;
height: 35px;
position: absolute;
top: 132px;
background-color: $default-clap-color;
border-radius: 50%;
z-index: 1;
.counter {
font-family: sans-serif;
font-size: 14px;
color: #fff;
}
}
.click-counter.first-active {
animation: first-bump-in 1s forwards;
}
.click-counter.active {
animation: bump-in 1s forwards;
}
.clap-container-sonar {
width: 60px;
height: 60px;
background: $default-clap-color;
border-radius: 50%;
position: absolute;
opacity: 0;
z-index: 0;
}
.hover-active {
animation: sonar-wave 2s forwards;
}
.particles-container {
display: flex;
justify-content: center;
align-items: center;
width: 60px;
height: 60px;
position: absolute;
/* border: 1px solid gray; */
/* z-index: 3; */
.triangle {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 10px solid red;
border-bottom: 4px solid transparent;
position: absolute;
.square {
width: 5px;
height: 5px;
background: $default-clap-color;
position: absolute;
left: -15px;
top: 0;
}
}
.pop-top {
animation: pop-top 1s forwards;
}
.pop-top-left {
animation: pop-top-left 1s forwards;
}
.pop-top-right {
animation: pop-top-right 1s forwards;
}
.pop-bottom-right {
animation: pop-bottom-right 1s forwards;
}
.pop-bottom-left {
animation: pop-bottom-left 1s forwards;
}
}
}
// * * * Animations * * * //
@keyframes sonar-wave {
0% {
opacity: 0.7;
}
100% {
transform: scale(1.4);
opacity: 0;
}
}
@keyframes fade-in {
0% {
opacity: 0;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
// * * * Pop Animations * * * //
@keyframes pop-top {
0% {
transform: translate(0, 0) rotate(0);
opacity: 0.4;
}
100% {
transform: translate(0, -100px) rotate(0);
opacity: 0;
}
}
@keyframes pop-top-left {
0% {
transform: translate(0, 0) rotate(-55deg);
opacity: 0.4;
}
100% {
transform: translate(-100px, -50px) rotate(-55deg);
opacity: 0;
}
}
@keyframes pop-top-right {
0% {
transform: translate(0, 0) rotate(55deg);
opacity: 0.4;
}
100% {
transform: translate(100px, -50px) rotate(55deg);
opacity: 0;
}
}
@keyframes pop-bottom-right {
0% {
transform: translate(0, 0) rotate(135deg);
opacity: 0.4;
}
100% {
transform: translate(70px, 80px) rotate(135deg);
opacity: 0;
}
}
@keyframes pop-bottom-left {
0% {
transform: translate(0, 0) rotate(-135deg);
opacity: 0.4;
}
100% {
transform: translate(-70px, 80px) rotate(-135deg);
opacity: 0;
}
}
@keyframes first-bump-in {
0% {
transform: translateY(-65px);
opacity: 1;
}
50% {
transform: translateY(-80px);
opacity: 1;
}
100% {
transform: translateY(-100px);
opacity: 0;
}
}
@keyframes bump-in {
0% {
transform: translateY(-80px) scale(0.9);
opacity: 1;
}
50% {
transform: translateY(-80px) scale(1);
opacity: 1;
}
100% {
transform: translateY(-100px) scale(1);
opacity: 0;
}
}
@keyframes scaleAndBack {
0% {
transform: scale(1);
}
50% {
transform: scale(1.15);
}
100% {
transform: scale(1);
}
}
@topshef
Copy link

topshef commented Sep 28, 2020

@JonathanDn thanks a lot, yes I'll share it back on github and DM you :-)

@JonathanDn
Copy link
Author

@JonathanDn thanks a lot, yes I'll share it back on github and DM you :-)

Awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment