JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Power switch</title>
</head>
<body>
<a id="myButton">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="90" viewbox="0 0 540 540">
<defs>
<style type="text/css">
.off {stroke: var(--off);}
.power {
stroke: var(--on);
stroke-dasharray: 1000;
animation: powering 3s linear 0s 1 normal forwards;
}
@keyframes powering {
0% {stroke-dashoffset: 1000;}
100% {stroke-dashoffset: 0;}
}
.shutdown {
stroke: var(--on);
stroke-dasharray: 1000;
animation: shutdowning var(--time) linear 0s 1 normal forwards;
}
@keyframes shutdowning {
0% {stroke-dashoffset: var(--offset);}
100% {stroke-dashoffset: 1000;}
}
</style>
</defs>
<path fill="none" stroke="var(--off)" stroke-width="78" stroke-linecap="round" d="M400,143 a200,200 0 1,1 -260,0" />
<g>
<path class="off" id="myIcon1" stroke-width="80" stroke-linecap="round" fill="none" d="M400,143 a200,200 0 1,1 -260,0" />
<line class="off" id="myIcon2" stroke-width="80" stroke-linecap="round" x1="270" y1="240" x2="270" y2="45" />
</g>
</svg>
</a>
<div class="off2" id="animationTrick"></div>
</body>
</html>
CSS
:root {
--bg: #405480;
--off: #2C303A;
--on: #114B8E;
--time: 3s;
--offset: 1000;
}
html, body {
background: var(--bg);
color: #f5f5f5;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
a {
display: block;
cursor: pointer;
text-decoration: none;
user-select: none;
-webkit-user-select: none;
padding: 35px 120px;
line-height: 1;
white-space: nowrap;
color: #fff;
border-radius: 12px;
border: 1px solid #00397C;
box-shadow: 0 3px 2px rgba(0, 0, 0, 0.24),
0 9px 4px rgba(22, 21, 21, 0.45),
15px 15px 10px rgba(22, 21, 21, 0.2),
inset 0 -3px 6px rgba(0, 0, 0, 0.3),
inset 0 2px 0 rgba(255, 255, 255, 0.27),
inset 0 4px 6px #405480,
inset 1px 2px 3px 0px rgba(0, 0, 0, 0);
}
a:hover:not(.lock) {
box-shadow: 0 3px 2px rgba(0, 0, 0, 0.24),
0 0 0 rgba(22, 21, 21, 0),
5px 5px 10px rgba(22, 21, 21, 0.2),
inset 0 -3px 6px rgba(0, 0, 0, 0.3),
inset 0 2px 1px rgba(255, 255, 255, 0.2),
inset 0px 4px 6px 0px #405480,
inset 1px 2px 3px 0px rgba(0, 0, 0, 0);
}
a:active:not(.lock) {
box-shadow: 0 1px 1px 0 rgba(255, 255, 255, 0.32),
1px 1px 10px rgba(22, 21, 21, 0.2),
5px 5px 10px rgba(22, 21, 21, 0),
inset 0 3px 6px rgba(0, 0, 0, 0.3),
inset 6px 6px 10px rgba(0, 0, 0, 0.3),
inset 0 -2px 6px rgba(255, 255, 255, 0.1),
inset 1px 2px 3px 0px rgba(0, 0, 0, 0.5);
}
.lock {
cursor: not-allowed;
box-shadow: 0 1px 1px 0 rgba(255, 255, 255, 0.32),
1px 1px 10px rgba(22, 21, 21, 0.2),
5px 5px 10px rgba(22, 21, 21, 0),
inset 0 3px 6px rgba(0, 0, 0, 0.3),
inset 6px 6px 10px rgba(0, 0, 0, 0.3),
inset 0 -2px 6px rgba(255, 255, 255, 0.1),
inset 1px 2px 3px 0px rgba(0, 0, 0, 0.5);
}
div#animationTrick {
position: absolute;
visibility: hidden;
overflow: hidden;
height: 0px;
}
.power2 {
width: 1000px;
animation: powering2 3s linear 0s 1 normal forwards;
}
@keyframes powering2 {
0% { width: 1000px; }
100% { width:...
JavaScript
/*
L'icone on-off est constituée d'un arc de cercle (symbole 'off' nommé myIcon1) et d'un trait (symbole 'on' nommé myIcon2)
La classe de ces symboles SVG est définie comme suit :
myIcon1 myIcon2 myButton
--------------------------
off off état initial
power off appui sur le bouton
power blink disabled appui sur le bouton de plus de 3 secondes
on on disabled après wake-on-LAN et ping réussi (en test: après 10 secondes)
off off après extinction et ping en échec (en test : 1 minute)
*/
var wolTimeout, pingTimeout, suspendTimeout;
['mousedown', 'touchstart', 'keypress'].forEach(e => {
document.getElementById('myButton').addEventListener(e, ev => {
if((e != 'keypress' || (e == 'keypress' && ev.which == 32)) && document.getElementById('myIcon1').classList.contains('off')) {
let el1 = document.getElementById('myIcon1');
let el2 = document.getElementById('animationTrick');
el1.classList.replace('off', 'power');
el2.classList.replace('off2', 'power2');
}
})
});
['mouseup', 'touchend', 'keyup'].forEach(e => {
document.getElementById('myButton').addEventListener(e, ev => {
if((e != 'keyup' || (e == 'keyup' && ev.which == 32)) && document.getElementById('myIcon1').classList.contains('power') && document.getElementById('myIcon2').classList.contains('off')) {
let el1 = document.getElementById('myIcon1');
let el2 = document.getElementById('animationTrick');
el1.style.animationPlayState = 'paused';
el2.style.animationPlayState = 'paused';
let offset = el2.clientWidth;
document.documentElement.style.setProperty('--offset', offset);
let time = 3 * (1000 - offset) / 1000;
document.documentElement.style.setProperty('--time', time + 's');
el1.style.animationPlayState = 'running';
el2.style.animationPlayState = 'running';
el1.classList.replace('power',...