Files
DesktopPet/_2233live2d-main/index.html
2026-07-15 21:24:12 +08:00

322 lines
10 KiB
HTML
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 http-equiv="Content-Type" content="text/html" charset="utf-8">
<link rel="shortcut icon" href="favicon.ico">
<title>哔哩哔哩2233娘 Live2D 预览</title>
<meta name="description" content="哔哩哔哩客户端中的所有2233官方liv2d模型" />
<meta name="keywords" content="哔哩哔哩,bilibili,2233,live2d" />
<style>
/* ── CSS 变量 ── */
:root,
[data-theme="dark"] {
--bg-body: #18191c;
--bg-panel: #1a1d22;
--bg-btn: #23262d;
--border-main: #2a2d33;
--border-btn: #3a3d44;
--color-label: #888;
--color-btn: #ccc;
--color-title: #e0e0e0;
--color-sub: #666;
--bg-canvas: #0d0d0d;
}
[data-theme="light"] {
--bg-body: #f4f5f7;
--bg-panel: #ffffff;
--bg-btn: #f0f1f3;
--border-main: #dde0e6;
--border-btn: #c8ccd4;
--color-label: #999;
--color-btn: #444;
--color-title: #222;
--color-sub: #aaa;
--bg-canvas: #e8e8ec;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: var(--bg-body);
font-family: "Microsoft YaHei", sans-serif;
min-height: 100vh;
display: flex;
flex-direction: column;
transition: background 0.2s;
}
h1 {
padding: 18px 24px 14px;
font-size: 18px;
color: var(--color-title);
background: var(--bg-panel);
border-bottom: 1px solid var(--border-main);
letter-spacing: 1px;
}
h1 span {
font-size: 12px;
color: var(--color-sub);
margin-left: 10px;
font-weight: normal;
}
/* ── 主布局 ── */
.main {
display: flex;
flex: 1;
overflow: hidden;
}
/* ── 左侧:预览区 ── */
.preview {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: var(--bg-canvas);
border-right: 1px solid var(--border-main);
position: relative;
min-height: 480px;
transition: background 0.2s;
}
.preview-label {
position: absolute;
top: 14px;
left: 50%;
transform: translateX(-50%);
font-size: 13px;
color: var(--color-sub);
white-space: nowrap;
pointer-events: none;
}
canvas#live2d {
display: block;
/* 实际尺寸由 JS 控制,这里只做视觉居中 */
}
/* ── 右侧:控制区 ── */
.controls {
width: 400px;
flex-shrink: 0;
background: var(--bg-panel);
overflow-y: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 6px;
transition: background 0.2s;
}
.controls .label {
font-size: 11px;
color: var(--color-label);
margin-top: 8px;
margin-bottom: 2px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.controls .label:first-child {
margin-top: 0;
}
/* 角色切换 */
.char-row {
display: flex;
gap: 8px;
margin-bottom: 4px;
}
.char-btn {
flex: 1;
padding: 8px 0;
border: 1px solid var(--border-btn);
border-radius: 4px;
background: var(--bg-btn);
color: var(--color-btn);
font-size: 14px;
cursor: pointer;
transition: all 0.15s;
outline: none;
}
.char-btn:hover {
border-color: #00a1d6;
color: #00a1d6;
}
.char-btn.active {
background: #00a1d6;
border-color: #00a1d6;
color: #fff;
}
.btn {
width: 100%;
padding: 7px 14px;
border: 1px solid var(--border-btn);
border-radius: 4px;
background: var(--bg-btn);
color: var(--color-btn);
font-size: 13px;
cursor: pointer;
transition: all 0.15s;
outline: none;
text-align: left;
}
.btn:hover {
border-color: #00a1d6;
color: #00a1d6;
}
.btn.active {
background: #00a1d6;
border-color: #00a1d6;
color: #fff;
}
/* ── 右下角主题切换 ── */
.theme-toggle {
position: fixed;
right: 20px;
top: 10px;
padding: 7px 14px;
border: 1px solid var(--border-btn);
border-radius: 20px;
background: var(--bg-btn);
color: var(--color-btn);
font-size: 13px;
cursor: pointer;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
transition: all 0.15s;
z-index: 9999;
white-space: nowrap;
}
.theme-toggle:hover {
border-color: #00a1d6;
color: #00a1d6;
}
</style>
</head>
<body>
<h1>2233娘 Live2D 预览</h1>
<div class="main">
<!--canvas -->
<div class="preview">
<div class="preview-label" id="previewLabel">加载中...</div>
<canvas id="live2d" width="512" height="512"></canvas>
</div>
<!-- 右:控制 -->
<div class="controls" id="controls">
<span class="label">角色</span>
<div class="char-row">
<button class="char-btn active" data-char="22">22娘</button>
<button class="char-btn" data-char="33">33娘</button>
</div>
<span class="label">服装</span>
<!-- 模型按钮由 JS 渲染 -->
</div>
</div>
<button class="theme-toggle" id="themeToggle"></button>
<!-- 引入Live2D_JS -->
<script type="text/javascript" src="res/js/live2d.js"></script>
<script>
// ── 模型列表 ──────────────────────────────────────────────────────────────
const MODELS = [
{ key: 'default', label: '默认模型' },
{ key: '2016.xmas.1', label: '2016圣诞节 Ver.1' },
{ key: '2016.xmas.2', label: '2016圣诞节 Ver.2' },
{ key: '2017.newyear', label: '2017新年' },
{ key: '2017.school', label: '2017校园' },
{ key: '2017.vdays', label: '2017情人节' },
{ key: '2017.summer.normal.1', label: '2017夏季普通版Ver.1' },
{ key: '2017.summer.super.1', label: '2017夏季高级版Ver.1' },
{ key: '2017.summer.normal.2', label: '2017夏季普通版Ver.2' },
{ key: '2017.summer.super.2', label: '2017夏季高级版Ver.2' },
{ key: '2017.cba-normal', label: '2017CBA普通版' },
{ key: '2017.cba-super', label: '2017CBA高级版' },
{ key: '2017.tomo-bukatsu.low', label: '2017朋友社团活动普通版' },
{ key: '2017.tomo-bukatsu.high', label: '2017朋友社团活动高级版' },
{ key: '2017.valley', label: '2017山谷?' },
{ key: '2018.spring', label: '2018春季' },
{ key: '2018.lover', label: '2018情人节' },
{ key: '2018.bls-summer', label: '2018夏季 BLS' },
{ key: '2018.bls-winter', label: '2018冬季 BLS' },
]
let currentChar = '22'
let currentModel = 'default'
// ── 渲染模型按钮 ──────────────────────────────────────────────────────────
const controls = document.getElementById('controls')
function renderModelBtns() {
// 移除旧的模型按钮(保留角色行和 label
controls.querySelectorAll('.btn').forEach(el => el.remove())
MODELS.forEach(m => {
const btn = document.createElement('button')
btn.className = 'btn' + (m.key === currentModel ? ' active' : '')
btn.textContent = m.label
btn.addEventListener('click', () => loadModel(currentChar, m.key))
controls.appendChild(btn)
})
}
// ── 加载模型 ──────────────────────────────────────────────────────────────
function loadModel(char, modelKey) {
currentChar = char
currentModel = modelKey
const path = `res/model/${char}/model.${modelKey}.json`
document.getElementById('previewLabel').textContent =
`${char}娘 · ${MODELS.find(m => m.key === modelKey)?.label ?? modelKey}`
// 更新按钮状态
controls.querySelectorAll('.btn').forEach((btn, i) => {
btn.classList.toggle('active', MODELS[i].key === modelKey)
})
controls.querySelectorAll('.char-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.char === char)
})
loadlive2d('live2d', path)
}
// ── 角色切换 ──────────────────────────────────────────────────────────────
controls.querySelectorAll('.char-btn').forEach(btn => {
btn.addEventListener('click', () => loadModel(btn.dataset.char, currentModel))
})
// ── 主题切换 ──────────────────────────────────────────────────────────────
const STORAGE_KEY = 'live2d_theme'
const themeBtn = document.getElementById('themeToggle')
function applyTheme(theme) {
document.documentElement.setAttribute('data-theme', theme)
themeBtn.textContent = theme === 'dark' ? '☀️ 浅色' : '🌙 深色'
}
function getInitialTheme() {
const saved = localStorage.getItem(STORAGE_KEY)
if (saved) return saved
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
applyTheme(getInitialTheme())
themeBtn.addEventListener('click', () => {
const next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
applyTheme(next)
localStorage.setItem(STORAGE_KEY, next)
})
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (!localStorage.getItem(STORAGE_KEY)) applyTheme(e.matches ? 'dark' : 'light')
})
// ── 初始化 ────────────────────────────────────────────────────────────────
renderModelBtns()
loadModel('22', 'default')
</script>
</body>
</html>