Spaces:
Running
Running
Update index.html
Browse files- index.html +24 -109
index.html
CHANGED
|
@@ -7,62 +7,30 @@
|
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
<style>
|
| 9 |
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@700;800&family=Inter:wght@400;600;800&display=swap');
|
| 10 |
-
|
| 11 |
body { margin: 0; padding: 0; width: 100vw; height: 100vh; overflow: hidden; background-color: #050508; color: #fff; font-family: 'Inter', sans-serif; display: flex; align-items: center; justify-content: center; }
|
| 12 |
-
|
| 13 |
-
/* Hiệu ứng nền */
|
| 14 |
.bg-glow { position: fixed; width: 60vw; height: 60vw; background: radial-gradient(circle, rgba(99, 102, 241, 0.15) 0%, rgba(0,0,0,0) 70%); top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 0; }
|
| 15 |
-
|
| 16 |
.main-container { position: relative; z-index: 10; width: 100%; max-width: 700px; display: flex; flex-direction: column; align-items: center; padding: 20px; }
|
| 17 |
-
|
| 18 |
-
/* Tiêu đề khẳng định thương hiệu */
|
| 19 |
-
.brand-title {
|
| 20 |
-
font-family: 'JetBrains Mono', monospace;
|
| 21 |
-
font-size: 10px;
|
| 22 |
-
letter-spacing: 5px;
|
| 23 |
-
font-weight: 800;
|
| 24 |
-
background: linear-gradient(to right, #6366f1, #a5b4fc, #6366f1);
|
| 25 |
-
-webkit-background-clip: text;
|
| 26 |
-
-webkit-text-fill-color: transparent;
|
| 27 |
-
text-transform: uppercase;
|
| 28 |
-
margin-bottom: 30px;
|
| 29 |
-
text-align: center;
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
.tz-select { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); color: #a5b4fc; padding: 6px 18px; border-radius: 12px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 25px; outline: none; cursor: pointer; transition: 0.3s; }
|
| 33 |
-
.tz-select:hover { background: rgba(99, 102, 241, 0.15); border-color: #6366f1; }
|
| 34 |
-
|
| 35 |
.clock-wrapper { display: flex; align-items: center; justify-content: center; font-family: 'JetBrains Mono', monospace; font-size: clamp(3.5rem, 18vw, 7.5rem); font-weight: 800; line-height: 1; gap: 4px; margin-bottom: 15px; }
|
| 36 |
.digit-column { height: 1.1em; overflow: hidden; position: relative; width: 0.65em; text-align: center; }
|
| 37 |
.digit-strip { transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
|
| 38 |
.digit-val { height: 1.1em; display: flex; align-items: center; justify-content: center; color: #fff; text-shadow: 0 0 30px rgba(99, 102, 241, 0.4); }
|
| 39 |
.sep { color: #4f46e5; opacity: 0.8; margin: 0 4px; padding-bottom: 15px; }
|
| 40 |
-
|
| 41 |
.stats-bar { background: rgba(255, 255, 255, 0.02); backdrop-filter: blur(25px); border: 1px solid rgba(255, 255, 255, 0.05); border-radius: 24px; padding: 15px 40px; display: flex; gap: 40px; margin-top: 40px; }
|
| 42 |
.stat-box { display: flex; flex-direction: column; align-items: center; }
|
| 43 |
.stat-label { font-size: 7px; text-transform: uppercase; letter-spacing: 2px; color: #4f46e5; margin-bottom: 4px; font-weight: 600; }
|
| 44 |
-
.stat-data { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700;
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
#countdown-overlay {
|
| 48 |
-
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 49 |
-
background: #000; display: none; z-index: 100;
|
| 50 |
-
flex-direction: column; align-items: center; justify-content: center;
|
| 51 |
-
}
|
| 52 |
-
#countdown-num { font-size: 18rem; font-weight: 900; color: #f87171; text-shadow: 0 0 60px rgba(248, 113, 113, 0.6); font-family: 'JetBrains Mono'; }
|
| 53 |
</style>
|
| 54 |
</head>
|
| 55 |
<body onclick="lockScreen()">
|
| 56 |
<div class="bg-glow"></div>
|
| 57 |
-
|
| 58 |
-
<div id="countdown-overlay">
|
| 59 |
-
<div class="text-white text-2xl tracking-[15px] mb-10 font-black">HAPPY NEW YEAR</div>
|
| 60 |
-
<div id="countdown-num">10</div>
|
| 61 |
-
</div>
|
| 62 |
|
| 63 |
<div class="main-container">
|
| 64 |
<div class="brand-title">Đồng hồ chính xác nhất thế giới</div>
|
| 65 |
-
|
| 66 |
<select id="timezone-selector" class="tz-select" onchange="changeTimezone()">
|
| 67 |
<option value="Asia/Ho_Chi_Minh">🇻🇳 Sài Gòn (GMT+7)</option>
|
| 68 |
<option value="Asia/Singapore">🇸🇬 Singapore (GMT+8)</option>
|
|
@@ -70,7 +38,6 @@
|
|
| 70 |
<option value="Europe/London">🇬🇧 London (GMT+0)</option>
|
| 71 |
<option value="America/New_York">🇺🇸 New York (GMT-5)</option>
|
| 72 |
</select>
|
| 73 |
-
|
| 74 |
<div class="clock-wrapper">
|
| 75 |
<div class="digit-column"><div class="digit-strip" id="h1"></div></div>
|
| 76 |
<div class="digit-column"><div class="digit-strip" id="h2"></div></div>
|
|
@@ -81,9 +48,7 @@
|
|
| 81 |
<div class="digit-column"><div class="digit-strip" id="s1"></div></div>
|
| 82 |
<div class="digit-column"><div class="digit-strip" id="s2"></div></div>
|
| 83 |
</div>
|
| 84 |
-
|
| 85 |
<div id="full-date" class="text-gray-500 text-[11px] tracking-[5px] uppercase font-bold">---</div>
|
| 86 |
-
|
| 87 |
<div class="stats-bar">
|
| 88 |
<div class="stat-box"><span class="stat-label">Network Ping</span><span class="stat-data" id="v-ping">--</span></div>
|
| 89 |
<div class="stat-box"><span class="stat-label">Time Offset</span><span class="stat-data" id="v-offset">--</span></div>
|
|
@@ -92,94 +57,44 @@
|
|
| 92 |
</div>
|
| 93 |
|
| 94 |
<script>
|
| 95 |
-
let serverOffset = 0;
|
| 96 |
-
|
| 97 |
-
let wakeLock = null;
|
| 98 |
|
| 99 |
function createStrips() {
|
| 100 |
['h1','h2','m1','m2','s1','s2'].forEach(id => {
|
| 101 |
-
const
|
| 102 |
-
for(let i=0; i<=9; i++) {
|
| 103 |
-
const div = document.createElement('div');
|
| 104 |
-
div.className = 'digit-val'; div.innerText = i; strip.appendChild(div);
|
| 105 |
-
}
|
| 106 |
});
|
| 107 |
}
|
| 108 |
-
|
| 109 |
-
function setDigit(id, val) {
|
| 110 |
-
const strip = document.getElementById(id);
|
| 111 |
-
if (strip) strip.style.transform = `translateY(-${val * 1.1}em)`;
|
| 112 |
-
}
|
| 113 |
-
|
| 114 |
function changeTimezone() { selectedTimezone = document.getElementById('timezone-selector').value; }
|
| 115 |
-
|
| 116 |
-
async function lockScreen() {
|
| 117 |
-
if ('wakeLock' in navigator) { try { wakeLock = await navigator.wakeLock.request('screen'); } catch(e){} }
|
| 118 |
-
}
|
| 119 |
|
| 120 |
async function sync() {
|
| 121 |
const start = performance.now();
|
| 122 |
try {
|
| 123 |
-
const r = await fetch(
|
| 124 |
const d = await r.json();
|
| 125 |
-
const ping =
|
| 126 |
-
|
| 127 |
serverOffset = (d.ts * 1000 + ping/2) - Date.now();
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
const elPing = document.getElementById('v-ping');
|
| 131 |
-
const elOffset = document.getElementById('v-offset');
|
| 132 |
-
|
| 133 |
-
// Màu sắc động
|
| 134 |
-
elPing.innerText = ping + "ms";
|
| 135 |
-
elPing.style.color = ping < 150 ? "#4ade80" : (ping < 400 ? "#facc15" : "#f87171");
|
| 136 |
-
|
| 137 |
-
elOffset.innerText = offsetAbs + "ms";
|
| 138 |
-
elOffset.style.color = offsetAbs < 10 ? "#4ade80" : (offsetAbs < 200 ? "#facc15" : "#f87171");
|
| 139 |
-
|
| 140 |
document.getElementById('v-ip').innerText = d.ip;
|
| 141 |
-
} catch(e){}
|
| 142 |
}
|
| 143 |
|
| 144 |
function update() {
|
| 145 |
const now = new Date(Date.now() + serverOffset);
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
const diff = (newYearDate - now) / 1000;
|
| 152 |
-
|
| 153 |
-
const overlay = document.getElementById('countdown-overlay');
|
| 154 |
-
if (diff > 0 && diff <= 10) {
|
| 155 |
-
overlay.style.display = 'flex';
|
| 156 |
-
document.getElementById('countdown-num').innerText = Math.ceil(diff);
|
| 157 |
-
} else {
|
| 158 |
-
overlay.style.display = 'none';
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
// Update Clock
|
| 162 |
-
const formatter = new Intl.DateTimeFormat('en-GB', {
|
| 163 |
-
timeZone: selectedTimezone, hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false
|
| 164 |
-
});
|
| 165 |
-
const parts = formatter.formatToParts(now);
|
| 166 |
-
const h = parts.find(p => p.type === 'hour').value;
|
| 167 |
-
const m = parts.find(p => p.type === 'minute').value;
|
| 168 |
-
const s = parts.find(p => p.type === 'second').value;
|
| 169 |
-
|
| 170 |
-
setDigit('h1', h[0]); setDigit('h2', h[1]);
|
| 171 |
-
setDigit('m1', m[0]); setDigit('m2', m[1]);
|
| 172 |
-
setDigit('s1', s[0]); setDigit('s2', s[1]);
|
| 173 |
-
|
| 174 |
-
document.getElementById('full-date').innerText = new Intl.DateTimeFormat('vi-VN', {
|
| 175 |
-
timeZone: selectedTimezone, weekday: 'long', day: 'numeric', month: 'long', year: 'numeric'
|
| 176 |
-
}).format(now);
|
| 177 |
}
|
| 178 |
|
| 179 |
-
createStrips();
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
setInterval(update, 500);
|
| 183 |
</script>
|
| 184 |
</body>
|
| 185 |
</html>
|
|
|
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
<style>
|
| 9 |
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@700;800&family=Inter:wght@400;600;800&display=swap');
|
|
|
|
| 10 |
body { margin: 0; padding: 0; width: 100vw; height: 100vh; overflow: hidden; background-color: #050508; color: #fff; font-family: 'Inter', sans-serif; display: flex; align-items: center; justify-content: center; }
|
|
|
|
|
|
|
| 11 |
.bg-glow { position: fixed; width: 60vw; height: 60vw; background: radial-gradient(circle, rgba(99, 102, 241, 0.15) 0%, rgba(0,0,0,0) 70%); top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 0; }
|
|
|
|
| 12 |
.main-container { position: relative; z-index: 10; width: 100%; max-width: 700px; display: flex; flex-direction: column; align-items: center; padding: 20px; }
|
| 13 |
+
.brand-title { font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: 5px; font-weight: 800; background: linear-gradient(to right, #6366f1, #a5b4fc, #6366f1); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-transform: uppercase; margin-bottom: 30px; text-align: center; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
.tz-select { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); color: #a5b4fc; padding: 6px 18px; border-radius: 12px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 25px; outline: none; cursor: pointer; transition: 0.3s; }
|
|
|
|
|
|
|
| 15 |
.clock-wrapper { display: flex; align-items: center; justify-content: center; font-family: 'JetBrains Mono', monospace; font-size: clamp(3.5rem, 18vw, 7.5rem); font-weight: 800; line-height: 1; gap: 4px; margin-bottom: 15px; }
|
| 16 |
.digit-column { height: 1.1em; overflow: hidden; position: relative; width: 0.65em; text-align: center; }
|
| 17 |
.digit-strip { transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
|
| 18 |
.digit-val { height: 1.1em; display: flex; align-items: center; justify-content: center; color: #fff; text-shadow: 0 0 30px rgba(99, 102, 241, 0.4); }
|
| 19 |
.sep { color: #4f46e5; opacity: 0.8; margin: 0 4px; padding-bottom: 15px; }
|
|
|
|
| 20 |
.stats-bar { background: rgba(255, 255, 255, 0.02); backdrop-filter: blur(25px); border: 1px solid rgba(255, 255, 255, 0.05); border-radius: 24px; padding: 15px 40px; display: flex; gap: 40px; margin-top: 40px; }
|
| 21 |
.stat-box { display: flex; flex-direction: column; align-items: center; }
|
| 22 |
.stat-label { font-size: 7px; text-transform: uppercase; letter-spacing: 2px; color: #4f46e5; margin-bottom: 4px; font-weight: 600; }
|
| 23 |
+
.stat-data { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700; }
|
| 24 |
+
#countdown-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #000; display: none; z-index: 100; flex-direction: column; align-items: center; justify-content: center; }
|
| 25 |
+
#countdown-num { font-size: 18rem; font-weight: 900; color: #f87171; text-shadow: 0 0 60px rgba(248, 113, 113, 0.6); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
</style>
|
| 27 |
</head>
|
| 28 |
<body onclick="lockScreen()">
|
| 29 |
<div class="bg-glow"></div>
|
| 30 |
+
<div id="countdown-overlay"><div class="text-white text-2xl tracking-[15px] mb-10 font-black">HAPPY NEW YEAR</div><div id="countdown-num">10</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
<div class="main-container">
|
| 33 |
<div class="brand-title">Đồng hồ chính xác nhất thế giới</div>
|
|
|
|
| 34 |
<select id="timezone-selector" class="tz-select" onchange="changeTimezone()">
|
| 35 |
<option value="Asia/Ho_Chi_Minh">🇻🇳 Sài Gòn (GMT+7)</option>
|
| 36 |
<option value="Asia/Singapore">🇸🇬 Singapore (GMT+8)</option>
|
|
|
|
| 38 |
<option value="Europe/London">🇬🇧 London (GMT+0)</option>
|
| 39 |
<option value="America/New_York">🇺🇸 New York (GMT-5)</option>
|
| 40 |
</select>
|
|
|
|
| 41 |
<div class="clock-wrapper">
|
| 42 |
<div class="digit-column"><div class="digit-strip" id="h1"></div></div>
|
| 43 |
<div class="digit-column"><div class="digit-strip" id="h2"></div></div>
|
|
|
|
| 48 |
<div class="digit-column"><div class="digit-strip" id="s1"></div></div>
|
| 49 |
<div class="digit-column"><div class="digit-strip" id="s2"></div></div>
|
| 50 |
</div>
|
|
|
|
| 51 |
<div id="full-date" class="text-gray-500 text-[11px] tracking-[5px] uppercase font-bold">---</div>
|
|
|
|
| 52 |
<div class="stats-bar">
|
| 53 |
<div class="stat-box"><span class="stat-label">Network Ping</span><span class="stat-data" id="v-ping">--</span></div>
|
| 54 |
<div class="stat-box"><span class="stat-label">Time Offset</span><span class="stat-data" id="v-offset">--</span></div>
|
|
|
|
| 57 |
</div>
|
| 58 |
|
| 59 |
<script>
|
| 60 |
+
let serverOffset = 0, selectedTimezone = 'Asia/Ho_Chi_Minh', wakeLock = null;
|
| 61 |
+
const API_URL = 'https://tuhbooh-networttools.hf.space';
|
|
|
|
| 62 |
|
| 63 |
function createStrips() {
|
| 64 |
['h1','h2','m1','m2','s1','s2'].forEach(id => {
|
| 65 |
+
const s = document.getElementById(id); s.innerHTML = '';
|
| 66 |
+
for(let i=0; i<=9; i++) { const d = document.createElement('div'); d.className = 'digit-val'; d.innerText = i; s.appendChild(d); }
|
|
|
|
|
|
|
|
|
|
| 67 |
});
|
| 68 |
}
|
| 69 |
+
function setDigit(id, val) { document.getElementById(id).style.transform = `translateY(-${val * 1.1}em)`; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
function changeTimezone() { selectedTimezone = document.getElementById('timezone-selector').value; }
|
| 71 |
+
async function lockScreen() { if(navigator.wakeLock) wakeLock = await navigator.wakeLock.request('screen'); }
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
async function sync() {
|
| 74 |
const start = performance.now();
|
| 75 |
try {
|
| 76 |
+
const r = await fetch(`${API_URL}?t=${Date.now()}`);
|
| 77 |
const d = await r.json();
|
| 78 |
+
const ping = performance.now() - start;
|
|
|
|
| 79 |
serverOffset = (d.ts * 1000 + ping/2) - Date.now();
|
| 80 |
+
document.getElementById('v-ping').innerText = Math.round(ping) + "ms";
|
| 81 |
+
document.getElementById('v-offset').innerText = Math.round(Math.abs(serverOffset)) + "ms";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
document.getElementById('v-ip').innerText = d.ip;
|
| 83 |
+
} catch(e) {}
|
| 84 |
}
|
| 85 |
|
| 86 |
function update() {
|
| 87 |
const now = new Date(Date.now() + serverOffset);
|
| 88 |
+
const timeStr = now.toLocaleTimeString('en-GB', {timeZone: selectedTimezone, hour12:false, hour:'2-digit', minute:'2-digit', second:'2-digit'});
|
| 89 |
+
const [h, m, s] = timeStr.split(':');
|
| 90 |
+
setDigit('h1', h[0]); setDigit('h2', h[1]); setDigit('m1', m[0]); setDigit('m2', m[1]); setDigit('s1', s[0]); setDigit('s2', s[1]);
|
| 91 |
+
document.getElementById('full-date').innerText = new Intl.DateTimeFormat('vi-VN', {timeZone: selectedTimezone, weekday:'long', day:'numeric', month:'long', year:'numeric'}).format(now);
|
| 92 |
+
requestAnimationFrame(update);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
}
|
| 94 |
|
| 95 |
+
createStrips(); sync();
|
| 96 |
+
setInterval(sync, 15000); // Sync mỗi 15s để ổn định
|
| 97 |
+
requestAnimationFrame(update);
|
|
|
|
| 98 |
</script>
|
| 99 |
</body>
|
| 100 |
</html>
|