|
@@ -1,187 +0,0 @@
|
|
|
-<!doctype html>
|
|
|
|
|
-<html lang="zh-CN">
|
|
|
|
|
-<head>
|
|
|
|
|
- <meta charset="utf-8">
|
|
|
|
|
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
|
|
|
- <title>考试投屏时钟</title>
|
|
|
|
|
- <style>
|
|
|
|
|
- :root{
|
|
|
|
|
- --black:#050507;
|
|
|
|
|
- --white:#fff;
|
|
|
|
|
- --blue:#2997ff;
|
|
|
|
|
- --blue2:#0071e3;
|
|
|
|
|
- --text:rgba(255,255,255,.96);
|
|
|
|
|
- --sub:rgba(255,255,255,.68);
|
|
|
|
|
- --muted:rgba(255,255,255,.48);
|
|
|
|
|
- --glass:rgba(18,18,22,.56);
|
|
|
|
|
- --glass2:rgba(255,255,255,.08);
|
|
|
|
|
- --line:rgba(255,255,255,.14);
|
|
|
|
|
- --ok:#30d158;
|
|
|
|
|
- --warn:#ffd60a;
|
|
|
|
|
- --danger:#ff453a;
|
|
|
|
|
- }
|
|
|
|
|
- *{box-sizing:border-box}
|
|
|
|
|
- html,body{height:100%;margin:0}
|
|
|
|
|
- body{
|
|
|
|
|
- min-height:100%;
|
|
|
|
|
- overflow:hidden;
|
|
|
|
|
- color:var(--text);
|
|
|
|
|
- font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",Arial,sans-serif;
|
|
|
|
|
- background:#000;
|
|
|
|
|
- }
|
|
|
|
|
- body:before{
|
|
|
|
|
- content:"";
|
|
|
|
|
- position:fixed;inset:0;
|
|
|
|
|
- background:
|
|
|
|
|
- radial-gradient(circle at 50% -12%, rgba(41,151,255,.62), transparent 34%),
|
|
|
|
|
- radial-gradient(circle at 12% 30%, rgba(120,119,198,.30), transparent 30%),
|
|
|
|
|
- radial-gradient(circle at 88% 72%, rgba(48,209,88,.18), transparent 30%),
|
|
|
|
|
- linear-gradient(180deg,#08080c 0%,#000 58%,#08080b 100%);
|
|
|
|
|
- z-index:-3;
|
|
|
|
|
- }
|
|
|
|
|
- body:after{
|
|
|
|
|
- content:"";
|
|
|
|
|
- position:fixed;inset:0;
|
|
|
|
|
- background:
|
|
|
|
|
- linear-gradient(rgba(255,255,255,.035) 1px, transparent 1px),
|
|
|
|
|
- linear-gradient(90deg, rgba(255,255,255,.028) 1px, transparent 1px);
|
|
|
|
|
- background-size:64px 64px;
|
|
|
|
|
- mask-image:radial-gradient(circle at center, black 0%, transparent 72%);
|
|
|
|
|
- z-index:-2;
|
|
|
|
|
- pointer-events:none;
|
|
|
|
|
- }
|
|
|
|
|
- .orb{position:fixed;border-radius:999px;filter:blur(12px);opacity:.62;z-index:-1;animation:float 10s ease-in-out infinite alternate}
|
|
|
|
|
- .orb.one{width:20vw;height:20vw;left:7vw;top:12vh;background:rgba(41,151,255,.24)}
|
|
|
|
|
- .orb.two{width:16vw;height:16vw;right:10vw;bottom:12vh;background:rgba(48,209,88,.14);animation-delay:-3s}
|
|
|
|
|
- @keyframes float{to{transform:translate3d(2vw,2vh,0) scale(1.08)}}
|
|
|
|
|
-
|
|
|
|
|
- .screen{height:100%;display:grid;grid-template-rows:auto 1fr auto;padding:clamp(18px,3.2vw,48px);gap:clamp(14px,2vw,28px)}
|
|
|
|
|
- .nav,.dock{
|
|
|
|
|
- display:flex;align-items:center;justify-content:space-between;gap:18px;
|
|
|
|
|
- min-height:58px;padding:10px 14px;border:1px solid var(--line);border-radius:999px;
|
|
|
|
|
- background:var(--glass);backdrop-filter:saturate(180%) blur(26px);-webkit-backdrop-filter:saturate(180%) blur(26px);
|
|
|
|
|
- box-shadow:0 18px 60px rgba(0,0,0,.26), inset 0 1px 0 rgba(255,255,255,.10);
|
|
|
|
|
- }
|
|
|
|
|
- .brand{display:flex;align-items:center;gap:12px;min-width:0}
|
|
|
|
|
- .logo{width:38px;height:38px;border-radius:50%;display:grid;place-items:center;background:linear-gradient(145deg,#fff,#b8ddff);color:#06101f;font-weight:800;box-shadow:0 0 34px rgba(41,151,255,.42)}
|
|
|
|
|
- .brand-title{font-size:clamp(15px,1.4vw,22px);font-weight:650;letter-spacing:-.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
|
|
|
- .pill{display:inline-flex;align-items:center;gap:8px;border-radius:999px;padding:10px 16px;background:var(--glass2);border:1px solid var(--line);color:var(--sub);font-size:clamp(13px,1.15vw,18px);white-space:nowrap}
|
|
|
|
|
- .dot{width:8px;height:8px;border-radius:50%;background:var(--ok);box-shadow:0 0 20px var(--ok)}
|
|
|
|
|
-
|
|
|
|
|
- .hero{display:grid;place-items:center;text-align:center;min-height:0}
|
|
|
|
|
- .hero-inner{width:min(1500px,100%)}
|
|
|
|
|
- .eyebrow{margin:0 0 clamp(10px,1.4vw,22px);font-size:clamp(20px,2.25vw,40px);font-weight:520;color:var(--sub);letter-spacing:.16em;text-transform:uppercase}
|
|
|
|
|
- .clock-card{position:relative;margin:auto;padding:clamp(22px,4.8vw,72px) clamp(18px,5vw,82px);border-radius:clamp(28px,4vw,58px);background:linear-gradient(180deg,rgba(255,255,255,.14),rgba(255,255,255,.045));border:1px solid rgba(255,255,255,.18);box-shadow:0 34px 120px rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.18);backdrop-filter:blur(28px);overflow:hidden}
|
|
|
|
|
- .clock-card:before{content:"";position:absolute;inset:-2px;background:linear-gradient(115deg,transparent 0 20%,rgba(255,255,255,.20) 35%,transparent 52%);transform:translateX(-55%);animation:shine 8s ease-in-out infinite;pointer-events:none}
|
|
|
|
|
- @keyframes shine{55%,100%{transform:translateX(75%)}}
|
|
|
|
|
- .clock{position:relative;margin:0;font-family:"DIN Alternate","Arial Narrow","Helvetica Neue",Arial,"PingFang SC","Microsoft YaHei",sans-serif;font-variant-numeric:tabular-nums;font-size:clamp(88px,17.2vw,268px);font-weight:700;line-height:.92;letter-spacing:.018em;word-spacing:.03em;text-shadow:0 12px 44px rgba(0,0,0,.32)}
|
|
|
|
|
- .seconds{display:inline;color:inherit;font-size:1em;margin-left:0;letter-spacing:inherit;vertical-align:baseline;text-shadow:inherit}
|
|
|
|
|
- .date{margin:clamp(16px,2vw,30px) 0 0;color:var(--sub);font-size:clamp(22px,2.6vw,46px);font-weight:500;letter-spacing:.06em}
|
|
|
|
|
-
|
|
|
|
|
- .timer{display:none;width:min(1160px,96%);margin:clamp(22px,3vh,44px) auto 0;padding:clamp(18px,2.6vw,34px);border-radius:32px;background:rgba(0,0,0,.38);border:1px solid var(--line);box-shadow:inset 0 1px 0 rgba(255,255,255,.10)}
|
|
|
|
|
- .timer-top{display:grid;grid-template-columns:1fr auto;align-items:end;gap:18px;text-align:left}
|
|
|
|
|
- .timer-label{font-size:clamp(18px,1.8vw,32px);color:var(--sub);letter-spacing:.12em;font-weight:620}
|
|
|
|
|
- .remain{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",monospace;font-variant-numeric:tabular-nums;font-size:clamp(44px,7.2vw,116px);font-weight:850;line-height:.9;letter-spacing:-.06em}
|
|
|
|
|
- .progress{height:clamp(12px,1vw,18px);margin-top:22px;border-radius:999px;background:rgba(255,255,255,.12);overflow:hidden;box-shadow:inset 0 1px 4px rgba(0,0,0,.4)}
|
|
|
|
|
- .bar{height:100%;width:100%;border-radius:999px;background:linear-gradient(90deg,var(--ok),var(--blue));box-shadow:0 0 34px rgba(41,151,255,.42);transition:width .35s linear,background .3s ease}
|
|
|
|
|
-
|
|
|
|
|
- .dock{border-radius:28px;padding:14px 18px;color:var(--sub)}
|
|
|
|
|
- .help{font-size:clamp(13px,1.2vw,18px);line-height:1.65}.help b{color:#fff;font-weight:650}
|
|
|
|
|
- .actions{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end;align-items:center}
|
|
|
|
|
- button,a.btn{appearance:none;text-decoration:none;border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.10);color:#fff;border-radius:999px;padding:12px 18px;font-size:15px;line-height:1;cursor:pointer;transition:.18s ease;box-shadow:inset 0 1px 0 rgba(255,255,255,.12)}
|
|
|
|
|
- .title-input{width:190px;max-width:32vw;border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.10);color:#fff;border-radius:999px;padding:11px 16px;font-size:15px;line-height:1;outline:none;box-shadow:inset 0 1px 0 rgba(255,255,255,.12);transition:.18s ease}
|
|
|
|
|
- .title-input::placeholder{color:rgba(255,255,255,.58)}
|
|
|
|
|
- .title-input:focus{border-color:rgba(41,151,255,.75);background:rgba(255,255,255,.16);box-shadow:0 0 0 3px rgba(41,151,255,.16), inset 0 1px 0 rgba(255,255,255,.12)}
|
|
|
|
|
- button.primary,a.primary{background:var(--blue2);border-color:var(--blue2);box-shadow:0 12px 36px rgba(0,113,227,.30)}
|
|
|
|
|
- button:hover,a.btn:hover{transform:translateY(-1px);background:rgba(255,255,255,.18)}button.primary:hover,a.primary:hover{background:#147ce5}
|
|
|
|
|
-
|
|
|
|
|
- body.warn .bar{background:linear-gradient(90deg,var(--warn),#ff9f0a);box-shadow:0 0 34px rgba(255,214,10,.34)}
|
|
|
|
|
- body.danger .bar{background:linear-gradient(90deg,var(--danger),#ff9f0a);box-shadow:0 0 38px rgba(255,69,58,.46)}
|
|
|
|
|
- body.danger .remain,body.danger .seconds{color:var(--danger);text-shadow:0 0 36px rgba(255,69,58,.52)}
|
|
|
|
|
- body.done .remain{color:var(--danger);letter-spacing:-.02em}
|
|
|
|
|
-
|
|
|
|
|
- @media(max-width:760px){
|
|
|
|
|
- body{overflow:auto}.screen{min-height:100%;height:auto}.nav,.dock{border-radius:24px;align-items:flex-start}.pill.hide-sm{display:none}.timer-top{grid-template-columns:1fr;text-align:center}.timer-label{text-align:center}.dock{display:block;text-align:center}.actions{justify-content:center;margin-top:14px}.title-input{width:100%;max-width:280px}.clock-card{border-radius:30px}.clock{font-size:clamp(78px,22vw,160px)}
|
|
|
|
|
- }
|
|
|
|
|
- </style>
|
|
|
|
|
-</head>
|
|
|
|
|
-<body>
|
|
|
|
|
- <div class="orb one"></div><div class="orb two"></div>
|
|
|
|
|
- <div class="screen">
|
|
|
|
|
- <header class="nav">
|
|
|
|
|
- <div class="brand"><div class="logo">考</div><div class="brand-title" id="examTitle">考试投屏时钟</div></div>
|
|
|
|
|
- <div style="display:flex;gap:10px;align-items:center;min-width:0">
|
|
|
|
|
- <div class="pill hide-sm"><span class="dot"></span><span id="statusText">北京时间 · 实时同步</span></div>
|
|
|
|
|
- <div class="pill" id="ampm">--</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </header>
|
|
|
|
|
-
|
|
|
|
|
- <main class="hero">
|
|
|
|
|
- <div class="hero-inner">
|
|
|
|
|
- <p class="eyebrow" id="pageTitle">EXAM CLOCK</p>
|
|
|
|
|
- <section class="clock-card">
|
|
|
|
|
- <p class="clock"><span id="hhmm">--:--</span>:<span class="seconds" id="ss">--</span></p>
|
|
|
|
|
- <div class="date" id="dateText">----</div>
|
|
|
|
|
- </section>
|
|
|
|
|
- <section class="timer" id="timerPanel">
|
|
|
|
|
- <div class="timer-top">
|
|
|
|
|
- <div class="timer-label" id="timerLabel">距离考试结束</div>
|
|
|
|
|
- <div class="remain" id="remainText">--:--:--</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="progress"><div class="bar" id="bar"></div></div>
|
|
|
|
|
- </section>
|
|
|
|
|
- </div>
|
|
|
|
|
- </main>
|
|
|
|
|
-
|
|
|
|
|
- <footer class="dock">
|
|
|
|
|
- <div class="help" id="hintText">投屏建议点击 <b>全屏显示</b>;地址后加 <b>?duration=120</b> 可显示 120 分钟倒计时。</div>
|
|
|
|
|
- <div class="actions">
|
|
|
|
|
- <input class="title-input" id="titleInput" type="text" maxlength="30" placeholder="输入标题" autocomplete="off">
|
|
|
|
|
- <button type="button" onclick="applyTitle()">添加标题</button>
|
|
|
|
|
- <button class="primary" type="button" onclick="toggleFullScreen()">全屏显示</button>
|
|
|
|
|
- <a class="btn" href="?duration=120">120分钟倒计时</a>
|
|
|
|
|
- <button type="button" onclick="location.href=location.pathname">只显示时钟</button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </footer>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <script>
|
|
|
|
|
- const $=(id)=>document.getElementById(id);
|
|
|
|
|
- const params=new URLSearchParams(location.search);
|
|
|
|
|
- const title=params.get('title')||'考试投屏时钟';
|
|
|
|
|
- const label=params.get('label')||'距离考试结束';
|
|
|
|
|
- const durationMin=Number(params.get('duration')||0);
|
|
|
|
|
- const endParam=params.get('end');
|
|
|
|
|
- const startTs=Date.now();
|
|
|
|
|
- let endTs=null;
|
|
|
|
|
- $('examTitle').textContent=title; document.title=title; $('timerLabel').textContent=label;
|
|
|
|
|
- $('titleInput').value=params.get('title')||'';
|
|
|
|
|
- if(params.get('title')) $('pageTitle').textContent=title; else $('pageTitle').textContent='EXAM CLOCK';
|
|
|
|
|
- if(endParam){const parsed=new Date(endParam.replace(' ','T')).getTime(); if(!Number.isNaN(parsed)) endTs=parsed;}
|
|
|
|
|
- else if(durationMin>0){endTs=startTs+durationMin*60*1000;}
|
|
|
|
|
- if(endTs){$('timerPanel').style.display='block';$('hintText').innerHTML='倒计时已开启;可用参数:<b>?duration=分钟&title=标题</b>,或点击全屏投放。';}
|
|
|
|
|
- function pad(n){return String(n).padStart(2,'0')}
|
|
|
|
|
- function formatRemain(ms){const total=Math.max(0,Math.ceil(ms/1000));const h=Math.floor(total/3600),m=Math.floor((total%3600)/60),s=total%60;return `${pad(h)}:${pad(m)}:${pad(s)}`}
|
|
|
|
|
- function tick(){
|
|
|
|
|
- const now=new Date(),w=['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
|
|
|
|
|
- $('hhmm').textContent=`${pad(now.getHours())}:${pad(now.getMinutes())}`;$('ss').textContent=pad(now.getSeconds());
|
|
|
|
|
- $('dateText').textContent=`${now.getFullYear()}年${pad(now.getMonth()+1)}月${pad(now.getDate())}日 ${w[now.getDay()]}`;
|
|
|
|
|
- $('ampm').textContent=now.getHours()<12?'上午':'下午';
|
|
|
|
|
- if(endTs){const remain=endTs-now.getTime(),total=Math.max(1,endTs-startTs),pct=Math.max(0,Math.min(100,remain/total*100));
|
|
|
|
|
- $('remainText').textContent=remain<=0?'考试结束':formatRemain(remain);$('bar').style.width=pct+'%';
|
|
|
|
|
- document.body.classList.toggle('warn',pct<=25&&pct>10);document.body.classList.toggle('danger',pct<=10&&pct>0);document.body.classList.toggle('done',remain<=0);
|
|
|
|
|
- $('statusText').textContent=remain<=0?'考试已结束':'倒计时进行中';
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- function applyTitle(){
|
|
|
|
|
- const value=$('titleInput').value.trim();
|
|
|
|
|
- const url=new URL(location.href);
|
|
|
|
|
- if(value) url.searchParams.set('title',value); else url.searchParams.delete('title');
|
|
|
|
|
- location.href=url.pathname+url.search;
|
|
|
|
|
- }
|
|
|
|
|
- $('titleInput').addEventListener('keydown',function(e){if(e.key==='Enter')applyTitle();});
|
|
|
|
|
- function toggleFullScreen(){if(!document.fullscreenElement)document.documentElement.requestFullscreen?.();else document.exitFullscreen?.();}
|
|
|
|
|
- tick();setInterval(tick,250);
|
|
|
|
|
- </script>
|
|
|
|
|
-</body>
|
|
|
|
|
-</html>
|
|
|