Last active
June 9, 2023 07:34
-
-
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/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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})`; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome!