<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>该充电了</title>
<style>
* {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
scroll-behavior: smooth;
}
html,
body {
height: 100%;
overflow: hidden;
}
body {
font-family: Departure Mono;
background: #1e1e1e;
background: linear-gradient(28deg, #111, #272727);
color: #fff;
position: relative;
height: 100%;
}
.face {
--xv: 0;
--yv: 0;
background: #141414;
background: #fff1;
background-image: radial-gradient(transparent 1px, #111 0);
background-size: 3px 3px;
font-weight: 1000;
position: absolute;
margin: auto;
left: 0;
right: 0;
top: 0;
bottom: 3em;
width: 3em;
height: 3em;
border-radius: 10px;
border: 1px solid #fff2;
display: flex;
justify-content: center;
align-items: center;
padding-top: 1em;
overflow: hidden;
user-select: none;
box-shadow: 0px 0px 0px 2px #0005, 0px 0px 0px 1px #fff3,
-2px 2px 2px -1px #000, 0px 0px 12px 0px #fff0,
inset -2px 2px 3px -2px #fffa;
cursor: pointer;
transition: all 0.4s ease-out;
backdrop-filter: blur(10px);
perspective: 1000px;
}
.face #eyel {
translate: 0 -1em;
}
.face #eyer {
translate: 0 -1em;
}
body:has(.pwr-btn:not(.empty)) .face {
box-shadow: 0px 0px 0px 2px #0005, 0px 0px 0px 1px #fff3,
-2px 2px 2px -1px #000, 0px 0px 12px 0px #fff2,
inset -2px 2px 3px -2px #fffa;
}
.face > * {
text-shadow: -3px 3px 3px #000c, 0 1px 12px #fffa, 0 0 5px #fff;
}
body:has(.pwr-btn:not(.empty)) .face:hover > * {
transform: translateX(calc(var(--yv) * 0.2px)) translateY(
calc(var(--xv) * -0.2px)
);
transition: all 0.4s ease-out;
}
body:has(.pwr-btn.empty) .face > * {
opacity: 0.2;
}
.face::before {
content: '';
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: inset -2px 2px 2px 0px #fff5, inset 2px -2px 2px 1px #1118;
scale: 1.1;
border-radius: 12px;
transform: translateX(calc(var(--yv) * 0.2px)) translateY(
calc(var(--xv) * -0.2px)
);
transition: all 0.4s ease-out;
}
.face::after {
content: '';
display: block;
width: 200%;
height: 0.6em;
background: #fff2;
position: absolute;
rotate: 24deg;
top: -50%;
transition: all 0.4s ease-in-out;
}
.face:hover {
scale: 2;
box-shadow: -24px 33px 8px -10px #0005, 0px 0px 0px 1px #fff3,
-2px 2px 2px -1px #000, 0px 0px 12px 0px #fff0,
inset -2px 2px 3px -2px #fffc !important;
}
.face:hover::after {
top: 150%;
rotate: 42deg;
}
.pwr-btn {
position: absolute;
left: 0;
right: 0;
top: 2em;
bottom: 0;
margin: auto;
translate: 0 min(10em, 30vh);
background: #141414;
background: #5478ad85;
background-image: radial-gradient(transparent 1px, #111 0);
background-size: 3px 3px;
width: fit-content;
height: fit-content;
padding: 0.4em 1.6em;
border: 1px solid #fff4;
border-radius: 6px;
cursor: pointer;
box-shadow: 0px 0px 0px 2px #0005, 0px 0px 0px 1px #fff3,
0px 0px 0px 1px #fff3, -2px 2px 2px -1px #000, 0px 0px 12px 0px #fff2,
inset -2px 2px 3px -2px #fffa;
text-shadow: 0 1px 12px #fffa, 0 0 5px #fff;
user-select: none;
transition: all 0.4s ease-in-out;
}
.pwr-btn::before {
content: '';
display: block;
position: absolute;
left: 0;
bottom: 0;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
background: #fff;
mix-blend-mode: difference;
filter: blur(1px);
height: calc(var(--power, 0) * 1%);
width: 100%;
transition: all 0.4s ease-in-out;
}
.pwr-btn.full::before {
border-radius: 6px;
}
.pwr-btn.full {
filter: brightness(1.2);
box-shadow: 0px 0px 0px 2px #0005, 0px 0px 6px 1px #fff3,
0px 0px 12px 1px #fff3, -2px 2px 2px -1px #000, 0px 0px 12px 0px #fff2,
inset -2px 2px 3px -2px #fffa;
}
.pwr-btn.empty {
filter: brightness(0.5);
}
.power-line {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
translate: 0 calc(min(10em, 30vh) / 2);
width: 1.6px;
height: min(10em, 30vh);
transition: all 0.4s ease-in-out;
background: repeating-linear-gradient(
transparent,
transparent 4px,
#fff1 4px,
#fff1 8px
);
filter: drop-shadow(0 0 2px #fff);
overflow: hidden;
}
.power-line::before {
content: '';
display: block;
position: absolute;
bottom: 0;
width: 100%;
height: 40%;
background: linear-gradient(transparent 0%, #fff 50%, transparent 100%);
animation: generation 1.618s ease-out infinite;
opacity: 0;
}
@keyframes generation {
0% {
translate: 0 100%;
}
100% {
translate: 0 calc(-1 * min(10em, 30vh));
}
}
.power-line::after {
content: '';
display: block;
position: absolute;
bottom: 0;
left: -2.5em;
width: 5em;
height: 100%;
box-shadow: inset 0 0 2em 1em #fff6;
opacity: 0;
}
body:has(.pwr-btn:not(.empty)) .power-line::before,
body:has(.pwr-btn:not(.empty)) .power-line::after {
opacity: 0.6;
transition: all 1.618s ease-in-out;
}
body:has(.pwr-btn:not(.empty)) .power-line {
background: repeating-linear-gradient(
transparent,
transparent 4px,
#fff4 4px,
#fff4 8px
);
}
.face .zzz {
display: none;
}
.face.sleeping .zzz {
display: block;
color: #fff;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.face .zzz span {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
position: absolute;
left: 20%;
right: 0;
top: 20%;
bottom: 0;
scale: 0;
font-size: 1em;
animation: zzzs 1.5s linear infinite;
}
.face .zzz span:nth-child(2) {
animation-delay: 0.5s;
}
.face .zzz span:nth-child(3) {
animation-delay: 1s;
}
@keyframes zzzs {
0% {
scale: 0;
translate: 0;
rotate: 0;
}
100% {
scale: 1;
translate: 1em -2em;
rotate: -45deg;
}
}
</style>
</head>
<body>
<div class="power-line"></div>
<div class="face">
<div id="eyel">x</div>
<div id="mouth">-</div>
<div id="eyer">x</div>
<div class="zzz"><span>Z</span><span>z</span><span>z</span></div>
</div>
<div class="pwr-btn empty">Generate</div>
<script>
let eyel, eyer, mouth;
let standardInterval;
let currentMood = 'dead';
const faceEl = document.querySelector('.face');
moods(currentMood);
function cycleChar(eid, charPairs) {
const element = document.getElementById(eid);
if (!element) return;
if (element.timeoutId) {
clearTimeout(element.timeoutId);
}
if (!element.generation) {
element.generation = 0;
}
element.generation++;
const generation = element.generation;
let index = 0;
function updateChar() {
if (element.generation !== generation) return;
const [char, duration] = charPairs[index];
element.textContent = char;
index = (index + 1) % charPairs.length;
if (duration != 0) {
element.timeoutId = setTimeout(updateChar, duration * 1000);
} else {
element.timeoutId = null;
}
}
updateChar();
}
function clearCharCycles() {
['eyel', 'eyer', 'mouth'].forEach((id) => {
const element = document.getElementById(id);
if (element && element.timeoutId) {
clearTimeout(element.timeoutId);
element.timeoutId = null;
}
if (element && element.generation) {
element.generation++;
}
});
}
function moods(mood) {
clearCharCycles();
if (mood === 'dead') {
eyel = [['x', 0]];
eyer = [['x', 0]];
mouth = [['-', 0]];
}
if (mood === 'awake') {
eyel = [
['.', 4],
['', 0.3],
['\u{2027}', 7],
['', 0.3],
];
eyer = [
['.', 4],
['', 0.3],
['\u{2027}', 7],
['', 0.3],
];
mouth = [['.', 0]];
}
if (mood === 'happy') {
eyel = [
['.', 4],
['', 0.3],
['\u{2027}', 7],
['', 0.3],
];
eyer = [
['.', 4],
['', 0.3],
['\u{2027}', 7],
['', 0.3],
];
mouth = [['\u{02d8}', 0]];
}
if (mood === 'excited') {
eyel = [
['O', 3],
['\u{002a}', 0.4],
['O', 1.6],
['>', 2],
];
eyer = [
['O', 5],
['<', 2],
];
mouth = [
['\u{02d8}', 5],
['\u{25bd}', 2],
];
}
if (mood === 'concerned') {
eyel = [
['.', 4],
['', 0.3],
['\u{2027}', 7],
['', 0.3],
];
eyer = [
['.', 4],
['', 0.3],
['\u{2027}', 7],
['', 0.3],
];
mouth = [['\u{00ba}', 0]];
}
if (mood === 'sleeping') {
eyel = [['-', 4]];
eyer = [['-', 4]];
mouth = [
['\u{2027}', 4],
['\u{00ba}', 1],
];
faceEl.classList.add('sleeping');
} else if (faceEl.classList.contains('sleeping')) {
faceEl.classList.remove('sleeping');
}
cycleChar('eyel', eyel);
cycleChar('eyer', eyer);
cycleChar('mouth', mouth);
}
let power = 0;
const maxPower = 100;
const decayRate = 1;
const increment = 2;
const holdDuration = 16180;
let isHolding = false;
let isDropping = false;
let lastPower = power;
let accumulatedDrop = 0;
const powerBtn = document.querySelector('.pwr-btn');
function updatePower() {
if (!isHolding) {
power = Math.max(0, power - decayRate);
checkPowerDrop();
updatePowerHeight();
}
let newMood = currentMood;
if (power === 0) {
powerBtn.classList.add('empty');
powerBtn.classList.remove('full');
powerBtn.innerHTML = 'Generate';
} else if (power >= 100) {
powerBtn.innerHTML = '~ FULL ~';
powerBtn.classList.add('full');
powerBtn.classList.remove('empty');
} else {
powerBtn.classList.remove('full');
powerBtn.classList.remove('empty');
powerBtn.innerHTML = 'Generate';
}
if (power === 0) {
newMood = 'dead';
} else if (power > 0 && power < 20) {
newMood = 'concerned';
} else if (isDropping && power > 30 && power < 70) {
newMood = 'sleeping';
} else if (power >= 20 && power < 50) {
newMood = 'awake';
} else if (power >= 50 && power < 90) {
newMood = 'happy';
} else if (power >= 90 && power <= 100) {
newMood = 'excited';
}
const moodList = [
'dead',
'excited',
'awake',
'concerned',
'happy',
'sleeping',
];
if (newMood !== currentMood) {
currentMood = newMood;
if (moodList.includes(newMood)) {
moods(newMood);
} else {
clearCharCycles();
}
}
}
function checkPowerDrop() {
if (power < lastPower) {
accumulatedDrop += lastPower - power;
} else if (power > lastPower) {
accumulatedDrop = 0;
}
if (accumulatedDrop >= 20) {
isDropping = true;
} else {
isDropping = false;
}
lastPower = power;
}
function increasePower() {
accumulatedDrop = 0;
if (!isHolding) {
power = Math.min(maxPower, power + increment);
if (power === maxPower && !isHolding) {
startHold();
}
updatePowerHeight();
}
}
function updatePowerHeight() {
powerBtn.style.setProperty('--power', power);
}
function startHold() {
isHolding = true;
powerBtn.classList.add('full');
powerBtn.innerHTML = '~ FULL ~';
setTimeout(() => {
isHolding = false;
}, holdDuration);
}
powerBtn.addEventListener('click', increasePower);
standardInterval = setInterval(updatePower, 1000);
updatePowerHeight();
const ct = document.querySelector('.face');
ct.addEventListener('mousemove', function (e) {
const rect = ct.getBoundingClientRect();
const se = 42;
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const rotateX = (y / rect.height - 0.5) * -se;
const rotateY = (x / rect.width - 0.5) * se;
ct.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
ct.style.setProperty('--xv', rotateX);
ct.style.setProperty('--yv', rotateY);
});
ct.addEventListener('mouseleave', function () {
ct.style.transform = 'rotateX(0deg) rotateY(0deg)';
ct.style.setProperty('--xv', 0);
ct.style.setProperty('--yv', 0);
});
</script>
</body>
</html>