-
-
Save JonathanDn/09c3e0f037c4c52efcf56ee0878d5e4c to your computer and use it in GitHub Desktop.
<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); | |
} | |
} |
This looks great. How can I demo this standalone? This is probably a noddy question. i.e. how to get working code from js fiddle (which presumably links the files somehow but does not seem to show this) Thanks a million
@topshef What do you mean by demo standalone ?
You got the jsfiddle link which serves as a demo/playground to mess around with the code and microinteraction written in HTML, SCSS JS
https://jsfiddle.net/urft14zr/425/
@JonathanDn thanks for your quick reply :-) I mean to create the files needed to run the demo on its own, without js fiddle. i.e update html with links to the js and css, and header etc. Sorry if it's a noddy question!
@topshef Define on its own.
- On your local environment? (i.e your computer only)
- Do you mean to have it available online in a website other than jsfiddle?
- Do you want to add it to an existing app you have?
@topshef are you working with React/Vue/Vanilla JS?
@JonathanDn
Online, vanilla JS
- local + online (sync is via Webdrive)
- yes, demo it, not on js fiddle
- later, but now just a demo to understand the code and test some changes
Thanks
I'm checking this out https://stackoverflow.com/questions/9851878/is-there-a-download-function-in-jsfiddle
..Ok that was it.. just add /show to the js fiddle URL and then just save the page :-)
(not that clean though but it works)
I'm checking this out https://stackoverflow.com/questions/9851878/is-there-a-download-function-in-jsfiddle
..Ok that was it.. just add /show to the js fiddle URL and then just save the page :-)
(not that clean though but it works)
Awesome, I actually didn't know that ! I thought of uploading it all to codesandbox.io which shows the index.html
boilerplate with all the script and style tags. But I guess that helps resolved your issue as well?
- Would you suggest adding a codesandbox example would be more inviting for new devs?
@topshef By the way I would love to see what you are doing with it if you would mind sharing with me :) You can also DM me that to my twitter profile: https://twitter.com/jodoron
@JonathanDn thanks a lot, yes I'll share it back on github and DM you :-)
@JonathanDn thanks a lot, yes I'll share it back on github and DM you :-)
Awesome!
Hey @sastava007, This implementation is purely client side.
You could expose the totalClickCounter in row 55 as a global in the file and then whenever you want to send it to an API in your backend to update the total claps.