Files
Z User e45b68bb8b feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library:
- Connects to IMAP server (IMAPFlow, TLS on port 993)
- Fetches recent messages (configurable limit, default 20)
- Extracts: from, fromEmail, subject, date, snippet (200 chars)
- Thread-to-contact matching (PRD §6.1):
  1. Match sender email against persons table
  2. If no person match, extract domain and match against company.website
  3. Returns matchedCompany + matchedPerson for each email
- Audit-logged (action: crm.mailsync:imap)
- Zod validated (host, port, user, password, folder, limit)
- 502 on IMAP connection failure with detail
- 400 on validation failure

Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
2026-10-27 22:50:50 +00:00

744 lines
23 KiB
HTML
Executable File
Raw Permalink 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="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Showform — Viewfinder</title>
<style>
:root {
--hud: #ffffff;
--hud-dim: rgba(255,255,255,0.45);
--hud-line: rgba(255,255,255,0.2);
--rec: #ff3b30;
--mono: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; background: #000; overflow: hidden; }
body {
font-family: var(--mono);
color: var(--hud);
-webkit-font-smoothing: antialiased;
user-select: none;
}
.viewfinder {
position: relative;
width: 100vw;
height: 100vh;
background: #000;
overflow: hidden;
}
/* ---------- 视频舞台 ---------- */
.stage {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.video-wrap {
width: 100%;
height: 100%;
position: relative;
transition: transform 0.6s cubic-bezier(.2,.8,.2,1),
-webkit-mask-image 0.6s ease,
mask-image 0.6s ease;
}
video {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: filter 0.5s ease;
background: #0a0a0a;
}
/* ---------- 滤镜 ---------- */
.f-original { filter: contrast(1.02) brightness(1.0); }
.f-vivid { filter: saturate(1.6) contrast(1.15) brightness(1.05); }
.f-vintage { filter: sepia(0.55) contrast(0.92) brightness(0.96) saturate(0.7) hue-rotate(-10deg); }
.f-fuji { filter: contrast(1.1) saturate(1.2) brightness(1.05) hue-rotate(-5deg); }
.f-mono { filter: grayscale(1) contrast(1.2) brightness(0.98); }
.f-cold { filter: hue-rotate(180deg) saturate(1.3) brightness(0.95); }
.f-cinema { filter: contrast(1.2) saturate(0.8) brightness(0.9); }
.fisheye .video-wrap {
-webkit-mask-image: radial-gradient(circle at center, #000 56%, transparent 72%);
mask-image: radial-gradient(circle at center, #000 56%, transparent 72%);
transform: scale(1.06);
}
.fisheye video { filter: contrast(1.1) saturate(1.15); transform: scale(1.15); }
.flash { animation: flash 0.32s ease; }
@keyframes flash { 0% { opacity: 1; } 35% { opacity: 0.35; } 100% { opacity: 1; } }
.grain {
position: absolute; inset: 0;
pointer-events: none;
z-index: 4;
background: repeating-linear-gradient(0deg, rgba(255,255,255,0.012) 0 1px, transparent 1px 3px);
mix-blend-mode: overlay;
opacity: 0.7;
}
/* ---------- HUD ---------- */
.hud {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 5;
color: var(--hud);
text-shadow: 0 0 6px rgba(0,0,0,0.6);
}
.hud-pad { position: absolute; inset: 28px 32px; }
.frame-corner {
position: absolute;
width: 56px;
height: 56px;
border: 1.5px solid var(--hud);
}
.fc-tl { top: 0; left: 0; border-right: none; border-bottom: none; }
.fc-tr { top: 0; right: 0; border-left: none; border-bottom: none; }
.fc-bl { bottom: 0; left: 0; border-right: none; border-top: none; }
.fc-br { bottom: 0; right: 0; border-left: none; border-top: none; }
.top-row {
position: absolute;
top: 6px; left: 80px; right: 80px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
letter-spacing: 0.04em;
}
.top-left, .top-right { display: flex; align-items: center; gap: 14px; }
.badge {
display: inline-flex; align-items: center; justify-content: center;
padding: 1px 8px;
border: 1.5px solid var(--hud);
font-size: 12px;
letter-spacing: 0.06em;
min-width: 28px; height: 20px;
border-radius: 2px;
}
.badge-a { width: 28px; height: 24px; font-weight: 600; font-size: 15px; }
.rec-btn {
display: inline-flex;
align-items: center;
gap: 6px;
background: transparent;
border: 1.5px solid var(--hud);
color: var(--hud);
padding: 2px 9px 2px 7px;
font-family: var(--mono);
font-size: 13px;
letter-spacing: 0.06em;
cursor: pointer;
border-radius: 3px;
pointer-events: auto;
height: 22px;
transition: background 0.2s ease;
}
.rec-btn:hover { background: rgba(255,255,255,0.12); }
.rec-btn:active { transform: scale(0.96); }
.rec-dot {
width: 10px; height: 10px;
background: var(--rec);
border-radius: 50%;
box-shadow: 0 0 8px rgba(255,59,48,0.7);
animation: blink 1.1s steps(2, end) infinite;
}
.rec-btn.paused .rec-dot {
background: transparent;
box-shadow: none;
animation: none;
width: 8px; height: 10px;
border-left: 2px solid var(--hud);
border-right: 2px solid var(--hud);
border-radius: 0;
}
@keyframes blink { 50% { opacity: 0.15; } }
.battery {
position: relative;
width: 34px; height: 16px;
border: 1.5px solid var(--hud);
padding: 1.5px;
border-radius: 1px;
}
.battery::after {
content: '';
position: absolute;
right: -4px; top: 4px;
width: 2px; height: 6px;
background: var(--hud);
}
.battery span { display: block; height: 100%; background: var(--hud); width: 100%; }
.side-col {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 22px;
font-size: 12px;
letter-spacing: 0.04em;
align-items: center;
}
.side-left { left: -4px; }
.side-right { right: -4px; align-items: flex-end; }
.icon-box { display: flex; flex-direction: column; align-items: center; gap: 4px; line-height: 1; }
.icon {
width: 22px; height: 22px;
border: 1.5px solid var(--hud);
border-radius: 2px;
display: flex; align-items: center; justify-content: center;
}
.icon svg { width: 14px; height: 14px; stroke: var(--hud); fill: none; stroke-width: 1.5; }
.bottom-row {
position: absolute;
bottom: 6px; left: 80px; right: 80px;
display: flex;
justify-content: space-between;
align-items: flex-end;
font-size: 14px;
letter-spacing: 0.04em;
}
.bottom-left, .bottom-right { display: flex; align-items: center; gap: 16px; }
.timecode {
font-size: 18px;
letter-spacing: 0.12em;
font-variant-numeric: tabular-nums;
}
.micro {
font-size: 11px;
color: var(--hud-dim);
margin-top: 4px;
letter-spacing: 0.15em;
}
.stack { display: flex; flex-direction: column; align-items: center; gap: 2px; }
/* 中央对焦框 */
.reticle {
position: absolute;
top: 50%; left: 50%;
width: 78px; height: 78px;
transform: translate(-50%, -50%);
}
.reticle .c { position: absolute; width: 16px; height: 16px; border: 1.5px solid var(--hud); }
.reticle .c1 { top: 0; left: 0; border-right: none; border-bottom: none; }
.reticle .c2 { top: 0; right: 0; border-left: none; border-bottom: none; }
.reticle .c3 { bottom: 0; left: 0; border-right: none; border-top: none; }
.reticle .c4 { bottom: 0; right: 0; border-left: none; border-top: none; }
.reticle .dot {
position: absolute;
top: 50%; left: 50%;
width: 4px; height: 4px;
background: var(--hud);
border-radius: 50%;
transform: translate(-50%, -50%);
}
.reticle.focus { animation: focus 0.5s ease; }
@keyframes focus {
0% { transform: translate(-50%, -50%) scale(1.3); opacity: 0.4; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
/* ---------- 滤镜纵向选择器(底部中央) ---------- */
.filter-wheel {
position: absolute;
left: 50%;
bottom: 88px;
transform: translateX(-50%);
width: 280px;
height: 168px; /* 显示 5 项,每项 ~36px */
z-index: 7;
pointer-events: auto;
overflow: hidden;
-webkit-mask-image: linear-gradient(to bottom,
transparent 0%,
#000 25%,
#000 75%,
transparent 100%);
mask-image: linear-gradient(to bottom,
transparent 0%,
#000 25%,
#000 75%,
transparent 100%);
}
.filter-list {
position: absolute;
left: 0; right: 0;
top: 50%;
transition: transform 0.4s cubic-bezier(.22,.85,.25,1.05);
will-change: transform;
}
.filter-item {
height: 36px;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
color: var(--hud-dim);
cursor: pointer;
font-family: var(--mono);
font-size: 14px;
letter-spacing: 0.04em;
white-space: nowrap;
opacity: 0.45;
transition: color 0.3s ease, opacity 0.3s ease, transform 0.3s ease;
}
.filter-item:hover { opacity: 0.75; }
.filter-item.active {
color: var(--hud);
opacity: 1;
transform: scale(1.08);
}
.chip {
border: 1.5px solid currentColor;
padding: 2px 8px;
border-radius: 4px;
line-height: 1.1;
font-size: 13px;
min-width: 32px;
text-align: center;
}
.chip.name { padding: 2px 12px; }
.filter-item.active .chip.num2 { background: var(--hud); color: #000; border-color: var(--hud); }
/* 中线 — 不跨视频,只局限在 wheel 内 */
.wheel-indicator {
position: absolute;
left: 50%;
bottom: calc(88px + 84px - 18px); /* 居中线对齐 active 项 */
transform: translateX(-50%);
width: 240px;
height: 36px;
z-index: 6;
pointer-events: none;
border-top: 1px solid rgba(255,255,255,0.18);
border-bottom: 1px solid rgba(255,255,255,0.18);
}
/* 两侧小箭头提示 */
.wheel-indicator::before,
.wheel-indicator::after {
content: '';
position: absolute;
top: 50%;
width: 8px; height: 8px;
border: 1.5px solid var(--hud);
transform: translateY(-50%) rotate(45deg);
}
.wheel-indicator::before { left: -16px; border-right: none; border-bottom: none; }
.wheel-indicator::after { right: -16px; border-left: none; border-top: none; }
/* 操作提示(自动消失) */
.help {
position: absolute;
left: 50%;
bottom: 270px;
transform: translateX(-50%);
pointer-events: none;
z-index: 50;
text-align: center;
font-size: 11px;
letter-spacing: 0.22em;
color: var(--hud-dim);
background: rgba(0,0,0,0.4);
backdrop-filter: blur(6px);
padding: 8px 16px;
border: 1px solid rgba(255,255,255,0.2);
border-radius: 3px;
opacity: 0;
transition: opacity 0.4s ease;
white-space: nowrap;
}
.help.show { opacity: 1; }
.help kbd {
display: inline-block;
padding: 1px 6px;
border: 1px solid var(--hud-dim);
border-radius: 2px;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0;
color: var(--hud);
margin: 0 2px;
}
/* 加载中 */
.loading {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
z-index: 8;
text-align: center;
color: var(--hud);
font-size: 12px;
letter-spacing: 0.3em;
pointer-events: none;
}
.loading.hidden { display: none; }
.loading .spinner {
width: 32px; height: 32px;
border: 1.5px solid var(--hud-dim);
border-top-color: var(--hud);
border-radius: 50%;
margin: 0 auto 12px;
animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.fallback-bg {
position: absolute;
inset: 0;
z-index: 0;
background: radial-gradient(ellipse at 30% 40%, #1a1a1a 0%, #050505 70%), #000;
display: none;
}
.fallback-bg.show { display: block; }
@media (max-width: 700px) {
.top-row, .bottom-row { left: 60px; right: 60px; font-size: 12px; }
.badge { font-size: 11px; padding: 1px 6px; }
.timecode { font-size: 15px; }
.frame-corner { width: 38px; height: 38px; }
.filter-wheel { width: 240px; bottom: 78px; }
.wheel-indicator { width: 200px; bottom: calc(78px + 84px - 18px); }
}
</style>
</head>
<body>
<div class="viewfinder" id="viewfinder">
<div class="fallback-bg" id="fallback"></div>
<div class="stage" id="stage">
<div class="video-wrap" id="videoWrap">
<video id="vid" class="f-original"
autoplay muted loop playsinline preload="auto"
crossorigin="anonymous"></video>
</div>
<div class="grain"></div>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
LOADING
</div>
<div class="hud">
<div class="hud-pad">
<div class="frame-corner fc-tl"></div>
<div class="frame-corner fc-tr"></div>
<div class="frame-corner fc-bl"></div>
<div class="frame-corner fc-br"></div>
<div class="top-row">
<div class="top-left">
<span class="badge badge-a">A</span>
<span class="badge">16:9</span>
<span>25p</span>
</div>
<div class="top-right">
<span class="badge">FX</span>
<button class="rec-btn" id="recBtn" type="button" aria-label="play/pause">
<span class="rec-dot" id="recDot"></span>
<span id="recLabel">REC</span>
</button>
<span id="batPct">100%</span>
<div class="battery"><span id="batFill"></span></div>
</div>
</div>
<div class="side-col side-left">
<span class="badge">AF-S</span>
<div class="icon"><svg viewBox="0 0 20 20"><rect x="3" y="3" width="14" height="14" rx="1"/><circle cx="10" cy="10" r="3"/></svg></div>
<div class="icon-box">
<div class="icon"><svg viewBox="0 0 20 20"><circle cx="10" cy="7" r="3"/><path d="M4 17c0-3 3-5 6-5s6 2 6 5"/></svg></div>
<span>OFF</span>
</div>
<div class="icon-box">
<div class="icon"><svg viewBox="0 0 20 20"><rect x="3" y="3" width="14" height="14" rx="1"/><path d="M5 15l10-10"/></svg></div>
<span>OFF</span>
</div>
</div>
<div class="side-col side-right">
<div class="icon-box">
<span>AUTO</span>
<div class="icon"><svg viewBox="0 0 20 20"><circle cx="10" cy="10" r="2.5"/></svg></div>
</div>
<div class="icon-box"><span>AWB</span></div>
</div>
<div class="reticle" id="reticle">
<div class="c c1"></div>
<div class="c c2"></div>
<div class="c c3"></div>
<div class="c c4"></div>
<div class="dot"></div>
</div>
<div class="bottom-row">
<div class="bottom-left">
<div class="stack"><span id="fStop">F3.5</span><span class="micro">APERTURE</span></div>
<div class="stack"><span id="shutter">1/200</span><span class="micro">SHUTTER</span></div>
</div>
<div class="stack">
<span class="timecode" id="timecode">00:00:00:00</span>
<span class="micro" id="filterLabel">ORIGINAL · 4K</span>
</div>
<div class="bottom-right">
<div class="stack"><span id="iso">ISO 100</span><span class="micro">ISO</span></div>
<div class="stack"><span id="ev">± -0.5</span><span class="micro">EV</span></div>
</div>
</div>
</div>
</div>
<!-- 滤镜中线高亮 -->
<div class="wheel-indicator"></div>
<!-- 纵向滚动滤镜选择器 -->
<div class="filter-wheel" id="wheel">
<div class="filter-list" id="list"></div>
</div>
<!-- 操作提示 -->
<div class="help" id="help">
滚轮 / <kbd>↑</kbd><kbd>↓</kbd> 切换滤镜
</div>
</div>
<script>
const videoSources = [
'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4',
'https://www.w3schools.com/html/mov_bbb.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
'https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_1MB.mp4'
];
const filters = [
{ id: 'original', n1: '01', n2: '00', name: 'Original', iso: '100', fStop: 'F3.5', shutter: '1/200', ev: '± -0.5', label: 'ORIGINAL · 4K' },
{ id: 'vivid', n1: '02', n2: '15', name: 'Vivid', iso: '125', fStop: 'F2.8', shutter: '1/250', ev: '± +0.3', label: 'VIVID · POP' },
{ id: 'fuji', n1: '03', n2: '55', name: 'Fujifilm', iso: '200', fStop: 'F4.0', shutter: '1/160', ev: '± +0.0', label: 'CLASSIC CHROME' },
{ id: 'vintage', n1: '05', n2: '00', name: 'Vintage', iso: '400', fStop: 'F5.6', shutter: '1/125', ev: '± -0.7', label: 'VINTAGE 1977' },
{ id: 'fisheye', n1: '07', n2: '01', name: 'Fisheye', iso: '160', fStop: 'F2.2', shutter: '1/320', ev: '± +0.0', label: 'FISHEYE · 8MM' },
{ id: 'cinema', n1: '09', n2: '08', name: 'Cinema', iso: '320', fStop: 'F2.0', shutter: '1/100', ev: '± -1.0', label: 'CINEMA · 2.35:1' },
{ id: 'mono', n1: '11', n2: '21', name: 'Mono', iso: '800', fStop: 'F8.0', shutter: '1/500', ev: '± -0.3', label: 'MONO · TRI-X' },
{ id: 'cold', n1: '13', n2: '44', name: 'Cold', iso: '250', fStop: 'F3.2', shutter: '1/200', ev: '± -0.2', label: 'COLD · ARCTIC' },
];
const ITEM_H = 36;
const $ = id => document.getElementById(id);
const list = $('list');
const vid = $('vid');
const stage = $('stage');
const videoWrap = $('videoWrap');
const reticle = $('reticle');
const loading = $('loading');
const fallback = $('fallback');
let currentIdx = 0;
/* ---------- 视频加载 ---------- */
let srcIndex = 0;
function tryNextSource() {
if (srcIndex >= videoSources.length) {
loading.classList.add('hidden');
fallback.classList.add('show');
return;
}
vid.src = videoSources[srcIndex];
vid.load();
srcIndex++;
}
vid.addEventListener('canplay', () => {
loading.classList.add('hidden');
fallback.classList.remove('show');
vid.play().catch(() => {});
});
vid.addEventListener('error', tryNextSource);
let loadTimer;
function resetLoadTimer() {
clearTimeout(loadTimer);
loadTimer = setTimeout(() => {
if (vid.readyState < 3) { tryNextSource(); resetLoadTimer(); }
}, 10000);
}
resetLoadTimer();
tryNextSource();
/* ---------- 渲染滤镜列表 ---------- */
filters.forEach((f, i) => {
const el = document.createElement('div');
el.className = 'filter-item' + (i === currentIdx ? ' active' : '');
el.dataset.idx = i;
el.innerHTML = `
<span class="chip num1">${f.n1}</span>
<span class="chip num2">${f.n2}</span>
<span class="chip name">${f.name}</span>
`;
el.addEventListener('click', () => setFilter(i));
list.appendChild(el);
});
function setFilter(idx) {
if (idx < 0 || idx >= filters.length) return;
currentIdx = idx;
const f = filters[idx];
vid.className = 'f-' + f.id;
videoWrap.classList.toggle('fisheye', f.id === 'fisheye');
stage.classList.remove('flash');
void stage.offsetWidth;
stage.classList.add('flash');
[...list.children].forEach((el, i) => {
el.classList.toggle('active', i === idx);
});
/* 纵向居中:把 active 项移到中线(top: 50%) */
/* active 元素的 top(相对 list) = idx * ITEM_H
list 的 top = 50%(中线),要让 active 的中心也对齐中线
=> translateY = -(idx * ITEM_H) - ITEM_H/2 */
const offset = -(idx * ITEM_H) - ITEM_H / 2;
list.style.transform = `translateY(${offset}px)`;
$('iso').textContent = 'ISO ' + f.iso;
$('fStop').textContent = f.fStop;
$('shutter').textContent = f.shutter;
$('ev').textContent = f.ev;
$('filterLabel').textContent = f.label;
reticle.classList.remove('focus');
void reticle.offsetWidth;
reticle.classList.add('focus');
}
/* 键盘:上下切换 */
document.addEventListener('keydown', e => {
if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') { e.preventDefault(); setFilter((currentIdx - 1 + filters.length) % filters.length); }
if (e.key === 'ArrowDown' || e.key === 'ArrowRight') { e.preventDefault(); setFilter((currentIdx + 1) % filters.length); }
if (e.code === 'Space') { e.preventDefault(); vid.paused ? vid.play() : vid.pause(); }
if (e.key === 'f' || e.key === 'F') {
if (!document.fullscreenElement) document.documentElement.requestFullscreen?.();
else document.exitFullscreen?.();
}
});
/* 滚轮:在 wheel 区域内或整个 viewfinder 都能滚 */
let wheelLock = false;
function onWheel(e) {
e.preventDefault();
if (wheelLock) return;
wheelLock = true;
setTimeout(() => wheelLock = false, 200);
const dir = (e.deltaY > 0 || e.deltaX > 0) ? 1 : -1;
setFilter((currentIdx + dir + filters.length) % filters.length);
}
$('wheel').addEventListener('wheel', onWheel, { passive: false });
/* 整个 viewfinder 区域也可滚轮切换(更直观) */
$('viewfinder').addEventListener('wheel', onWheel, { passive: false });
/* 触摸滑动 */
let touchStartY = null;
$('wheel').addEventListener('touchstart', e => {
touchStartY = e.touches[0].clientY;
}, { passive: true });
$('wheel').addEventListener('touchmove', e => {
if (touchStartY === null) return;
const dy = e.touches[0].clientY - touchStartY;
if (Math.abs(dy) > 30) {
const dir = dy < 0 ? 1 : -1;
setFilter((currentIdx + dir + filters.length) % filters.length);
touchStartY = null;
}
}, { passive: true });
/* 点击对焦(避开底部 HUD 与滤镜轮) */
stage.addEventListener('click', e => {
if (e.clientY > window.innerHeight - 280) return;
const rect = stage.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
reticle.style.transition = 'none';
reticle.style.top = y + 'px';
reticle.style.left = x + 'px';
reticle.classList.remove('focus');
void reticle.offsetWidth;
reticle.style.transition = '';
reticle.classList.add('focus');
setTimeout(() => {
reticle.style.top = '50%';
reticle.style.left = '50%';
}, 1500);
});
/* 时间码 */
let startTs = null;
function tickTimecode(ts) {
if (!startTs) startTs = ts;
const elapsed = (ts - startTs) / 1000;
const totalFrames = Math.floor(elapsed * 25);
const ff = totalFrames % 25;
const ss = Math.floor(totalFrames / 25) % 60;
const mm = Math.floor(totalFrames / 1500) % 60;
const hh = Math.floor(totalFrames / 90000);
$('timecode').textContent =
String(hh).padStart(2,'0') + ':' +
String(mm).padStart(2,'0') + ':' +
String(ss).padStart(2,'0') + ':' +
String(ff).padStart(2,'0');
requestAnimationFrame(tickTimecode);
}
requestAnimationFrame(tickTimecode);
/* 电量 */
let battery = 100;
setInterval(() => {
battery = Math.max(12, battery - 1);
$('batPct').textContent = battery + '%';
$('batFill').style.width = battery + '%';
if (battery < 25) $('batFill').style.background = '#ff9f0a';
if (battery < 15) $('batFill').style.background = '#ff3b30';
}, 8000);
/* 操作提示 */
const help = $('help');
setTimeout(() => help.classList.add('show'), 800);
setTimeout(() => help.classList.remove('show'), 4200);
/* 播放/暂停按钮 */
const recBtn = $('recBtn');
const recLabel = $('recLabel');
function updateRec() {
if (vid.paused) {
recBtn.classList.add('paused');
recLabel.textContent = 'PAUSED';
} else {
recBtn.classList.remove('paused');
recLabel.textContent = 'REC';
}
}
recBtn.addEventListener('click', e => {
e.stopPropagation();
if (vid.paused) vid.play(); else vid.pause();
});
vid.addEventListener('play', updateRec);
vid.addEventListener('pause', updateRec);
/* 初次定位 */
setTimeout(() => setFilter(0), 50);
</script>
</body>
</html>