Files
BusSchedule127/web/index.html

225 lines
9.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Автобус №127 · Кандалакша — Полярные Зори — Зашеек</title>
<style>
:root{
--bg:#0d1220; --bg2:#131a2e; --card:#182238; --card2:#1e2a45;
--line:#2a3757; --txt:#e8edf7; --muted:#8b98b8;
--accent:#38d39f; --accent2:#4f9cff; --warn:#ffb648;
}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Ubuntu,sans-serif;
background:radial-gradient(1200px 600px at 70% -10%,#1b2a4d 0%,var(--bg) 55%);
color:var(--txt);min-height:100vh;padding:24px 16px 48px}
.wrap{max-width:760px;margin:0 auto}
header{text-align:center;margin-bottom:18px}
.route{font-size:12px;letter-spacing:.14em;color:var(--muted);text-transform:uppercase}
h1{font-size:clamp(22px,4.5vw,30px);margin:6px 0 4px;font-weight:700}
.sub{color:var(--muted);font-size:14px}
.clock{font-size:22px;font-weight:600;margin-top:10px;color:var(--accent)}
.clock small{color:var(--muted);font-weight:400;font-size:12px}
.day{display:inline-block;margin-top:8px;padding:4px 12px;border-radius:999px;
font-size:12px;border:1px solid var(--line);color:var(--muted)}
.day b{color:var(--accent2)}
.pickers{display:flex;gap:8px;margin:16px 0}
.pick{flex:1;padding:13px 8px;border-radius:12px;border:1px solid var(--line);
background:var(--card);color:var(--muted);font-size:14px;cursor:pointer;
transition:.15s;text-align:center;font-weight:600}
.pick:hover{border-color:var(--accent2);color:var(--txt)}
.pick.active{background:linear-gradient(135deg,var(--accent2),var(--accent));
color:#04101c;border-color:transparent;box-shadow:0 6px 20px rgba(79,156,255,.25)}
.where{text-align:center;color:var(--muted);font-size:13px;margin:2px 0 12px}
.dir{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px}
.card h3{margin:0 0 4px;font-size:15px;font-weight:700}
.card h3 .arr{color:var(--accent)}
.card .via{color:var(--muted);font-size:11px;margin-bottom:10px;letter-spacing:.03em}
.trip{display:flex;align-items:baseline;justify-content:space-between;padding:8px 0;
border-bottom:1px dashed var(--line);font-size:15px}
.trip:last-child{border-bottom:none}
.trip .t{font-variant-numeric:tabular-nums;font-weight:700;font-size:19px}
.trip .t small{font-weight:400;color:var(--muted);font-size:11px;display:block}
.trip .w{color:var(--accent);font-size:12px;font-weight:600;white-space:nowrap}
.trip.soon .w{color:var(--warn)}
.empty{color:var(--muted);font-size:13px;padding:6px 0}
/* кнопка полного расписания */
.fullbtn{display:block;width:100%;margin:16px 0 6px;padding:12px;border-radius:12px;
border:1px solid var(--line);background:var(--card2);color:var(--txt);
font-size:14px;font-weight:600;cursor:pointer;transition:.15s}
.fullbtn:hover{border-color:var(--accent2)}
.fullbtn .chev{float:right;transition:.2s;color:var(--muted)}
.fullbtn.open .chev{transform:rotate(180deg)}
.tablecard{background:var(--card);border:1px solid var(--line);border-radius:14px;
overflow:hidden;display:none;margin-bottom:8px}
.thead{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;background:var(--card2);
font-size:11px;color:var(--muted);text-align:center}
.thead div{padding:11px 6px;border-left:1px solid var(--line)}
.thead div:first-child{border-left:none}
.thead b{display:block;color:var(--txt);font-size:12px;font-weight:600}
.row{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;text-align:center;border-top:1px solid var(--line);
font-variant-numeric:tabular-nums;font-size:15px}
.row div{padding:10px 6px;border-left:1px solid var(--line)}
.row div:first-child{border-left:none}
.row:nth-child(even){background:#15203a}
footer{margin-top:22px;text-align:center;color:var(--muted);font-size:12px}
@media(max-width:560px){.dir{grid-template-columns:1fr}.pickers{gap:6px}}
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="route">Мурманская область · регулярный маршрут</div>
<h1>Автобус №127</h1>
<div class="sub">Кандалакша — Полярные Зори — Зашеек</div>
<div class="clock" id="clock">--:--:-- <small>МСК</small></div>
<div class="day" id="dayBadge"></div>
</header>
<div class="pickers">
<button class="pick" data-stop="Кандалакша">Кандалакша</button>
<button class="pick active" data-stop="Полярные Зори">Полярные Зори</button>
<button class="pick" data-stop="Зашеек">Зашеек</button>
</div>
<div class="where" id="whereLabel">Вы в Полярных Зорях — доступные направления:</div>
<section class="dir" id="dirBox"></section>
<button class="fullbtn" id="fullBtn">Полное расписание <span class="chev">▾</span></button>
<section class="tablecard" id="fullTable">
<div class="thead">
<div><b>Кандалакша</b>отпр.</div>
<div><b>Полярные Зори</b>→ Зашеек</div>
<div><b>Зашеек</b>прибытие</div>
<div><b>Полярные Зори</b>→ Кандалакша</div>
</div>
<div id="tableBody"></div>
</section>
<footer>Расписание действует с 30.09.2020 · время московское (МСК)</footer>
</div>
<script>
const SCHEDULE = [
["05:20","06:20","06:30","06:50"],
["06:10","07:00","07:25","07:45"],
["07:40","08:30","09:00","09:20"],
["08:00","08:50","09:20","09:50"],
["09:45","10:35","11:05","11:25"],
["11:00","11:50","12:20","12:40"],
["12:30","13:20","13:50","14:10"],
["13:30","14:20","14:50","15:10"],
["14:00","14:50","15:25","15:50"],
["15:30","16:20","17:00","17:25"],
["16:50","17:40","18:10","18:35"],
["18:20","19:10","19:40","20:05"],
["20:20","21:10","21:40","22:05"]
];
const DIRECTIONS = {
"Кандалакша": [
{to:"Полярные Зори", dep:0, arr:1, via:"через 1 час"},
{to:"Зашеек", dep:0, arr:2, via:"через Полярные Зори"}
],
"Полярные Зори": [
{to:"Зашеек", dep:1, arr:2, via:"≈ 30 мин в пути"},
{to:"Кандалакша", dep:3, arr:null, via:"≈ 1 час в пути"}
],
"Зашеек": [
{to:"Полярные Зори", dep:2, arr:3, via:"≈ 20 мин в пути"},
{to:"Кандалакша", dep:2, arr:null, via:"через Полярные Зори"}
]
};
const DAYS = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"];
function toMin(t){ const [h,m]=t.split(":").map(Number); return h*60+m; }
function nowMs(){ return new Date(Date.now()+3*3600*1000); } // МСК = UTC+3
function fmtWait(m){
if(m <= 0) return "отправление сейчас";
const h = Math.floor(m/60), mm = m%60;
if(h === 0) return `через ${mm} мин`;
if(mm === 0) return `через ${h} ч`;
return `через ${h} ч ${mm} мин`;
}
let currentStop = "Полярные Зори";
function render(){
const now = nowMs();
const curMin = now.getHours()*60 + now.getMinutes();
const dirs = DIRECTIONS[currentStop];
document.getElementById("dirBox").innerHTML = dirs.map(d=>{
const upcoming = SCHEDULE
.map((r,ri)=>({dep:toMin(r[d.dep]), arr:d.arr!==null?r[d.arr]:null, ri}))
.filter(x=>x.dep >= curMin)
.sort((a,b)=>a.dep-b.dep)
.slice(0,3)
.map(x=>{
const w = x.dep - curMin;
const arr = x.arr ? `<small>прибытие ${x.arr}</small>` : "";
return `<div class="trip ${w<15?'soon':''}">
<span class="t">${SCHEDULE[x.ri][d.dep]}${arr}</span>
<span class="w">${fmtWait(w)}</span>
</div>`;
}).join("") || `<div class="empty">на сегодня всё — первый завтра в ${SCHEDULE[0][d.dep]}</div>`;
return `<div class="card">
<h3>→ <span class="arr">${d.to}</span></h3>
<div class="via">${d.via}</div>
${upcoming}
</div>`;
}).join("");
}
function renderTable(){
document.getElementById("tableBody").innerHTML = SCHEDULE.map(row =>
`<div class="row"><div>${row[0]}</div><div>${row[1]}</div><div>${row[2]}</div><div>${row[3]}</div></div>`
).join("");
}
function renderDay(){
const dow = nowMs().getDay();
document.getElementById("dayBadge").innerHTML = `Сегодня <b>${DAYS[dow]}</b>`;
}
function tickClock(){
const d = nowMs();
const p = n=>String(n).padStart(2,"0");
document.getElementById("clock").innerHTML = `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())} <small>МСК</small>`;
}
document.querySelectorAll(".pick").forEach(btn=>{
btn.addEventListener("click",()=>{
document.querySelectorAll(".pick").forEach(b=>b.classList.remove("active"));
btn.classList.add("active");
currentStop = btn.dataset.stop;
const w = {Кандалакша:"в Кандалакше", "Полярные Зори":"в Полярных Зорях", Зашеек:"в Зашейке"};
document.getElementById("whereLabel").textContent =
`Вы ${w[currentStop]} — доступные направления:`;
render();
});
});
document.getElementById("fullBtn").addEventListener("click",()=>{
const t = document.getElementById("fullTable");
const b = document.getElementById("fullBtn");
const open = t.style.display === "block";
t.style.display = open ? "none" : "block";
b.classList.toggle("open", !open);
});
tickClock(); renderDay(); render(); renderTable();
setInterval(tickClock,1000);
setInterval(render,30000);
setInterval(renderDay,60000);
</script>
</body>
</html>