新增简易的8080面板😊
This commit is contained in:
324
video/plugin-demo.html
Normal file
324
video/plugin-demo.html
Normal file
@@ -0,0 +1,324 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>FutureOSS - 插件开发演示</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Segoe UI', 'PingFang SC', sans-serif;
|
||||
background: #0d0d0d;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
.bg-grid {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(240, 147, 251, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(240, 147, 251, 0.1) 1px, transparent 1px);
|
||||
background-size: 40px 40px;
|
||||
z-index: 0;
|
||||
}
|
||||
#play-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(0,0,0,0.9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
#play-overlay.hidden { opacity: 0; pointer-events: none; }
|
||||
.play-circle {
|
||||
width: 120px; height: 120px;
|
||||
border: 4px solid #f093fb;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3rem;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
transition: transform 0.3s, background 0.3s;
|
||||
}
|
||||
.play-circle:hover {
|
||||
transform: scale(1.1);
|
||||
background: rgba(240, 147, 251, 0.2);
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(240, 147, 251, 0.4); }
|
||||
50% { box-shadow: 0 0 0 30px rgba(240, 147, 251, 0); }
|
||||
}
|
||||
|
||||
/* 时间线 */
|
||||
#timeline {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
.step {
|
||||
position: absolute;
|
||||
width: 70%;
|
||||
padding: 40px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(240, 147, 251, 0.3);
|
||||
opacity: 0;
|
||||
transform: scale(0.8) translateY(50px);
|
||||
transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
.step.active {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
.step.prev {
|
||||
opacity: 0;
|
||||
transform: scale(0.6) translateX(-100px);
|
||||
}
|
||||
.step-number {
|
||||
width: 50px; height: 50px;
|
||||
background: linear-gradient(135deg, #f093fb, #f5576c);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.step h3 { font-size: 1.5rem; margin-bottom: 15px; }
|
||||
.step p { color: #aaa; line-height: 1.8; font-size: 1.05rem; }
|
||||
.step code {
|
||||
display: block;
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 8px;
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 0.9rem;
|
||||
color: #4facfe;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#progress {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #f093fb, #f5576c, #4facfe);
|
||||
width: 0%;
|
||||
transition: width 0.3s;
|
||||
z-index: 50;
|
||||
}
|
||||
#title {
|
||||
position: fixed;
|
||||
top: 40px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
opacity: 0;
|
||||
transition: opacity 0.8s;
|
||||
z-index: 10;
|
||||
text-shadow: 0 4px 20px rgba(240, 147, 251, 0.5);
|
||||
}
|
||||
#title.show { opacity: 1; }
|
||||
#controls {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
z-index: 50;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
#controls.show { opacity: 1; }
|
||||
.ctrl-btn {
|
||||
width: 50px; height: 50px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 1.3rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, transform 0.2s;
|
||||
}
|
||||
.ctrl-btn:hover { background: rgba(240, 147, 251, 0.5); transform: scale(1.1); }
|
||||
|
||||
/* 装饰几何 */
|
||||
.geo {
|
||||
position: absolute;
|
||||
border: 2px solid rgba(240, 147, 251, 0.2);
|
||||
border-radius: 4px;
|
||||
animation: spin 10s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-grid"></div>
|
||||
<div class="geo" style="width:80px;height:80px;top:15%;left:10%;"></div>
|
||||
<div class="geo" style="width:60px;height:60px;bottom:20%;right:15%;border-radius:50%;animation-duration:8s;"></div>
|
||||
<div class="geo" style="width:100px;height:100px;top:60%;left:80%;animation-duration:12s;animation-direction:reverse;"></div>
|
||||
|
||||
<div id="play-overlay">
|
||||
<div class="play-circle">▶</div>
|
||||
</div>
|
||||
|
||||
<div id="title">🔌 从零开发一个插件</div>
|
||||
|
||||
<div id="timeline">
|
||||
<div class="step" data-step="0">
|
||||
<div class="step-number">1</div>
|
||||
<h3>📁 创建插件目录</h3>
|
||||
<p>按照规范在 <code>store/@{作者名}/插件名/</code> 下创建目录</p>
|
||||
<code>
|
||||
store/@{myname}/hello-world/<br>
|
||||
├── main.py<br>
|
||||
├── manifest.json<br>
|
||||
└── README.md
|
||||
</code>
|
||||
</div>
|
||||
<div class="step" data-step="1">
|
||||
<div class="step-number">2</div>
|
||||
<h3>📝 编写 manifest.json</h3>
|
||||
<p>声明插件名称、版本、依赖和描述信息</p>
|
||||
<code>
|
||||
{<br>
|
||||
"name": "hello-world",<br>
|
||||
"version": "1.0.0",<br>
|
||||
"author": "@{myname}",<br>
|
||||
"description": "我的第一个插件"<br>
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
<div class="step" data-step="2">
|
||||
<div class="step-number">3</div>
|
||||
<h3>🐍 编写 main.py</h3>
|
||||
<p>实现插件的初始化逻辑,注册路由或事件</p>
|
||||
<code>
|
||||
class Plugin:<br>
|
||||
def init(self, app):<br>
|
||||
@app.route("/hello")<br>
|
||||
def hello():<br>
|
||||
return {"msg": "Hello, FutureOSS!"}
|
||||
</code>
|
||||
</div>
|
||||
<div class="step" data-step="3">
|
||||
<div class="step-number">4</div>
|
||||
<h3>🚀 启动 & 测试</h3>
|
||||
<p>运行项目,访问 <code>http://localhost:8080/hello</code> 验证插件是否正常工作</p>
|
||||
<code>
|
||||
bash start.sh<br>
|
||||
curl http://localhost:8080/hello<br>
|
||||
# → {"msg": "Hello, FutureOSS!"}
|
||||
</code>
|
||||
</div>
|
||||
<div class="step" data-step="4">
|
||||
<div class="step-number">5</div>
|
||||
<h3>📦 发布到商店</h3>
|
||||
<p>打包插件并上传到 Gitee 商店,其他人一键安装!</p>
|
||||
<code>
|
||||
python tools/sign_single_plugin.py @{myname}/hello-world<br>
|
||||
# 上传到 Gitee 商店仓库
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="progress"></div>
|
||||
<div id="controls">
|
||||
<button class="ctrl-btn" id="btn-replay" title="重播">↻</button>
|
||||
<button class="ctrl-btn" id="btn-sound" title="音效">🔊</button>
|
||||
<button class="ctrl-btn" id="btn-back" title="返回">←</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const AudioCtx = window.AudioContext || window.webkitAudioContext;
|
||||
let audioCtx = null, soundEnabled = true, bgMusic = null;
|
||||
function initAudio() { if (!audioCtx) audioCtx = new AudioCtx(); }
|
||||
function playBgMusic() {
|
||||
if (!soundEnabled || !audioCtx) return;
|
||||
stopBgMusic();
|
||||
const dur = 30, sr = audioCtx.sampleRate;
|
||||
const buf = audioCtx.createBuffer(2, sr * dur, sr);
|
||||
for (let ch = 0; ch < 2; ch++) {
|
||||
const d = buf.getChannelData(ch);
|
||||
for (let i = 0; i < d.length; i++) {
|
||||
const t = i / sr;
|
||||
d[i] = (Math.sin(2*Math.PI*262*t)*0.1 + Math.sin(2*Math.PI*330*t)*0.08 + Math.sin(2*Math.PI*392*t)*0.06) * Math.exp(-t%5);
|
||||
}
|
||||
}
|
||||
bgMusic = audioCtx.createBufferSource();
|
||||
bgMusic.buffer = buf; bgMusic.loop = true;
|
||||
const g = audioCtx.createGain(); g.gain.value = 0.12;
|
||||
bgMusic.connect(g).connect(audioCtx.destination);
|
||||
bgMusic.start();
|
||||
}
|
||||
function stopBgMusic() { if (bgMusic) { try{bgMusic.stop();}catch(e){} bgMusic=null; } }
|
||||
function playPop() {
|
||||
if (!soundEnabled || !audioCtx) return;
|
||||
const o = audioCtx.createOscillator(), g = audioCtx.createGain();
|
||||
o.type='sine'; o.frequency.setValueAtTime(900, audioCtx.currentTime);
|
||||
o.frequency.exponentialRampToValueAtTime(300, audioCtx.currentTime+0.15);
|
||||
g.gain.setValueAtTime(0.25, audioCtx.currentTime);
|
||||
g.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime+0.15);
|
||||
o.connect(g).connect(audioCtx.destination); o.start(); o.stop(audioCtx.currentTime+0.15);
|
||||
}
|
||||
document.getElementById('btn-sound').addEventListener('click', () => {
|
||||
soundEnabled=!soundEnabled;
|
||||
document.getElementById('btn-sound').textContent=soundEnabled?'🔊':'🔇';
|
||||
if(!soundEnabled) stopBgMusic(); else playBgMusic();
|
||||
});
|
||||
|
||||
const overlay = document.getElementById('play-overlay');
|
||||
const title = document.getElementById('title');
|
||||
const progress = document.getElementById('progress');
|
||||
const controls = document.getElementById('controls');
|
||||
const steps = document.querySelectorAll('.step');
|
||||
let animationTimer = null;
|
||||
|
||||
function setProgress(p) { progress.style.width = p + '%'; }
|
||||
|
||||
function startAnimation() {
|
||||
setProgress(0);
|
||||
steps.forEach(s => { s.classList.remove('active','prev'); });
|
||||
title.classList.remove('show');
|
||||
controls.classList.remove('show');
|
||||
overlay.classList.add('hidden');
|
||||
initAudio(); playBgMusic(); playPop();
|
||||
|
||||
setTimeout(() => { title.classList.add('show'); setProgress(5); }, 300);
|
||||
|
||||
steps.forEach((step, i) => {
|
||||
setTimeout(() => {
|
||||
steps.forEach((s, j) => {
|
||||
if (j < i) s.classList.add('prev'), s.classList.remove('active');
|
||||
else s.classList.remove('prev');
|
||||
});
|
||||
step.classList.add('active');
|
||||
playPop();
|
||||
setProgress(10 + (i + 1) * 18);
|
||||
}, 1000 + i * 3500);
|
||||
});
|
||||
|
||||
setTimeout(() => { controls.classList.add('show'); setProgress(100); }, 1000 + steps.length * 3500 + 500);
|
||||
}
|
||||
|
||||
overlay.addEventListener('click', startAnimation);
|
||||
document.getElementById('btn-replay').addEventListener('click', startAnimation);
|
||||
document.getElementById('btn-back').addEventListener('click', () => { stopBgMusic(); location.href='index.html'; });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user