5FNe.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Fursuit Pro V1.0</title>
<style>
:root { --bg: #f5f7fa; --card: #ffffff; --text: #333333; --accent: #81c784; --border: #e0e0e0; --btn-bg: #eef2f5; --danger: #ff9999; }
[data-theme="dark"] { --bg: #12171f; --card: #1e2733; --text: #e0e0e0; --accent: #a5d6a7; --border: #2d3b4a; --btn-bg: #263340; }
body { font-family: -apple-system, sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 12px; display: flex; flex-direction: column; align-items: center; transition: 0.3s; }
.container { width: 100%; max-width: 420px; }
.top-bar { display: flex; justify-content: space-between; margin-bottom: 12px; width: 100%; }
.mini-btn { padding: 6px 12px; border-radius: 10px; border: 1px solid var(--border); background: var(--card); color: var(--text); font-size: 13px; }
.mini-btn.active { background: var(--accent); color: #fff; }
.card { background: var(--card); border: 1px solid var(--border); padding: 16px; border-radius: 20px; margin-bottom: 12px; box-shadow: 0 3px 8px rgba(0,0,0,0.08); }
h2 { font-size: 1rem; margin: 0 0 12px 0; border-left: 4px solid var(--accent); padding-left: 10px; }
button { width: 100%; padding: 14px; border: none; border-radius: 14px; font-weight: bold; font-size: 1rem; cursor: pointer; background: var(--btn-bg); color: var(--text); margin-bottom: 8px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
input[type=range] { width: 100%; height: 8px; margin: 12px 0; accent-color: var(--accent); border-radius: 4px; }
.btn-lock { background: #fff0f0 !important; color: var(--danger) !important; border: 1px solid #ffcccc !important; }
.btn-unlock { background: var(--accent) !important; color: #fff !important; margin-top: 12px; }
#status { font-size: 13px; text-align: center; margin-bottom: 6px; font-weight: bold; }
.ir-list { font-size: 0.9rem; line-height: 1.9; padding-left: 10px; }
</style>
</head>
<body data-theme="dark">
<div class="container">
<div class="top-bar">
<div class="lang-group">
<button class="mini-btn active" onclick="setLang('zh')">中</button>
<button class="mini-btn" onclick="setLang('en')">EN</button>
<button class="mini-btn" onclick="setLang('jp')">日</button>
</div>
<div class="theme-group">
<button class="mini-btn" onclick="setTheme('light')">☀️</button>
<button class="mini-btn active" onclick="setTheme('dark')">🌙</button>
</div>
</div>
<div class="card" style="text-align:center;">
<h1 style="font-size:1.3rem; margin:0; color:var(--accent);">FUYI PRO V1.0</h1>
<div id="status" style="color:var(--danger);">OFFLINE</div>
<button id="btn-connect" onclick="connectBLE()" style="background:var(--accent); color:#fff; font-size:1.1rem;">🔌 连接设备</button>
</div>
<div id="panel" style="opacity: 0.3; pointer-events: none;">
<div class="card">
<h2 id="t-angle">实时角度 <span id="realAngle" style="font-weight:bold; color:var(--accent);">90°</span></h2>
<input type="range" min="40" max="90" value="90" oninput="document.getElementById('realAngle').innerText=this.value+'°'" onchange="send('ANG', this.value)">
<div class="grid-3">
<button onclick="send('BLINK', 1)" id="t-lfast">左快眨</button>
<button onclick="send('BLINK', 0)" id="t-dfast">双快眨</button>
<button onclick="send('BLINK', 2)" id="t-rfast">右快眨</button>
</div>
</div>
<div class="card">
<div style="display:flex; justify-content:space-between; align-items:center;">
<h2 id="t-auto" style="margin:0;">自动眨眼</h2>
<input type="checkbox" style="width:28px; height:28px; accent-color:var(--accent);" onchange="send('AUTO', this.checked?1:0)">
</div>
<input type="range" min="1000" max="10000" step="500" value="4000" onchange="send('AUTODUR', this.value)">
</div>
<div class="card">
<h2 id="t-locktitle">拍照定格锁定</h2>
<div class="grid-3">
<button class="btn-lock" onclick="send('CLOSE', 1)" id="t-lockl">仅锁左眼</button>
<button class="btn-lock" onclick="send('CLOSE', 0)" id="t-lockd">全闭锁定</button>
<button class="btn-lock" onclick="send('CLOSE', 2)" id="t-lockr">仅锁右眼</button>
</div>
<button class="btn-unlock" onclick="send('OPEN', 0)" id="t-unlock">解除锁定</button>
</div>
<div class="card">
<h2 id="t-slowtitle">慢动作眨眼 <span id="durVal" style="color:var(--accent);">0.5s</span></h2>
<input type="range" min="500" max="3000" step="100" value="500" oninput="document.getElementById('durVal').innerText=(this.value/1000).toFixed(1)+'s'" onchange="send('DUR', this.value)">
<div class="grid-3">
<button onclick="send('SLOW', 1)" id="t-lslow">左慢眨</button>
<button onclick="send('SLOW', 0)" id="t-dslow">双慢眨</button>
<button onclick="send('SLOW', 2)" id="t-rslow">右慢眨</button>
</div>
</div>
<!-- 遥控器教程(更新OK键说明) -->
<div class="card">
<h2 id="t-irtitle">遥控器按键指南</h2>
<div class="ir-list" id="t-irlist">
1键 → 左眼快速眨眼<br>
2键 → 双眼快速眨眼<br>
3键 → 右眼快速眨眼<br>
4键 → 左眼慢眨<br>
5键 → 双眼慢眨<br>
6键 → 右眼慢眨<br>
上键 → 双眼全闭锁定<br>
下键 → 解除所有锁定(快速睁眼)<br>
左键 → 仅锁左眼<br>
右键 → 仅锁右眼<br>
OK键 → 开启自动眨眼(默认4秒)<br>
连续按可循环切换间隔:2秒 → 4秒 → 6秒 → 8秒<br>
(关闭自动眨眼请用网页开关)
</div>
</div>
</div>
</div>
<script>
const S_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
const C_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
let bC;
const i18n = {
zh: {
angle: "实时角度", lfast: "左快眨", dfast: "双快眨", rfast: "右快眨",
auto: "自动眨眼", locktitle: "拍照定格锁定", lockl: "仅锁左眼", lockd: "全闭锁定", lockr: "仅锁右眼", unlock: "解除锁定",
slow: "慢动作眨眼", lslow: "左慢眨", dslow: "双慢眨", rslow: "右慢眨",
conn: "🔌 连接设备", irtitle: "遥控器按键指南",
irlist: "1键 → 左眼快速眨眼\n2键 → 双眼快速眨眼\n3键 → 右眼快速眨眼\n4键 → 左眼慢眨\n5键 → 双眼慢眨\n6键 → 右眼慢眨\n上键 → 双眼全闭锁定\n下键 → 解除所有锁定(快速睁眼)\n左键 → 仅锁左眼\n右键 → 仅锁右眼\nOK键 → 开启自动眨眼(默认4秒)\n 连续按可循环切换间隔:2秒 → 4秒 → 6秒 → 8秒\n (关闭自动眨眼请用网页开关)"
},
en: {
angle: "Real-time Angle", lfast: "Left Fast", dfast: "Both Fast", rfast: "Right Fast",
auto: "Auto Blink", locktitle: "Pose Lock", lockl: "Lock Left", lockd: "Lock Both", lockr: "Lock Right", unlock: "Release",
slow: "Slow Motion", lslow: "Left Slow", dslow: "Both Slow", rslow: "Right Slow",
conn: "🔌 CONNECT", irtitle: "Remote Guide",
irlist: "1 key → Left Fast Blink\n2 key → Both Fast Blink\n3 key → Right Fast Blink\n4 key → Left Slow\n5 key → Both Slow\n6 key → Right Slow\nUp key → Lock Both Closed\nDown key → Release Lock\nLeft key → Lock Left Only\nRight key → Lock Right Only\nOK key → Turn On Auto Blink (default 4s)\n Press repeatedly to cycle interval: 2s → 4s → 6s → 8s\n (Turn off only via web switch)"
},
jp: {
angle: "リアルタイム角度", lfast: "左速眨", dfast: "両速眨", rfast: "右速眨",
auto: "自動眨眼", locktitle: "ポーズ固定", lockl: "左のみ固定", lockd: "全閉固定", lockr: "右のみ固定", unlock: "解除",
slow: "スローモーション", lslow: "左スロー", dslow: "両スロー", rslow: "右スロー",
conn: "🔌 接続", irtitle: "リモコンガイド",
irlist: "1キー → 左速まばたき\n2キー → 両目速まばたき\n3キー → 右速まばたき\n4キー → 左スロー\n5キー → 両目スロー\n6キー → 右スロー\n上キー → 両目全閉\n下キー → 全解除\n左キー → 左のみ固定\n右キー → 右のみ固定\nOKキー → 自動眨眼をオン(デフォルト4秒)\n 連続押して間隔切替:2秒 → 4秒 → 6秒 → 8秒\n (オフはウェブスイッチのみ)"
}
};
function setLang(l) {
const d = i18n[l];
document.getElementById('t-angle').childNodes[0].textContent = d.angle + " ";
document.getElementById('t-lfast').innerText = d.lfast;
document.getElementById('t-dfast').innerText = d.dfast;
document.getElementById('t-rfast').innerText = d.rfast;
document.getElementById('t-auto').innerText = d.auto;
document.getElementById('t-locktitle').innerText = d.locktitle;
document.getElementById('t-lockl').innerText = d.lockl;
document.getElementById('t-lockd').innerText = d.lockd;
document.getElementById('t-lockr').innerText = d.lockr;
document.getElementById('t-unlock').innerText = d.unlock;
document.getElementById('t-slowtitle').childNodes[0].textContent = d.slow + " ";
document.getElementById('t-lslow').innerText = d.lslow;
document.getElementById('t-dslow').innerText = d.dslow;
document.getElementById('t-rslow').innerText = d.rslow;
document.getElementById('btn-connect').innerText = d.conn;
document.getElementById('t-irtitle').innerText = d.irtitle;
document.getElementById('t-irlist').innerHTML = d.irlist.replace(/\n/g, '<br>').replace(/ /g, ' ');
}
function setTheme(t) { document.body.setAttribute('data-theme', t); }
async function connectBLE() {
try {
const d = await navigator.bluetooth.requestDevice({ filters: [{ name: 'Fursuit-Pro-V1.0' }], optionalServices: [S_UUID] });
const s = await d.gatt.connect();
const sv = await s.getPrimaryService(S_UUID);
bC = await sv.getCharacteristic(C_UUID);
document.getElementById('status').innerText = "ONLINE";
document.getElementById('status').style.color = "#a5d6a7";
document.getElementById('panel').style.opacity = "1";
document.getElementById('panel').style.pointerEvents = "auto";
} catch (e) { alert("连接失败"); }
}
function send(c, v) {
if (bC) bC.writeValue(new TextEncoder().encode(c + ":" + v));
if (navigator.vibrate) navigator.vibrate(30);
}
</script>
</body>
</html>
Download
pasted.sh