Last active
August 7, 2022 13:13
-
-
Save motsu0/a6e3807cf385ff923575cc68d2d00748 to your computer and use it in GitHub Desktop.
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
.sandglass{ | |
box-sizing: border-box; | |
width: 90%; | |
max-width: 400px; | |
margin: 100px auto; | |
position: relative; | |
user-select: none; | |
} | |
.sandglass::after{ | |
content: ""; | |
display: block; | |
padding-top: 100%; | |
} | |
#sandglass-inner{ | |
box-sizing: border-box; | |
width: 100%; | |
height: 100%; | |
position: absolute; | |
top: 0; | |
left: 0; | |
transition: transform 1s; | |
} | |
.sandglass__half{ | |
height: 50%; | |
position: relative; | |
background-color: rgb(217, 255, 255); | |
} | |
.sandglass__top{ | |
clip-path: polygon(0 0, 100% 0, 51% 100%, 49% 100%); | |
} | |
.sandglass__bottom{ | |
clip-path: polygon(49% 0, 51% 0, 100% 100%, 0 100%); | |
} | |
.sandglass__top-wrapper,.sandglass__bottom-wrapper{ | |
width: 100%; | |
position: absolute; | |
top: 0; | |
left: 0; | |
z-index: 2; | |
} | |
.sandglass__bottom-wrapper{ | |
transform: rotateZ(180deg); | |
} | |
.sandglass__sand{ | |
width: 100%; | |
height: 0; | |
position: absolute; | |
left: 0; | |
bottom: 0; | |
z-index: 0; | |
background-color: #DEB887; | |
} | |
#sandglass-inner.s-rotated .sandglass__sand{ | |
top: 0; | |
} | |
#sandglass__particle{ | |
width: 8px; | |
height: 8px; | |
position: absolute; | |
z-index: 1; | |
top: 50%; | |
left: 50%; | |
transform: translate(-4px,-4px); | |
background-color: #DEB887; | |
} | |
/* */ | |
.settings{ | |
margin-top: 40px; | |
text-align: center; | |
} | |
.settings-row{ | |
margin: 24px 0; | |
} | |
#input-time{ | |
width: 48px; | |
height: 20px; | |
margin-right: 4px; | |
} | |
/* */ | |
.s-hide{ | |
display: none; | |
} | |
.t-pointer{ | |
cursor: pointer; | |
} |
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="sandglass"> | |
<div id="sandglass-inner" class="t-pointer"> | |
<div class="sandglass__half sandglass__top"> | |
<img src="pathto/sandglass-top.svg" alt="砂時計の半分の縁" class="sandglass__top-wrapper"> | |
<div id="sandglass__top__sand" class="sandglass__sand"></div> | |
</div> | |
<div class="sandglass__half sandglass__bottom"> | |
<img src="pathto/sandglass-top.svg" alt="砂時計の半分の縁" class="sandglass__bottom-wrapper"> | |
<div id="sandglass__bottom__sand" class="sandglass__sand"></div> | |
</div> | |
</div> | |
<div id="sandglass__particle" class="s-hide"></div> | |
</div> | |
<div class="settings"> | |
<div class="settings-row"> | |
<input id="input-time" class="t-pointer" type="number" min="1" max="3600" placeholder="3" value="3">分 | |
</div> | |
<div class="settings-row"> | |
<button id="bt-rotate" class="t-pointer">ひっくり返す</button> | |
</div> | |
<div class="settings-row"> | |
<button id="bt-reset" class="t-pointer">リセット</button> | |
</div> | |
<div class="settings-row"> | |
<input id="input-color" class="t-pointer" type="color" value="#DEB887"> | |
</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
const el_sandglass_inner = document.getElementById('sandglass-inner'); | |
const el_sandglass__sand = document.getElementsByClassName('sandglass__sand'); | |
const el_sandglass__top__sand = document.getElementById('sandglass__top__sand'); | |
const el_sandglass__bottom__sand = document.getElementById('sandglass__bottom__sand'); | |
const el_sandglass__particle = document.getElementById('sandglass__particle'); | |
const input_time = document.getElementById('input-time'); | |
const bt_rotate = document.getElementById('bt-rotate'); | |
const bt_reset = document.getElementById('bt-reset'); | |
const input_color = document.getElementById('input-color'); | |
const sand_max = 90; | |
const sand = [0,100]; | |
let timer_sand; | |
let time_max; | |
let time_remain = 0; | |
let timer_fall; | |
el_sandglass_inner.addEventListener('click',checkForm); | |
bt_rotate.addEventListener('click',checkForm); | |
bt_reset.addEventListener('click',reset); | |
input_color.addEventListener('input',changeColor); | |
const anime_rotate = el_sandglass_inner.animate([ | |
{ | |
transform: 'rotateZ(0)' | |
}, | |
{ | |
transform: 'rotateZ(180deg)' | |
} | |
],{ | |
duration: 1000, | |
fill: "forwards" | |
}); | |
anime_rotate.pause(); | |
anime_rotate.playbackRate = -1; | |
const height_sandglass = el_sandglass_inner.clientHeight; | |
const anime_fall = el_sandglass__particle.animate([ | |
{ | |
transform: 'translate(-4px,-4px)' | |
}, | |
{ | |
transform: `translate(-4px,${height_sandglass/2-8-height_sandglass*.01}px)` | |
} | |
],{ | |
duration: 500, | |
iterations: Infinity | |
}); | |
anime_fall.pause(); | |
anime_rotate.onfinish = ()=>{ | |
anime_fall.currentTime = 0; | |
el_sandglass__particle.classList.remove('s-hide'); | |
anime_fall.play(); | |
}; | |
viewSand(); | |
function checkForm(){ | |
const minute = Number(input_time.value); | |
if(minute===0) return; | |
time_max = 60*minute; | |
rotate(); | |
} | |
function rotate(){ | |
clearInterval(timer_sand); | |
el_sandglass__particle.classList.add('s-hide'); | |
anime_fall.pause(); | |
input_time.disabled = true; | |
el_sandglass_inner.classList.toggle('s-rotated'); | |
const is_nospin = !el_sandglass_inner.classList.contains('s-rotated'); | |
const diff = 100/time_max; | |
time_remain = time_max - time_remain; | |
viewSand(); | |
timer_sand = setInterval(()=>{ | |
if(time_remain>=1) time_remain--; | |
if(is_nospin){ | |
sand[0] -= diff; | |
sand[1] += diff; | |
}else{ | |
sand[0] += diff; | |
sand[1] -= diff; | |
} | |
sand.forEach((v,i)=>{ | |
if(v<0) sand[i] = 0; | |
if(v>100) sand[i] = 100; | |
}); | |
viewSand(); | |
if(time_remain===0){ | |
clearInterval(timer_sand); | |
el_sandglass__particle.classList.add('s-hide'); | |
anime_fall.pause(); | |
input_time.disabled = false; | |
} | |
},1000); | |
anime_rotate.playbackRate = -anime_rotate.playbackRate; | |
anime_rotate.play(); | |
} | |
function viewSand(){ | |
const is_nospin = !el_sandglass_inner.classList.contains('s-rotated'); | |
const height_sand = [0,0]; | |
if(is_nospin){ | |
height_sand[0] = sand_max*Math.sqrt((sand[0]*sand_max/100)/100); | |
height_sand[1] = 100 - 100*Math.sqrt(((100-sand_max)+sand_max*(100-sand[1])/100)/100)+1; | |
}else{ | |
height_sand[0] = 100 - 100*Math.sqrt(((100-sand_max)+sand_max*(100-sand[0])/100)/100)+1; | |
height_sand[1] = sand_max*Math.sqrt((sand[1]*sand_max/100)/100); | |
} | |
el_sandglass__top__sand.style.height = height_sand[0]+'%'; | |
el_sandglass__bottom__sand.style.height = height_sand[1]+'%'; | |
} | |
function reset(){ | |
clearInterval(timer_sand); | |
el_sandglass__particle.classList.add('s-hide'); | |
anime_fall.pause(); | |
sand[0] = 0; | |
sand[1] = 100; | |
time_remain = 0; | |
input_time.disabled = false; | |
el_sandglass_inner.classList.remove('s-rotated'); | |
anime_rotate.cancel(); | |
anime_rotate.playbackRate = -1; | |
anime_fall.cancel(); | |
viewSand(); | |
} | |
function changeColor(){ | |
const color = input_color.value; | |
[...el_sandglass__sand].forEach(el=>{ | |
el.style.backgroundColor = color; | |
}); | |
el_sandglass__particle.style.backgroundColor = color; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment