The medium clap effect 👏 recreated with React. Yeah!
Created
February 11, 2019 16:58
-
-
Save dslusser/bee0d4949d2370f2bd671db47dda47c9 to your computer and use it in GitHub Desktop.
Medium clap effect w/ ReactJS
This file contains hidden or 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 id="app"></div> | |
<aside id="message">In case you didn't know, you gotta click the clap button above 😎😜😜 | |
<br /> | |
<a href="https://twitter.com/OhansEmmanuel" target="_blank">@ohansemmanuel</a> | |
<br /> | |
<a href="https://codepen.io/ohansemmanuel/full/dVdvJQ/" | |
target="_blank"> | |
See Vanilla JS implementation | |
</a> | |
</aside> |
This file contains hidden or 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
class App extends React.Component { | |
constructor(props) { | |
super(props) | |
this.state = { | |
count: 0, | |
countTotal: this._generateRandomNumber(500,10000), | |
isClicked: false, | |
} | |
this._handleClick = this._handleClick.bind(this); | |
} | |
componentDidMount() { | |
const tlDuration = 300 | |
const triangleBurst = new mojs.Burst({ | |
parent: '#clap', | |
radius: {50:95}, | |
count: 5, | |
angle: 30, | |
children: { | |
shape: 'polygon', | |
radius: {6: 0}, | |
scale: 1, | |
stroke: 'rgba(211,84,0 ,0.5)', | |
strokeWidth: 2, | |
angle: 210, | |
delay: 30, | |
speed: 0.2, | |
easing: mojs.easing.bezier(0.1, 1, 0.3 ,1), | |
duration: tlDuration | |
} | |
}) | |
const circleBurst = new mojs.Burst({ | |
parent: '#clap', | |
radius: {50:75}, | |
angle: 25, | |
duration: tlDuration, | |
children: { | |
shape: 'circle', | |
fill: 'rgba(149,165,166 ,0.5)', | |
delay: 30, | |
speed: 0.2, | |
radius: {3: 0}, | |
easing: mojs.easing.bezier(0.1, 1, 0.3, 1), | |
} | |
}) | |
const countAnimation = new mojs.Html({ | |
el: '#clap--count', | |
isShowStart: false, | |
isShowEnd: true, | |
y: {0: -30}, | |
opacity: {0:1}, | |
duration: tlDuration | |
}).then({ | |
opacity: {1:0}, | |
y: -80, | |
delay: tlDuration/2 | |
}) | |
const countTotalAnimation = new mojs.Html({ | |
el: '#clap--count-total', | |
isShowStart: false, | |
isShowEnd: true, | |
opacity: {0:1}, | |
delay: 3*(tlDuration)/2, | |
duration: tlDuration, | |
y: {0: -3} | |
}) | |
const scaleButton = new mojs.Html({ | |
el: '#clap', | |
duration: tlDuration, | |
scale: {1.3: 1}, | |
easing: mojs.easing.out | |
}) | |
const clap = document.getElementById('clap') | |
clap.style.transform = "scale(1, 1)" | |
this._animationTimeline = new mojs.Timeline() | |
this._animationTimeline.add([ | |
countAnimation, | |
countTotalAnimation, | |
scaleButton, | |
circleBurst, | |
triangleBurst | |
]) | |
} | |
_generateRandomNumber (min, max) { | |
return Math.floor(Math.random()*(max-min+1)+min) | |
} | |
_handleClick () { | |
this._animationTimeline.replay() | |
this.setState(function(prevState, nextState) { | |
return { | |
count: Math.min(prevState.count + 1, 50), | |
countTotal: prevState.countTotal + 1, | |
isClicked: true | |
} | |
}) | |
} | |
render() { | |
const {count, countTotal, isClicked} = this.state; | |
return getAppContent(count, countTotal, isClicked, this._handleClick) | |
} | |
} | |
function getAppContent(count, countTotal, isClicked, handleClick) { | |
return <div><button id="clap" className="clap" onClick={handleClick}> | |
<span> | |
{/*<!-- SVG Created by Luis Durazo from the Noun Project -->*/} | |
<svg id="clap--icon" xmlns="http://www.w3.org/2000/svg" viewBox="-549 338 100.1 125" className={`${isClicked && 'checked'}`}> | |
<path d="M-471.2 366.8c1.2 1.1 1.9 2.6 2.3 4.1.4-.3.8-.5 1.2-.7 1-1.9.7-4.3-1-5.9-2-1.9-5.2-1.9-7.2.1l-.2.2c1.8.1 3.6.9 4.9 2.2zm-28.8 14c.4.9.7 1.9.8 3.1l16.5-16.9c.6-.6 1.4-1.1 2.1-1.5 1-1.9.7-4.4-.9-6-2-1.9-5.2-1.9-7.2.1l-15.5 15.9c2.3 2.2 3.1 3 4.2 5.3zm-38.9 39.7c-.1-8.9 3.2-17.2 9.4-23.6l18.6-19c.7-2 .5-4.1-.1-5.3-.8-1.8-1.3-2.3-3.6-4.5l-20.9 21.4c-10.6 10.8-11.2 27.6-2.3 39.3-.6-2.6-1-5.4-1.1-8.3z"/> | |
<path d="M-527.2 399.1l20.9-21.4c2.2 2.2 2.7 2.6 3.5 4.5.8 1.8 1 5.4-1.6 8l-11.8 12.2c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l34-35c1.9-2 5.2-2.1 7.2-.1 2 1.9 2 5.2.1 7.2l-24.7 25.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l28.5-29.3c2-2 5.2-2 7.1-.1 2 1.9 2 5.1.1 7.1l-28.5 29.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.4 1.7 0l24.7-25.3c1.9-2 5.1-2.1 7.1-.1 2 1.9 2 5.2.1 7.2l-24.7 25.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l14.6-15c2-2 5.2-2 7.2-.1 2 2 2.1 5.2.1 7.2l-27.6 28.4c-11.6 11.9-30.6 12.2-42.5.6-12-11.7-12.2-30.8-.6-42.7m18.1-48.4l-.7 4.9-2.2-4.4m7.6.9l-3.7 3.4 1.2-4.8m5.5 4.7l-4.8 1.6 3.1-3.9"/> | |
</svg> | |
</span> | |
<span | |
id="clap--count" className="clap--count">+{count}</span> | |
<span | |
id="clap--count-total" className="clap--count-total">{countTotal}</span> | |
</button> | |
</div> | |
} | |
ReactDOM.render(<App />, document.getElementById('app')) |
This file contains hidden or 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
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/mo-js/0.288.1/mo.min.js"></script> |
This file contains hidden or 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
@import url("https://fonts.googleapis.com/css?family=Montserrat:400,400i,700"); | |
/*======================== | |
SASS definitions | |
=======================*/ | |
$btn-dimension: 80px; | |
$primary-color: rgba(189,195,199 ,1); | |
$secondary-color: rgba(39,174,96 ,1); | |
@mixin debug { | |
outline: 1px solid red; | |
} | |
/*======================== | |
FLEXBOX ALIGNMENT | |
=======================*/ | |
body { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
min-height: 100vh; | |
font-family: Montserrat; | |
} | |
/*======================== | |
BASIC styles | |
=======================*/ | |
* { | |
box-sizing: border-box | |
} | |
/*======================== | |
BUTTON styles | |
=======================*/ | |
.clap { | |
position: relative; | |
outline: 1px solid transparent; | |
border-radius: 50%; | |
border: 1px solid $primary-color; | |
width: $btn-dimension; | |
height: $btn-dimension; | |
background: none; | |
&:after { | |
content: ""; | |
position: absolute; | |
top: 0; | |
left: 0; | |
display: block; | |
border-radius: 50%; | |
//border: 1px solid $primary-color; | |
width: $btn-dimension - 1px; | |
height: $btn-dimension - 1px; | |
} | |
&:hover { | |
cursor: pointer; | |
border: 1px solid $secondary-color; | |
transition: border-color 0.3s ease-in; | |
&:after { | |
animation: shockwave 1s ease-in infinite; | |
} | |
} | |
svg { | |
width: 40px; | |
fill: none; | |
stroke: $secondary-color; | |
stroke-width: 2px; | |
&.checked { | |
fill: $secondary-color; | |
stroke: #fff; | |
stroke-width: 1px; | |
} | |
} | |
.clap--count { | |
position: absolute; | |
top: -$btn-dimension/1.6; | |
left: $btn-dimension/4; | |
font-size: 0.8rem; | |
color: white; | |
background: $secondary-color; | |
border-radius: 50%; | |
height: $btn-dimension/2; | |
width: $btn-dimension/2; | |
line-height: $btn-dimension/2; | |
} | |
.clap--count-total { | |
position: absolute; | |
font-size: 0.8rem; | |
width: $btn-dimension; | |
text-align: center; | |
left: 0; | |
top: -$btn-dimension/3.5; | |
color: $primary-color; | |
} | |
} | |
/*==================== | |
Message | |
======================*/ | |
#message { | |
position: absolute; | |
bottom: 20px; | |
color: $secondary-color; | |
line-height: 1.52rem; | |
padding: 1rem; | |
font-size: 0.9rem; | |
a { | |
color: $primary-color | |
} | |
} | |
@keyframes shockwave { | |
0% { | |
transform: scale(1); | |
box-shadow: 0 0 2px $secondary-color; | |
opacity: 1; | |
} | |
100% { | |
transform: scale(1); | |
opacity: 0; | |
box-shadow: 0 0 50px darken($secondary-color, 20%), inset 0 0 10px $secondary-color; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment