Skip to content

Instantly share code, notes, and snippets.

@ryanlucas
Last active June 19, 2023 16:36
Show Gist options
  • Save ryanlucas/20e5cd5749ffae86cc6289ab829600ec to your computer and use it in GitHub Desktop.
Save ryanlucas/20e5cd5749ffae86cc6289ab829600ec to your computer and use it in GitHub Desktop.
Animated Button
Animated Button
Go
click -> Loading
Loading
successful -> Success
failure -> Error
Success
reset -> Go
Error
reset -> Go
function add_styles(css_str){
const StyleID = 'sketch_styles';
let $s = document.querySelector('#' + StyleID);
if(null === $s){
$s = document.createElement('style');
$s.id = StyleID
document.head.appendChild($s)
}
$s.innerText = css_str.replace(/\r?\n|\r/g,'');
}
// Define CSS
add_styles(`
.animated-button {
background: white;
border: none;
border-radius: 60px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
font-family: Corbel, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", "DejaVu Sans", "Bitstream Vera Sans", "Liberation Sans", Verdana, "Verdana Ref", sans-serif;
font-size: 18px;
outline: none;
cursor: pointer;
font-weight: bolder;
letter-spacing: -0.4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
transition: all 300ms ease-in;
height: 60px;
width: 60px;
color: #2ea5bd;
}
.animated-button[data-state="Go"] {
width: 140px;
}
.animated-button[data-state="Success"] {
background: #51c191;
}
.animated-button[data-state="Error"] {
background: #fd775f;
animation-name: shake;
animation-iteration-count: 1;
animation-timing-function: ease-in-out;
animation-duration: 600ms;
animation-delay: 300ms;
}
.animated-button .icon {
background: red;
width: 28px;
height: 28px;
pointerEvents: none;
}
[data-state="Go"] .icon {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><defs><style>.a{fill:%2322a7c2;}</style></defs><path class="a" d="M18.54,5.13a1,1,0,0,0-1.41-.07L15.65,6.4a1,1,0,0,0-.08,1.42l4.91,5.42c.37.41.22.74-.33.74H4.39a1,1,0,0,0-1,1v2a1,1,0,0,0,1,1H20.47c.55,0,.69.33.31.73l-5.15,5.42a1,1,0,0,0,0,1.41l1.45,1.38a1,1,0,0,0,1.42,0L27.92,17a1.1,1.1,0,0,0,0-1.47Z"/></svg>');
background-repeat: no-repeat;
background-size: contain;
}
[data-state="Loading"] .icon {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><defs><style>.a{fill:%2322a7c2;}</style></defs><path class="a" d="M19.57,2.33h0a1.45,1.45,0,0,0,.9,1.47,12.56,12.56,0,1,1-17,11.73,12.42,12.42,0,0,1,.8-4.37,1.17,1.17,0,0,0-.7-1.52l-.12-.05a1.58,1.58,0,0,0-2,.95,15.13,15.13,0,0,0-.8,6A15.47,15.47,0,1,0,21.51,1.09,1.45,1.45,0,0,0,19.57,2.33Z"/></svg>');
background-repeat: no-repeat;
background-size: contain;
animation-name: spinning;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: 1s;
}
[data-state="Success"] .icon {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><defs><style>.a{fill:%23fff;}</style></defs><path class="a" d="M28,7.82a2.51,2.51,0,0,0-4.47-1.19L13.18,20.12a.22.22,0,0,1-.19.09.21.21,0,0,1-.15-.06L8.17,16.06a2.55,2.55,0,0,0-1.65-.61,2.5,2.5,0,0,0-1.88.85,2.52,2.52,0,0,0,.23,3.53l6.69,5.84a2.73,2.73,0,0,0,4-.39L27.47,9.67A2.5,2.5,0,0,0,28,7.82Z"/></svg>');
background-repeat: no-repeat;
background-size: contain;
animation-name: spin-in;
animation-iteration-count: 1;
animation-timing-function: linear;
animation-duration: 300ms;
}
[data-state="Error"] .icon {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><defs><style>.a{fill:%23fff;}</style></defs><path class="a" d="M16,27.36a2.89,2.89,0,0,1-2.19-.7,2.44,2.44,0,0,1-.66-1.73v-.77a2.44,2.44,0,0,1,.66-1.73,2.89,2.89,0,0,1,2.19-.7,2.89,2.89,0,0,1,2.19.7,2.44,2.44,0,0,1,.66,1.73v.77a2.44,2.44,0,0,1-.66,1.73A2.89,2.89,0,0,1,16,27.36Zm-1-7.42-1.5-9.09V4.64h5v6.21l-1.47,9.09Z"/></svg>');
background-repeat: no-repeat;
background-size: contain;
animation-name: spin-in;
animation-iteration-count: 1;
animation-timing-function: linear;
animation-duration: 300ms;
}
@keyframes spinning {
from { transform:rotate(0deg); }
to { transform:rotate(360deg); }
}
@keyframes spin-in {
0% { transform:rotate(-90deg); }
50% { transform:rotate(20deg); }
100% { transform:rotate(0deg); }
}
@keyframes shake {
from,
to {
transform: translate3d(0, 0, 0);
}
10%,
30%,
50%,
70%,
90% {
transform: translate3d(-4px, 0, 0);
}
20%,
40%,
60%,
80% {
transform: translate3d(4px, 0, 0);
}
}
`);
function render(model){
const current_state_name = model.active_states[0].name;
let events = {
Go: ["click"],
Loading: ["successful", "failure"],
Error: ["reset"],
Success: ["reset"]
}
return <div style={{background: "#fff0ed",height: "100%", display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center"}}>
<button class="animated-button" data-state={current_state_name}
onClick={() => model.emit(events[current_state_name][Math.floor(Math.random()*events[current_state_name].length)])}>
<div class="icon" ></div>
</button>
</div>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment