1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 05:37:52 +03:00
Files
android_cast/examples/app_hub/clock-widget.html
Anton Afanasyeu 2b7397edd2 sync
2026-05-27 22:53:49 +02:00

483 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clock Widget</title>
<style>
:root {
--bg: #0f1218;
--panel: #171c24;
--panel-2: #131820;
--border: rgba(255, 255, 255, 0.72);
--text: #eef3ff;
--muted: #9aa5ba;
--accent: #dbe7ff;
--digit: #d8f6b8;
--digit-dim: #76906a;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: radial-gradient(circle at 20% 15%, #1f2a3b 0%, var(--bg) 50%, #090c12 100%);
color: var(--text);
font-family: "Segoe UI", Tahoma, sans-serif;
padding: 18px;
}
.clock-widget {
width: min(980px, 100%);
border: 1px solid var(--border);
box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.08) inset;
background: linear-gradient(180deg, #141a22 0%, #0f131a 100%);
border-radius: 8px;
overflow: hidden;
}
.row {
width: 100%;
display: grid;
border-top: 1px solid rgba(255, 255, 255, 0.3);
}
.row:first-child {
border-top: 0;
}
.cell {
border-left: 1px solid rgba(255, 255, 255, 0.3);
min-height: 72px;
padding: 10px;
background: var(--panel);
}
.cell:first-child {
border-left: 0;
}
.row-1 {
grid-template-columns: 1fr;
}
.row-2 {
grid-template-columns: repeat(4, 1fr);
}
.row-3 {
grid-template-columns: repeat(3, 1fr);
}
.main-clock {
min-height: 220px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0)),
repeating-linear-gradient(
90deg,
rgba(255, 255, 255, 0.02) 0,
rgba(255, 255, 255, 0.02) 1px,
transparent 1px,
transparent 6px
),
#111822;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 10px;
text-align: center;
}
.primary-zone {
color: var(--muted);
letter-spacing: 0.14em;
font-weight: 600;
font-size: 0.82rem;
text-transform: uppercase;
}
.primary-time {
display: inline-flex;
align-items: flex-end;
gap: 6px;
padding: 10px 16px 8px;
border: 1px solid rgba(220, 235, 180, 0.45);
border-radius: 6px;
background: linear-gradient(180deg, #202a1e 0%, #181f16 100%);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04), 0 8px 18px rgba(0, 0, 0, 0.45);
font-family: "Overtime LCD", "DS-Digital", "Digital-7 Mono", "Consolas", monospace;
font-variant-numeric: tabular-nums;
color: var(--digit);
text-shadow: 0 0 5px rgba(190, 240, 150, 0.35), 0 0 1px rgba(180, 220, 145, 0.7);
letter-spacing: 0.06em;
line-height: 1;
}
.primary-time .hh,
.primary-time .mm {
font-size: clamp(3.1rem, 9.2vw, 5.7rem);
font-weight: 500;
}
.primary-time .colon {
display: inline-block;
width: 0.55em;
text-align: center;
font-size: clamp(3.1rem, 9.2vw, 5.7rem);
animation: casioBlink 1.25s linear infinite;
}
.primary-time .ss {
min-width: 2ch;
font-size: clamp(1.35rem, 3.1vw, 2rem);
margin-left: 2px;
margin-bottom: 0.5em;
color: var(--digit);
}
.date-cell {
display: flex;
justify-content: center;
align-items: center;
background: var(--panel-2);
color: #e8ecf6;
font-family: "Consolas", "Menlo", monospace;
font-size: clamp(0.92rem, 1.5vw, 1.1rem);
letter-spacing: 0.08em;
font-weight: 700;
text-transform: uppercase;
text-align: center;
}
.placeholder {
background: linear-gradient(180deg, #161d27 0%, #121923 100%);
}
.analog-cell {
min-height: 250px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
gap: 12px;
background: radial-gradient(circle at 50% 0%, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0) 55%), #121923;
}
.tz-label {
margin-top: 2px;
font-size: 0.82rem;
letter-spacing: 0.14em;
color: var(--muted);
text-transform: uppercase;
font-weight: 700;
}
.wall-clock {
width: min(94%, 220px);
aspect-ratio: 1 / 1;
border-radius: 50%;
position: relative;
border: 4px solid rgba(239, 243, 252, 0.78);
background:
radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0) 36%),
radial-gradient(circle at 50% 50%, #f6f8fc 0%, #e5ebf6 62%, #d3dceb 100%);
box-shadow:
0 12px 24px rgba(0, 0, 0, 0.45),
inset -8px -8px 18px rgba(0, 0, 0, 0.12),
inset 8px 10px 14px rgba(255, 255, 255, 0.4);
}
.roman {
position: absolute;
left: 50%;
top: 50%;
width: 20px;
height: 20px;
margin: -10px;
transform: rotate(calc(var(--i) * 30deg)) translateY(calc(var(--radius) * -1)) rotate(calc(var(--i) * -30deg));
font-family: "Times New Roman", serif;
font-size: clamp(0.76rem, 1.1vw, 0.95rem);
font-weight: 700;
color: #2a3447;
text-align: center;
user-select: none;
display: grid;
place-items: center;
}
.hand {
position: absolute;
left: 50%;
top: 50%;
transform-origin: 50% 100%;
transform: translate(-50%, -100%) rotate(0deg);
border-radius: 999px;
transition: transform 0.1s linear;
}
.hand.hour {
width: 6px;
height: 28%;
background: #1f2f46;
}
.hand.minute {
width: 4px;
height: 38%;
background: #2e4767;
}
.hand.second {
width: 2px;
height: 42%;
background: #b63b3b;
}
.pin {
position: absolute;
left: 50%;
top: 50%;
width: 12px;
height: 12px;
margin-left: -6px;
margin-top: -6px;
border-radius: 50%;
background: #111928;
border: 2px solid #f3f6ff;
z-index: 10;
}
.digital-subtime {
margin-top: auto;
margin-bottom: 3px;
font-family: "Consolas", monospace;
font-size: 0.9rem;
color: #d6deef;
letter-spacing: 0.08em;
}
@keyframes casioBlink {
0%, 20% {
opacity: 0;
}
20.01%, 60% {
opacity: 1;
}
60.01%, 100% {
opacity: 0;
}
}
@media (max-width: 900px) {
.row-2 {
grid-template-columns: repeat(2, 1fr);
}
.row-3 {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="clock-widget" id="clockWidget">
<div class="row row-1">
<div class="cell main-clock">
<div class="primary-zone" id="primaryZoneLabel"></div>
<div class="primary-time" aria-label="Primary time">
<span class="hh" id="primaryHH">00</span>
<span class="colon">:</span>
<span class="mm" id="primaryMM">00</span>
<span class="ss" id="primarySS">00</span>
</div>
</div>
</div>
<div class="row row-2">
<div class="cell placeholder"></div>
<div class="cell placeholder"></div>
<div class="cell placeholder"></div>
<div class="cell date-cell" id="dateCell">MON 01 JAN 2026</div>
</div>
<div class="row row-3" id="secondaryRow"></div>
</div>
<script>
(function () {
var CITY_LABELS = {
"America/New_York": "NEW YORK",
"Europe/Moscow": "MOSCOW",
"Asia/Shanghai": "BEIJING",
"Africa/Johannesburg": "CAPETOWN"
};
var ROMAN = ["XII", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI"];
var WEEK = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
var MONTH = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
function pad2(v) {
return String(v).padStart(2, "0");
}
function getSystemTimeZone() {
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
}
function computeSecondaryZones(primaryZone) {
var preferred = ["America/New_York", "Europe/Moscow", "Asia/Shanghai"];
var secondaries = preferred.filter(function (z) { return z !== primaryZone; });
while (secondaries.length < 3) {
if (!secondaries.includes("Africa/Johannesburg") && primaryZone !== "Africa/Johannesburg") {
secondaries.push("Africa/Johannesburg");
} else {
secondaries.push("UTC");
}
}
return secondaries.slice(0, 3);
}
function formatDatePrimary(date, zone) {
var parts = new Intl.DateTimeFormat("en-US", {
timeZone: zone,
weekday: "short",
day: "2-digit",
month: "short",
year: "numeric"
}).formatToParts(date);
var map = {};
parts.forEach(function (p) {
if (p.type !== "literal") map[p.type] = p.value;
});
var day = (map.weekday || "---").slice(0, 3).toUpperCase();
var dd = map.day || "00";
var mon = (map.month || "---").slice(0, 3).toUpperCase();
var yyyy = map.year || "0000";
return day + " " + dd + " " + mon + " " + yyyy;
}
function getTimeParts(date, zone) {
var parts = new Intl.DateTimeFormat("en-US", {
timeZone: zone,
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit"
}).formatToParts(date);
var map = {};
parts.forEach(function (p) {
if (p.type !== "literal") map[p.type] = p.value;
});
return {
hh: map.hour || "00",
mm: map.minute || "00",
ss: map.second || "00"
};
}
function buildSecondaryClock(tz) {
var cell = document.createElement("div");
cell.className = "cell analog-cell";
var label = document.createElement("div");
label.className = "tz-label";
label.textContent = CITY_LABELS[tz] || tz.toUpperCase();
cell.appendChild(label);
var clock = document.createElement("div");
clock.className = "wall-clock";
for (var i = 0; i < 12; i++) {
var numeral = document.createElement("div");
numeral.className = "roman";
numeral.style.setProperty("--i", String(i));
numeral.style.setProperty("--radius", "41%");
numeral.textContent = ROMAN[i];
clock.appendChild(numeral);
}
var hour = document.createElement("div");
hour.className = "hand hour";
var minute = document.createElement("div");
minute.className = "hand minute";
var second = document.createElement("div");
second.className = "hand second";
var pin = document.createElement("div");
pin.className = "pin";
clock.appendChild(hour);
clock.appendChild(minute);
clock.appendChild(second);
clock.appendChild(pin);
cell.appendChild(clock);
var digital = document.createElement("div");
digital.className = "digital-subtime";
digital.textContent = "--:--:--";
cell.appendChild(digital);
return {
tz: tz,
root: cell,
hour: hour,
minute: minute,
second: second,
digital: digital
};
}
var primaryZone = getSystemTimeZone();
var secondaryZones = computeSecondaryZones(primaryZone);
var secondaryRow = document.getElementById("secondaryRow");
var secondaryClocks = secondaryZones.map(function (tz) {
var c = buildSecondaryClock(tz);
secondaryRow.appendChild(c.root);
return c;
});
document.getElementById("primaryZoneLabel").textContent = "PRIMARY TZ: " + (CITY_LABELS[primaryZone] || primaryZone.toUpperCase());
function update() {
var now = new Date();
var primary = getTimeParts(now, primaryZone);
document.getElementById("primaryHH").textContent = primary.hh;
document.getElementById("primaryMM").textContent = primary.mm;
document.getElementById("primarySS").textContent = primary.ss;
document.getElementById("dateCell").textContent = formatDatePrimary(now, primaryZone);
secondaryClocks.forEach(function (clock) {
var p = getTimeParts(now, clock.tz);
var hNum = Number(p.hh);
var mNum = Number(p.mm);
var sNum = Number(p.ss);
var hourDeg = ((hNum % 12) + mNum / 60 + sNum / 3600) * 30;
var minuteDeg = (mNum + sNum / 60) * 6;
var secondDeg = sNum * 6;
clock.hour.style.transform = "translate(-50%, -100%) rotate(" + hourDeg + "deg)";
clock.minute.style.transform = "translate(-50%, -100%) rotate(" + minuteDeg + "deg)";
clock.second.style.transform = "translate(-50%, -100%) rotate(" + secondDeg + "deg)";
clock.digital.textContent = p.hh + ":" + p.mm + ":" + p.ss;
});
}
update();
setInterval(update, 1000);
})();
</script>
</body>
</html>