update: 2026-03-28 20:59
This commit is contained in:
418
InfoGenie-frontend/public/toolbox/图片处理/GIF拆帧/index.html
Normal file
418
InfoGenie-frontend/public/toolbox/图片处理/GIF拆帧/index.html
Normal file
@@ -0,0 +1,418 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>帧拆 - GIF & 图片拆帧工具</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://unpkg.com/gif-frames@1.0.1?main=bundled"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', system-ui, sans-serif;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.upload-zone:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 20px 25px -5px rgb(16 185 129 / 0.1), 0 8px 10px -6px rgb(16 185 129 / 0.1);
|
||||
}
|
||||
|
||||
.frame-card {
|
||||
transition: all 0.2s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
}
|
||||
|
||||
.frame-card:hover {
|
||||
transform: scale(1.03);
|
||||
box-shadow: 0 10px 15px -3px rgb(16 185 129 / 0.15);
|
||||
}
|
||||
|
||||
.tail-container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gradient-to-br from-emerald-50 via-white to-teal-50 min-h-screen">
|
||||
<div class="tail-container p-4 md:p-8">
|
||||
<!-- 头部 -->
|
||||
<header class="flex items-center justify-between mb-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-11 h-11 bg-emerald-500 rounded-2xl flex items-center justify-center text-white text-3xl shadow-lg">
|
||||
🖼️
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-emerald-900 tracking-tight">帧拆</h1>
|
||||
<p class="text-emerald-600 text-sm -mt-1">GIF & 图片拆帧工具</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
<div onclick="resetAll()"
|
||||
class="flex items-center gap-2 px-5 py-2.5 bg-white hover:bg-emerald-50 border border-emerald-200 rounded-3xl text-emerald-700 font-medium cursor-pointer transition-colors">
|
||||
<i class="fa-solid fa-rotate"></i>
|
||||
<span>重新开始</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="max-w-5xl mx-auto">
|
||||
<!-- 上传区域 -->
|
||||
<div id="upload-section"
|
||||
class="upload-zone bg-white border-4 border-dashed border-emerald-200 rounded-3xl p-12 text-center cursor-pointer hover:border-emerald-400">
|
||||
<div class="mx-auto w-20 h-20 bg-emerald-100 rounded-2xl flex items-center justify-center mb-6">
|
||||
<i class="fa-solid fa-cloud-arrow-up text-5xl text-emerald-500"></i>
|
||||
</div>
|
||||
<h2 class="text-2xl font-semibold text-emerald-900 mb-2">拖拽图片或 GIF 到此处</h2>
|
||||
<p class="text-emerald-600 mb-6">或点击上传 • 支持 JPG、PNG、GIF、WebP</p>
|
||||
<button onclick="document.getElementById('file-input').click()"
|
||||
class="px-8 py-4 bg-emerald-600 hover:bg-emerald-700 text-white font-medium rounded-3xl shadow-md flex items-center gap-3 mx-auto transition-all">
|
||||
<i class="fa-solid fa-upload"></i>
|
||||
选择文件
|
||||
</button>
|
||||
<input type="file" id="file-input" accept="image/*" class="hidden" onchange="handleFileSelect(event)">
|
||||
</div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div id="main-section" class="hidden">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
||||
<!-- 左侧:原始预览 -->
|
||||
<div class="lg:col-span-5 bg-white rounded-3xl shadow-sm p-6 border border-emerald-100">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fa-solid fa-image text-emerald-500"></i>
|
||||
<h3 class="font-semibold text-emerald-900">原始图片</h3>
|
||||
</div>
|
||||
<span id="file-info" class="text-xs text-emerald-500 font-mono"></span>
|
||||
</div>
|
||||
<div class="aspect-video bg-emerald-50 rounded-2xl overflow-hidden flex items-center justify-center border border-emerald-100">
|
||||
<img id="original-preview"
|
||||
class="max-h-full max-w-full object-contain"
|
||||
alt="原始图片">
|
||||
</div>
|
||||
<div class="mt-6 text-center">
|
||||
<button onclick="startExtract()"
|
||||
id="extract-btn"
|
||||
class="w-full py-4 bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white font-semibold rounded-3xl shadow-lg flex items-center justify-center gap-3 transition-all">
|
||||
<i class="fa-solid fa-scissors"></i>
|
||||
开始拆解成普通图片帧
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:帧结果 -->
|
||||
<div class="lg:col-span-7 bg-white rounded-3xl shadow-sm p-6 border border-emerald-100">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<i class="fa-solid fa-layer-group text-emerald-500"></i>
|
||||
<h3 class="font-semibold text-emerald-900">拆解结果</h3>
|
||||
<span id="frame-count-badge"
|
||||
class="px-3 py-1 bg-emerald-100 text-emerald-700 text-xs font-medium rounded-3xl">0 帧</span>
|
||||
</div>
|
||||
<button onclick="downloadAllZip()"
|
||||
id="download-all-btn"
|
||||
class="hidden px-6 py-2 bg-white border border-emerald-300 hover:border-emerald-400 text-emerald-700 rounded-3xl text-sm font-medium flex items-center gap-2">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
下载全部 (ZIP)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<div id="loading" class="hidden py-20 flex flex-col items-center justify-center">
|
||||
<div class="w-14 h-14 border-4 border-emerald-200 border-t-emerald-500 rounded-full animate-spin"></div>
|
||||
<p class="mt-6 text-emerald-600 font-medium">正在拆解 GIF 帧...</p>
|
||||
<p class="text-xs text-emerald-400 mt-1">大文件可能需要几秒</p>
|
||||
</div>
|
||||
|
||||
<!-- 帧网格 -->
|
||||
<div id="frames-grid"
|
||||
class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4 min-h-[400px]">
|
||||
<!-- JS 动态插入 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部说明 -->
|
||||
<div class="mt-16 text-center text-emerald-400 text-xs flex items-center justify-center gap-6">
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fa-solid fa-shield-halved"></i>
|
||||
<span>完全本地处理 • 隐私安全</span>
|
||||
</div>
|
||||
<div>适配电脑 & 手机</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fa-solid fa-leaf"></i>
|
||||
<span>简洁清新设计</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Tailwind 配置(可选美化)
|
||||
function initTailwind() {
|
||||
// 已通过 CDN 处理
|
||||
}
|
||||
|
||||
let currentFile = null;
|
||||
let currentPreviewUrl = null;
|
||||
let frameDataUrls = [];
|
||||
|
||||
const uploadSection = document.getElementById('upload-section');
|
||||
const mainSection = document.getElementById('main-section');
|
||||
const originalPreview = document.getElementById('original-preview');
|
||||
const fileInfo = document.getElementById('file-info');
|
||||
const framesGrid = document.getElementById('frames-grid');
|
||||
const frameCountBadge = document.getElementById('frame-count-badge');
|
||||
const downloadAllBtn = document.getElementById('download-all-btn');
|
||||
const loadingEl = document.getElementById('loading');
|
||||
const extractBtn = document.getElementById('extract-btn');
|
||||
|
||||
// 拖拽支持
|
||||
function setupDragDrop() {
|
||||
const zone = uploadSection;
|
||||
|
||||
zone.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
zone.style.borderColor = '#10b981';
|
||||
zone.style.backgroundColor = '#ecfdf5';
|
||||
});
|
||||
|
||||
zone.addEventListener('dragleave', () => {
|
||||
zone.style.borderColor = '#a7f3d0';
|
||||
zone.style.backgroundColor = '';
|
||||
});
|
||||
|
||||
zone.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
zone.style.borderColor = '#a7f3d0';
|
||||
zone.style.backgroundColor = '';
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file) handleFile(file);
|
||||
});
|
||||
}
|
||||
|
||||
function handleFileSelect(e) {
|
||||
const file = e.target.files[0];
|
||||
if (file) handleFile(file);
|
||||
}
|
||||
|
||||
function handleFile(file) {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
alert('请上传图片或 GIF 文件!');
|
||||
return;
|
||||
}
|
||||
|
||||
currentFile = file;
|
||||
currentPreviewUrl = URL.createObjectURL(file);
|
||||
|
||||
// 显示主界面
|
||||
uploadSection.classList.add('hidden');
|
||||
mainSection.classList.remove('hidden');
|
||||
|
||||
// 预览原始图片
|
||||
originalPreview.src = currentPreviewUrl;
|
||||
|
||||
// 文件信息
|
||||
const sizeMB = (file.size / (1024 * 1024)).toFixed(2);
|
||||
fileInfo.textContent = `${file.name} • ${sizeMB} MB`;
|
||||
|
||||
// 重置帧
|
||||
resetFrames();
|
||||
|
||||
// 自动高亮按钮
|
||||
extractBtn.classList.add('ring-4', 'ring-emerald-200');
|
||||
setTimeout(() => {
|
||||
extractBtn.classList.remove('ring-4', 'ring-emerald-200');
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function resetFrames() {
|
||||
framesGrid.innerHTML = '';
|
||||
frameDataUrls = [];
|
||||
frameCountBadge.textContent = '0 帧';
|
||||
downloadAllBtn.classList.add('hidden');
|
||||
}
|
||||
|
||||
async function startExtract() {
|
||||
if (!currentFile) return;
|
||||
|
||||
resetFrames();
|
||||
loadingEl.classList.remove('hidden');
|
||||
extractBtn.disabled = true;
|
||||
extractBtn.innerHTML = `<i class="fa-solid fa-spinner fa-spin"></i> 拆解中...`;
|
||||
|
||||
try {
|
||||
if (currentFile.type === 'image/gif') {
|
||||
await extractGifFrames();
|
||||
} else {
|
||||
await extractSingleImage();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('拆解失败,请尝试其他图片。\n错误:' + err.message);
|
||||
} finally {
|
||||
loadingEl.classList.add('hidden');
|
||||
extractBtn.disabled = false;
|
||||
extractBtn.innerHTML = `<i class="fa-solid fa-scissors"></i> 重新拆解`;
|
||||
}
|
||||
}
|
||||
|
||||
// 提取单张普通图片
|
||||
async function extractSingleImage() {
|
||||
const img = new Image();
|
||||
img.onload = function() {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/png', 1.0);
|
||||
|
||||
frameDataUrls = [{
|
||||
index: 0,
|
||||
url: dataUrl,
|
||||
isSingle: true
|
||||
}];
|
||||
|
||||
addFrameToUI(0, dataUrl, true);
|
||||
updateUIAfterExtract(1);
|
||||
};
|
||||
img.src = currentPreviewUrl;
|
||||
}
|
||||
|
||||
// 使用 gif-frames 提取 GIF 帧
|
||||
async function extractGifFrames() {
|
||||
const objectUrl = URL.createObjectURL(currentFile);
|
||||
|
||||
try {
|
||||
const frames = await gifFrames({
|
||||
url: objectUrl,
|
||||
frames: 'all',
|
||||
outputType: 'canvas',
|
||||
cumulative: true // 合成完整帧,显示效果更好
|
||||
});
|
||||
|
||||
frameDataUrls = [];
|
||||
|
||||
for (let i = 0; i < frames.length; i++) {
|
||||
const frame = frames[i];
|
||||
const canvas = frame.getImage();
|
||||
const dataUrl = canvas.toDataURL('image/png', 1.0);
|
||||
|
||||
frameDataUrls.push({
|
||||
index: i,
|
||||
url: dataUrl
|
||||
});
|
||||
|
||||
addFrameToUI(i, dataUrl, false);
|
||||
}
|
||||
|
||||
updateUIAfterExtract(frames.length);
|
||||
|
||||
} finally {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
function addFrameToUI(index, dataUrl, isSingle) {
|
||||
const col = document.createElement('div');
|
||||
col.className = `frame-card bg-white rounded-2xl overflow-hidden border border-emerald-100 shadow-sm`;
|
||||
|
||||
const num = (index + 1).toString().padStart(2, '0');
|
||||
const label = isSingle ? '唯一帧' : `帧 ${num}`;
|
||||
|
||||
col.innerHTML = `
|
||||
<div class="relative">
|
||||
<img src="${dataUrl}"
|
||||
class="w-full aspect-square object-contain bg-emerald-50 p-3">
|
||||
<div class="absolute top-3 left-3 bg-white/90 backdrop-blur px-3 py-1 text-[10px] font-mono font-bold text-emerald-700 rounded-2xl shadow">
|
||||
${label}
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3 flex justify-between items-center">
|
||||
<button onclick="downloadSingleFrame(${index})"
|
||||
class="flex-1 py-2 text-xs font-medium text-emerald-700 hover:bg-emerald-50 rounded-2xl flex items-center justify-center gap-1">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
下载 PNG
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
framesGrid.appendChild(col);
|
||||
}
|
||||
|
||||
function updateUIAfterExtract(count) {
|
||||
frameCountBadge.textContent = `${count} 帧`;
|
||||
downloadAllBtn.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function downloadSingleFrame(index) {
|
||||
const frame = frameDataUrls[index];
|
||||
if (!frame) return;
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = frame.url;
|
||||
link.download = `帧_${(index + 1).toString().padStart(3, '0')}.png`;
|
||||
link.click();
|
||||
}
|
||||
|
||||
async function downloadAllZip() {
|
||||
if (frameDataUrls.length === 0) return;
|
||||
|
||||
const zip = new JSZip();
|
||||
const folderName = currentFile.name.replace(/\.[^/.]+$/, "") || "frames";
|
||||
|
||||
for (let i = 0; i < frameDataUrls.length; i++) {
|
||||
const frame = frameDataUrls[i];
|
||||
const base64Data = frame.url.split(',')[1];
|
||||
const fileName = `帧_${(i + 1).toString().padStart(3, '0')}.png`;
|
||||
zip.file(fileName, base64Data, { base64: true });
|
||||
}
|
||||
|
||||
const zipBlob = await zip.generateAsync({ type: "blob" });
|
||||
const url = URL.createObjectURL(zipBlob);
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${folderName}_所有帧.zip`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function resetAll() {
|
||||
// 清理内存
|
||||
if (currentPreviewUrl) URL.revokeObjectURL(currentPreviewUrl);
|
||||
|
||||
currentFile = null;
|
||||
currentPreviewUrl = null;
|
||||
frameDataUrls = [];
|
||||
|
||||
uploadSection.classList.remove('hidden');
|
||||
mainSection.classList.add('hidden');
|
||||
framesGrid.innerHTML = '';
|
||||
frameCountBadge.textContent = '0 帧';
|
||||
downloadAllBtn.classList.add('hidden');
|
||||
fileInfo.textContent = '';
|
||||
}
|
||||
|
||||
// 初始化
|
||||
window.onload = function() {
|
||||
setupDragDrop();
|
||||
initTailwind();
|
||||
|
||||
// 示例提示(第一次加载)
|
||||
setTimeout(() => {
|
||||
console.log('%c帧拆工具已就绪 🎉\n完全本地运行,无需网络(除首次加载CDN)', 'color:#10b981; font-size:13px');
|
||||
}, 800);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
265
InfoGenie-frontend/public/toolbox/图片处理/图片png转ico格式/index.html
Normal file
265
InfoGenie-frontend/public/toolbox/图片处理/图片png转ico格式/index.html
Normal file
@@ -0,0 +1,265 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>PNG 转 ICO</title>
|
||||
<style>
|
||||
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial; margin: 24px; line-height: 1.5; }
|
||||
.row { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
|
||||
.card { border: 1px solid #ddd; border-radius: 10px; padding: 16px; max-width: 860px; }
|
||||
.sizes { display: flex; gap: 10px; flex-wrap: wrap; margin: 8px 0 12px; }
|
||||
label { user-select: none; }
|
||||
button { padding: 10px 14px; border-radius: 10px; border: 1px solid #ddd; cursor: pointer; }
|
||||
button:disabled { opacity: .6; cursor: not-allowed; }
|
||||
.hint { color: #555; font-size: 14px; }
|
||||
canvas { border: 1px dashed #ccc; border-radius: 8px; }
|
||||
.preview { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 12px; }
|
||||
.preview-item { text-align: center; font-size: 12px; color: #555; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>PNG 转化 ICO</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<input id="file" type="file" accept="image/png" />
|
||||
<button id="convert" disabled>转换并下载 .ico</button>
|
||||
</div>
|
||||
|
||||
<div class="hint" style="margin-top:10px;">
|
||||
默认生成:16/32/48/64/128/256。<br>
|
||||
建议上传至少 256×256 的 PNG,避免小图强行放大后发糊。
|
||||
</div>
|
||||
|
||||
<div style="margin-top:14px;">
|
||||
<div><strong>选择尺寸:</strong></div>
|
||||
<div class="sizes" id="sizes"></div>
|
||||
<div class="row">
|
||||
<label>缩放方式:
|
||||
<select id="fit">
|
||||
<option value="contain" selected>Contain(完整显示,留透明边)</option>
|
||||
<option value="cover">Cover(铺满裁切)</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
背景:
|
||||
<select id="bg">
|
||||
<option value="transparent" selected>透明</option>
|
||||
<option value="white">白色</option>
|
||||
<option value="black">黑色</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview" id="preview"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 常用 icon 尺寸
|
||||
const DEFAULT_SIZES = [16, 32, 48, 64, 128, 256];
|
||||
const sizesWrap = document.getElementById('sizes');
|
||||
const previewWrap = document.getElementById('preview');
|
||||
const fileInput = document.getElementById('file');
|
||||
const convertBtn = document.getElementById('convert');
|
||||
|
||||
// 生成尺寸复选框
|
||||
for (const s of DEFAULT_SIZES) {
|
||||
const id = `sz_${s}`;
|
||||
const label = document.createElement('label');
|
||||
label.innerHTML = `<input type="checkbox" id="${id}" value="${s}" checked> ${s}×${s}`;
|
||||
sizesWrap.appendChild(label);
|
||||
}
|
||||
|
||||
let currentImageBitmap = null;
|
||||
|
||||
fileInput.addEventListener('change', async () => {
|
||||
previewWrap.innerHTML = '';
|
||||
currentImageBitmap = null;
|
||||
convertBtn.disabled = true;
|
||||
|
||||
const file = fileInput.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
if (file.type !== 'image/png') {
|
||||
alert('请上传 PNG 文件');
|
||||
return;
|
||||
}
|
||||
|
||||
const bitmap = await createImageBitmap(file);
|
||||
currentImageBitmap = bitmap;
|
||||
|
||||
// 生成简单预览(只预览 64 和 128)
|
||||
for (const s of [64, 128]) {
|
||||
const canvas = await renderToCanvas(bitmap, s, getFit(), getBg());
|
||||
const item = document.createElement('div');
|
||||
item.className = 'preview-item';
|
||||
item.appendChild(canvas);
|
||||
const cap = document.createElement('div');
|
||||
cap.textContent = `${s}×${s}`;
|
||||
item.appendChild(cap);
|
||||
previewWrap.appendChild(item);
|
||||
}
|
||||
|
||||
convertBtn.disabled = false;
|
||||
});
|
||||
|
||||
convertBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
if (!currentImageBitmap) return;
|
||||
|
||||
const sizes = getSelectedSizes();
|
||||
if (sizes.length === 0) {
|
||||
alert('请至少选择一个尺寸');
|
||||
return;
|
||||
}
|
||||
|
||||
convertBtn.disabled = true;
|
||||
convertBtn.textContent = '转换中...';
|
||||
|
||||
// 1) 生成每个尺寸的 PNG ArrayBuffer
|
||||
const pngBuffers = [];
|
||||
for (const s of sizes) {
|
||||
const canvas = await renderToCanvas(currentImageBitmap, s, getFit(), getBg());
|
||||
const ab = await canvasToPngArrayBuffer(canvas);
|
||||
pngBuffers.push({ size: s, buffer: ab });
|
||||
}
|
||||
|
||||
// 2) 打包成 ICO(每张图像使用 PNG payload)
|
||||
const icoBytes = buildIcoFromPngBuffers(pngBuffers);
|
||||
|
||||
// 3) 下载
|
||||
const blob = new Blob([icoBytes], { type: 'image/x-icon' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'favicon.ico';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert('转换失败:' + (e?.message || e));
|
||||
} finally {
|
||||
convertBtn.disabled = false;
|
||||
convertBtn.textContent = '转换并下载 .ico';
|
||||
}
|
||||
});
|
||||
|
||||
function getSelectedSizes() {
|
||||
const cbs = sizesWrap.querySelectorAll('input[type="checkbox"]');
|
||||
const sizes = [];
|
||||
cbs.forEach(cb => { if (cb.checked) sizes.push(parseInt(cb.value, 10)); });
|
||||
// 从小到大排序(无硬性要求,但更直观)
|
||||
sizes.sort((a,b) => a - b);
|
||||
return sizes;
|
||||
}
|
||||
|
||||
function getFit() {
|
||||
return document.getElementById('fit').value;
|
||||
}
|
||||
function getBg() {
|
||||
return document.getElementById('bg').value;
|
||||
}
|
||||
|
||||
async function renderToCanvas(bitmap, size, fit = 'contain', bg = 'transparent') {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// 背景
|
||||
if (bg !== 'transparent') {
|
||||
ctx.fillStyle = bg;
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
} else {
|
||||
ctx.clearRect(0, 0, size, size);
|
||||
}
|
||||
|
||||
const iw = bitmap.width, ih = bitmap.height;
|
||||
|
||||
// 计算绘制区域
|
||||
let dw, dh, dx, dy;
|
||||
const scaleContain = Math.min(size / iw, size / ih);
|
||||
const scaleCover = Math.max(size / iw, size / ih);
|
||||
const scale = (fit === 'cover') ? scaleCover : scaleContain;
|
||||
|
||||
dw = Math.round(iw * scale);
|
||||
dh = Math.round(ih * scale);
|
||||
dx = Math.round((size - dw) / 2);
|
||||
dy = Math.round((size - dh) / 2);
|
||||
|
||||
// 更好的缩放质量
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
|
||||
ctx.drawImage(bitmap, dx, dy, dw, dh);
|
||||
|
||||
// cover 模式下如果超出画布,drawImage 已经裁切了
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function canvasToPngArrayBuffer(canvas) {
|
||||
return new Promise((resolve, reject) => {
|
||||
canvas.toBlob(async (blob) => {
|
||||
try {
|
||||
if (!blob) return reject(new Error('canvas.toBlob() 返回空'));
|
||||
const ab = await blob.arrayBuffer();
|
||||
resolve(ab);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}, 'image/png');
|
||||
});
|
||||
}
|
||||
|
||||
// 按 ICO 格式打包:ICONDIR(6) + ICONDIRENTRY(n*16) + PNG data...
|
||||
// 目录项字段(常见解释):width/height(1B, 0 表示 256), colorCount(1B), reserved(1B),
|
||||
// planes(2B), bitCount(2B), bytesInRes(4B), imageOffset(4B)
|
||||
function buildIcoFromPngBuffers(pngs) {
|
||||
const count = pngs.length;
|
||||
|
||||
const headerSize = 6;
|
||||
const entrySize = 16;
|
||||
const dirSize = headerSize + count * entrySize;
|
||||
|
||||
// 计算总大小
|
||||
let totalSize = dirSize;
|
||||
for (const p of pngs) totalSize += p.buffer.byteLength;
|
||||
|
||||
const out = new Uint8Array(totalSize);
|
||||
const dv = new DataView(out.buffer);
|
||||
|
||||
// ICONDIR
|
||||
dv.setUint16(0, 0, true); // reserved
|
||||
dv.setUint16(2, 1, true); // type = 1 (icon)
|
||||
dv.setUint16(4, count, true); // count
|
||||
|
||||
// ICONDIRENTRYs
|
||||
let dataOffset = dirSize;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const { size, buffer } = pngs[i];
|
||||
const entryOff = headerSize + i * entrySize;
|
||||
|
||||
dv.setUint8(entryOff + 0, size >= 256 ? 0 : size); // width
|
||||
dv.setUint8(entryOff + 1, size >= 256 ? 0 : size); // height
|
||||
dv.setUint8(entryOff + 2, 0); // colorCount
|
||||
dv.setUint8(entryOff + 3, 0); // reserved
|
||||
dv.setUint16(entryOff + 4, 1, true); // planes (常用 1)
|
||||
dv.setUint16(entryOff + 6, 32, true); // bitCount (常用 32)
|
||||
dv.setUint32(entryOff + 8, buffer.byteLength, true); // bytesInRes
|
||||
dv.setUint32(entryOff + 12, dataOffset, true); // imageOffset
|
||||
|
||||
// 拷贝 PNG 数据
|
||||
out.set(new Uint8Array(buffer), dataOffset);
|
||||
dataOffset += buffer.byteLength;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
426
InfoGenie-frontend/public/toolbox/图片处理/图片像素化处理/index.html
Normal file
426
InfoGenie-frontend/public/toolbox/图片处理/图片像素化处理/index.html
Normal file
@@ -0,0 +1,426 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>图片像素化工具</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
padding: 30px;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
margin-bottom: 30px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
border: 3px dashed #667eea;
|
||||
border-radius: 15px;
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 20px;
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.upload-area:hover {
|
||||
border-color: #764ba2;
|
||||
background: #f0f1ff;
|
||||
}
|
||||
|
||||
.upload-area.dragover {
|
||||
border-color: #764ba2;
|
||||
background: #e8e9ff;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#fileInput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin: 20px 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
flex: 1;
|
||||
height: 8px;
|
||||
border-radius: 5px;
|
||||
background: #e0e0e0;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #667eea;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb:hover {
|
||||
background: #764ba2;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #667eea;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.value-display {
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
display: none;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
canvas {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: none;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-download {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.btn-download:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-reset {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
.btn-reset:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(245, 87, 108, 0.4);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
padding: 30px 15px;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🎨 图片像素化工具</h1>
|
||||
|
||||
<div class="upload-area" id="uploadArea">
|
||||
<div class="upload-icon">📁</div>
|
||||
<div class="upload-text">点击或拖拽图片到这里</div>
|
||||
<input type="file" id="fileInput" accept="image/*">
|
||||
</div>
|
||||
|
||||
<div class="controls" id="controls">
|
||||
<div class="control-group">
|
||||
<label for="pixelSize">像素化程度</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="pixelSize" min="1" max="50" value="10">
|
||||
<span class="value-display" id="pixelValue">10</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="canvas-container" id="canvasContainer">
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="buttons" id="buttons">
|
||||
<button class="btn-download" id="downloadBtn">下载图片</button>
|
||||
<button class="btn-reset" id="resetBtn">重新上传</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const controls = document.getElementById('controls');
|
||||
const canvasContainer = document.getElementById('canvasContainer');
|
||||
const canvas = document.getElementById('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const pixelSizeInput = document.getElementById('pixelSize');
|
||||
const pixelValue = document.getElementById('pixelValue');
|
||||
const buttons = document.getElementById('buttons');
|
||||
const downloadBtn = document.getElementById('downloadBtn');
|
||||
const resetBtn = document.getElementById('resetBtn');
|
||||
|
||||
let originalImage = null;
|
||||
|
||||
// 点击上传区域
|
||||
uploadArea.addEventListener('click', () => {
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
// 文件选择
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file && file.type.startsWith('image/')) {
|
||||
loadImage(file);
|
||||
}
|
||||
});
|
||||
|
||||
// 拖拽上传
|
||||
uploadArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.add('dragover');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('dragleave', () => {
|
||||
uploadArea.classList.remove('dragover');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.remove('dragover');
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file && file.type.startsWith('image/')) {
|
||||
loadImage(file);
|
||||
}
|
||||
});
|
||||
|
||||
// 加载图片
|
||||
function loadImage(file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
originalImage = img;
|
||||
initCanvas();
|
||||
pixelateImage();
|
||||
showControls();
|
||||
};
|
||||
img.src = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
// 初始化画布
|
||||
function initCanvas() {
|
||||
const maxWidth = 800;
|
||||
let width = originalImage.width;
|
||||
let height = originalImage.height;
|
||||
|
||||
if (width > maxWidth) {
|
||||
height = (maxWidth / width) * height;
|
||||
width = maxWidth;
|
||||
}
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
// 像素化图片
|
||||
function pixelateImage() {
|
||||
const pixelSize = parseInt(pixelSizeInput.value);
|
||||
|
||||
// 清空画布
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 绘制原图
|
||||
ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 获取图像数据
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const data = imageData.data;
|
||||
|
||||
// 像素化处理
|
||||
for (let y = 0; y < canvas.height; y += pixelSize) {
|
||||
for (let x = 0; x < canvas.width; x += pixelSize) {
|
||||
// 计算区块的平均颜色
|
||||
let r = 0, g = 0, b = 0, a = 0, count = 0;
|
||||
|
||||
for (let dy = 0; dy < pixelSize && y + dy < canvas.height; dy++) {
|
||||
for (let dx = 0; dx < pixelSize && x + dx < canvas.width; dx++) {
|
||||
const index = ((y + dy) * canvas.width + (x + dx)) * 4;
|
||||
r += data[index];
|
||||
g += data[index + 1];
|
||||
b += data[index + 2];
|
||||
a += data[index + 3];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算平均值
|
||||
r = Math.floor(r / count);
|
||||
g = Math.floor(g / count);
|
||||
b = Math.floor(b / count);
|
||||
a = Math.floor(a / count);
|
||||
|
||||
// 填充像素块
|
||||
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
|
||||
ctx.fillRect(x, y, pixelSize, pixelSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 显示控制面板
|
||||
function showControls() {
|
||||
controls.style.display = 'block';
|
||||
canvasContainer.style.display = 'block';
|
||||
buttons.style.display = 'flex';
|
||||
}
|
||||
|
||||
// 像素大小改变
|
||||
pixelSizeInput.addEventListener('input', (e) => {
|
||||
pixelValue.textContent = e.target.value;
|
||||
pixelateImage();
|
||||
});
|
||||
|
||||
// 下载图片
|
||||
downloadBtn.addEventListener('click', () => {
|
||||
const link = document.createElement('a');
|
||||
link.download = `pixelated-image-${Date.now()}.png`;
|
||||
link.href = canvas.toDataURL('image/png');
|
||||
link.click();
|
||||
});
|
||||
|
||||
// 重置
|
||||
resetBtn.addEventListener('click', () => {
|
||||
originalImage = null;
|
||||
fileInput.value = '';
|
||||
controls.style.display = 'none';
|
||||
canvasContainer.style.display = 'none';
|
||||
buttons.style.display = 'none';
|
||||
pixelSizeInput.value = 10;
|
||||
pixelValue.textContent = '10';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
332
InfoGenie-frontend/public/toolbox/图片处理/图片转webp格式/index.html
Normal file
332
InfoGenie-frontend/public/toolbox/图片处理/图片转webp格式/index.html
Normal file
@@ -0,0 +1,332 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>图片转 WebP</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
body { font-family: 'Inter', system-ui, sans-serif; }
|
||||
.drop-zone {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.drop-zone.dragover {
|
||||
background-color: rgb(249 250 251);
|
||||
border-color: rgb(59 130 246);
|
||||
}
|
||||
.preview-img {
|
||||
max-height: 320px;
|
||||
object-fit: contain;
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-zinc-950 text-zinc-100 min-h-screen">
|
||||
<div class="max-w-5xl mx-auto px-4 py-8">
|
||||
<!-- 头部 -->
|
||||
<div class="flex flex-col items-center mb-10">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="w-9 h-9 bg-blue-600 rounded-xl flex items-center justify-center text-white font-bold text-2xl">W</div>
|
||||
<h1 class="text-4xl font-semibold tracking-tight">图片转 WebP</h1>
|
||||
</div>
|
||||
<p class="text-zinc-400 text-center max-w-md">
|
||||
本地转换 · 零上传 · 隐私安全<br>
|
||||
<span class="text-xs">支持 JPEG / PNG / GIF / BMP / AVIF 等任意格式</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="main" class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
|
||||
<!-- 左侧:上传 & 原图 -->
|
||||
<div class="space-y-6">
|
||||
<!-- 上传区 -->
|
||||
<div id="upload-zone"
|
||||
class="drop-zone border-2 border-dashed border-zinc-700 hover:border-blue-500 rounded-3xl h-80 flex flex-col items-center justify-center cursor-pointer bg-zinc-900/50">
|
||||
<input type="file" id="file-input" accept="image/*" class="hidden">
|
||||
<div class="text-center">
|
||||
<div class="w-16 h-16 mx-auto mb-4 bg-zinc-800 rounded-2xl flex items-center justify-center">
|
||||
📸
|
||||
</div>
|
||||
<p class="text-lg font-medium mb-1">拖拽图片到这里</p>
|
||||
<p class="text-sm text-zinc-400">或点击选择文件</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-6">支持所有常见图片格式</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 原图预览 -->
|
||||
<div id="original-preview" class="hidden bg-zinc-900 rounded-3xl overflow-hidden border border-zinc-800">
|
||||
<div class="bg-zinc-950 px-5 py-3 flex items-center justify-between border-b border-zinc-800">
|
||||
<span class="font-medium text-sm">原图</span>
|
||||
<span id="original-info" class="text-xs text-zinc-400"></span>
|
||||
</div>
|
||||
<div class="p-5">
|
||||
<img id="original-img" class="preview-img w-full mx-auto rounded-2xl" alt="原图">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:设置 & 转换结果 -->
|
||||
<div class="space-y-6">
|
||||
<!-- 参数设置 -->
|
||||
<div id="controls" class="hidden bg-zinc-900 rounded-3xl p-6 border border-zinc-800">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="font-medium text-sm">转换设置</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- 质量滑块 -->
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-2">
|
||||
<span class="text-zinc-400">压缩质量</span>
|
||||
<span id="quality-value" class="font-mono text-blue-400">82</span>
|
||||
</div>
|
||||
<input type="range" id="quality"
|
||||
min="1" max="100" value="82"
|
||||
class="w-full accent-blue-500">
|
||||
<div class="flex justify-between text-[10px] text-zinc-500 mt-1">
|
||||
<span>节省空间</span>
|
||||
<span>最高画质</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 转换按钮 -->
|
||||
<button id="convert-btn"
|
||||
class="w-full bg-blue-600 hover:bg-blue-500 transition-colors text-white font-medium py-4 rounded-2xl text-lg shadow-lg shadow-blue-500/30 flex items-center justify-center gap-2">
|
||||
<span>🚀</span>
|
||||
<span>开始转换为 WebP</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 转换结果 -->
|
||||
<div id="result" class="hidden bg-zinc-900 rounded-3xl overflow-hidden border border-zinc-800">
|
||||
<div class="bg-zinc-950 px-5 py-3 flex items-center justify-between border-b border-zinc-800">
|
||||
<span class="font-medium text-sm">转换结果</span>
|
||||
<button id="convert-again"
|
||||
class="text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1">
|
||||
<span>↻</span> 转换另一张
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="p-5">
|
||||
<img id="webp-img" class="preview-img w-full mx-auto rounded-2xl" alt="WebP">
|
||||
</div>
|
||||
|
||||
<!-- 数据对比 -->
|
||||
<div class="grid grid-cols-2 gap-px bg-zinc-800 mx-5 mb-5 rounded-2xl overflow-hidden">
|
||||
<div class="bg-zinc-900 p-4">
|
||||
<div class="text-xs text-zinc-400">原图大小</div>
|
||||
<div id="orig-size" class="font-mono text-lg font-medium text-white"></div>
|
||||
<div id="orig-dim" class="text-xs text-zinc-500 mt-1"></div>
|
||||
</div>
|
||||
<div class="bg-zinc-900 p-4">
|
||||
<div class="text-xs text-emerald-400">WebP 大小</div>
|
||||
<div id="webp-size" class="font-mono text-lg font-medium text-emerald-400"></div>
|
||||
<div id="webp-dim" class="text-xs text-zinc-500 mt-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx-5 mb-6 bg-emerald-950 border border-emerald-900 rounded-2xl px-4 py-3 flex items-center justify-between">
|
||||
<span class="text-emerald-400 text-sm">压缩率</span>
|
||||
<span id="compress-rate" class="font-mono text-2xl font-semibold text-emerald-400"></span>
|
||||
</div>
|
||||
|
||||
<!-- 下载按钮 -->
|
||||
<div class="px-5 pb-5">
|
||||
<a id="download-link"
|
||||
class="block text-center bg-white text-zinc-950 font-medium py-4 rounded-2xl text-lg hover:bg-zinc-100 transition-colors">
|
||||
⬇️ 下载 WebP 文件
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示 -->
|
||||
<div class="mt-12 text-center text-xs text-zinc-500">
|
||||
全部在浏览器本地完成 · 无需联网 · 图片永不上传
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Tailwind 脚本已通过 CDN 加载,这里无需额外 script
|
||||
function initTailwind() {
|
||||
// 已在 head 加载
|
||||
}
|
||||
|
||||
let originalFile = null;
|
||||
let webpBlob = null;
|
||||
let originalUrl = null;
|
||||
let webpUrl = null;
|
||||
|
||||
const uploadZone = document.getElementById('upload-zone');
|
||||
const fileInput = document.getElementById('file-input');
|
||||
const originalPreview = document.getElementById('original-preview');
|
||||
const originalImg = document.getElementById('original-img');
|
||||
const originalInfo = document.getElementById('original-info');
|
||||
|
||||
const controls = document.getElementById('controls');
|
||||
const qualitySlider = document.getElementById('quality');
|
||||
const qualityValue = document.getElementById('quality-value');
|
||||
const convertBtn = document.getElementById('convert-btn');
|
||||
|
||||
const resultPanel = document.getElementById('result');
|
||||
const webpImg = document.getElementById('webp-img');
|
||||
const origSizeEl = document.getElementById('orig-size');
|
||||
const origDimEl = document.getElementById('orig-dim');
|
||||
const webpSizeEl = document.getElementById('webp-size');
|
||||
const webpDimEl = document.getElementById('webp-dim');
|
||||
const compressRateEl = document.getElementById('compress-rate');
|
||||
const downloadLink = document.getElementById('download-link');
|
||||
const convertAgain = document.getElementById('convert-again');
|
||||
|
||||
// 拖拽事件
|
||||
function setupDragDrop() {
|
||||
uploadZone.addEventListener('dragover', e => {
|
||||
e.preventDefault();
|
||||
uploadZone.classList.add('dragover');
|
||||
});
|
||||
uploadZone.addEventListener('dragleave', () => {
|
||||
uploadZone.classList.remove('dragover');
|
||||
});
|
||||
uploadZone.addEventListener('drop', e => {
|
||||
e.preventDefault();
|
||||
uploadZone.classList.remove('dragover');
|
||||
handleFiles(e.dataTransfer.files);
|
||||
});
|
||||
uploadZone.addEventListener('click', () => fileInput.click());
|
||||
}
|
||||
|
||||
// 文件选择
|
||||
fileInput.addEventListener('change', e => {
|
||||
if (e.target.files.length > 0) handleFiles(e.target.files);
|
||||
});
|
||||
|
||||
function handleFiles(files) {
|
||||
const file = files[0];
|
||||
if (!file || !file.type.startsWith('image/')) {
|
||||
alert('请选择图片文件!');
|
||||
return;
|
||||
}
|
||||
originalFile = file;
|
||||
|
||||
// 释放之前的 URL
|
||||
if (originalUrl) URL.revokeObjectURL(originalUrl);
|
||||
|
||||
originalUrl = URL.createObjectURL(file);
|
||||
originalImg.src = originalUrl;
|
||||
|
||||
// 显示原图信息
|
||||
const sizeMB = (file.size / 1024 / 1024).toFixed(2);
|
||||
originalInfo.textContent = `${file.name} · ${(file.size / 1024).toFixed(1)} KB`;
|
||||
|
||||
originalPreview.classList.remove('hidden');
|
||||
controls.classList.remove('hidden');
|
||||
resultPanel.classList.add('hidden');
|
||||
|
||||
// 重置
|
||||
webpBlob = null;
|
||||
}
|
||||
|
||||
// 质量滑块实时显示
|
||||
qualitySlider.addEventListener('input', () => {
|
||||
qualityValue.textContent = qualitySlider.value;
|
||||
});
|
||||
|
||||
// 转换核心函数
|
||||
async function convertToWebP() {
|
||||
if (!originalFile) return;
|
||||
|
||||
convertBtn.innerHTML = `
|
||||
<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||
</svg>
|
||||
`;
|
||||
convertBtn.disabled = true;
|
||||
|
||||
const img = new Image();
|
||||
img.onload = async () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
const quality = parseFloat(qualitySlider.value) / 100;
|
||||
|
||||
// 使用 toBlob 获得更精确的 Blob
|
||||
canvas.toBlob(async (blob) => {
|
||||
webpBlob = blob;
|
||||
|
||||
if (webpUrl) URL.revokeObjectURL(webpUrl);
|
||||
webpUrl = URL.createObjectURL(webpBlob);
|
||||
|
||||
webpImg.src = webpUrl;
|
||||
|
||||
// 计算数据
|
||||
const origSize = originalFile.size;
|
||||
const webpSize = webpBlob.size;
|
||||
const rate = ((origSize - webpSize) / origSize * 100).toFixed(1);
|
||||
|
||||
// 填充界面
|
||||
origSizeEl.textContent = formatSize(origSize);
|
||||
origDimEl.textContent = `${img.width}×${img.height}`;
|
||||
|
||||
webpSizeEl.textContent = formatSize(webpSize);
|
||||
webpDimEl.textContent = `${img.width}×${img.height}`;
|
||||
|
||||
compressRateEl.textContent = rate + '%';
|
||||
|
||||
// 下载链接
|
||||
const newName = originalFile.name.replace(/\.[^/.]+$/, "") + ".webp";
|
||||
downloadLink.download = newName;
|
||||
downloadLink.href = webpUrl;
|
||||
|
||||
// 显示结果
|
||||
resultPanel.classList.remove('hidden');
|
||||
convertBtn.innerHTML = `✅ 已完成`;
|
||||
setTimeout(() => {
|
||||
convertBtn.innerHTML = `<span>🚀</span><span>重新转换</span>`;
|
||||
convertBtn.disabled = false;
|
||||
}, 800);
|
||||
|
||||
}, 'image/webp', quality);
|
||||
};
|
||||
|
||||
img.src = originalUrl;
|
||||
}
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (bytes < 1024) return bytes + ' B';
|
||||
else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||||
else return (bytes / 1024 / 1024).toFixed(2) + ' MB';
|
||||
}
|
||||
|
||||
// 绑定按钮
|
||||
convertBtn.addEventListener('click', convertToWebP);
|
||||
|
||||
convertAgain.addEventListener('click', () => {
|
||||
resultPanel.classList.add('hidden');
|
||||
// 清空结果但保留原图
|
||||
});
|
||||
|
||||
// 初始化
|
||||
function init() {
|
||||
setupDragDrop();
|
||||
// 初始质量显示
|
||||
qualityValue.textContent = qualitySlider.value;
|
||||
|
||||
// 键盘快捷键:回车转换
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter' && !resultPanel.classList.contains('hidden')) {
|
||||
convertToWebP();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = init;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
425
InfoGenie-frontend/public/toolbox/学习工具/HTML格式大全/index.html
Normal file
425
InfoGenie-frontend/public/toolbox/学习工具/HTML格式大全/index.html
Normal file
@@ -0,0 +1,425 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>html格式大全</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>现代浏览器支持的HTML元素大全</h1>
|
||||
<h2>by-树萌芽(=・ω・=)</h2>
|
||||
<h2>(零)页头区域 (header)</h2>
|
||||
<p>本页面展示所有现代浏览器支持的HTML元素,不使用CSS和JavaScript</p>
|
||||
<nav>
|
||||
<h2>一.页面导航</h2>
|
||||
<ul>
|
||||
<li><a href="#text">文本内容</a></li>
|
||||
<li><a href="#sections">内容分区</a></li>
|
||||
<li><a href="#media">媒体内容</a></li>
|
||||
<li><a href="#tables">表格元素</a></li>
|
||||
<li><a href="#forms">表单元素</a></li>
|
||||
<li><a href="#interactive">交互元素</a></li>
|
||||
<li><a href="#semantic">语义元素</a></li>
|
||||
<li><a href="#other">其他元素</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section id="text">
|
||||
<h2>二.文本内容元素</h2>
|
||||
|
||||
<h3>标题元素</h3>
|
||||
<h1>标题级别 1 (h1)</h1>
|
||||
<h2>标题级别 2 (h2)</h2>
|
||||
<h3>标题级别 3 (h3)</h3>
|
||||
<h4>标题级别 4 (h4)</h4>
|
||||
<h5>标题级别 5 (h5)</h5>
|
||||
<h6>标题级别 6 (h6)</h6>
|
||||
|
||||
<h3>段落与文本</h3>
|
||||
<p>这是一个段落 (p)</p>
|
||||
<hr>
|
||||
<p>上面是一条水平线 (hr)</p>
|
||||
<br>
|
||||
<p>上面是一个换行 (br)</p>
|
||||
<pre>
|
||||
预格式化文本 (pre)
|
||||
保留空格和换行
|
||||
like this
|
||||
</pre>
|
||||
<blockquote>
|
||||
这是一个块引用 (blockquote),用于引用较长内容
|
||||
<cite>引用来源 (cite)</cite>
|
||||
</blockquote>
|
||||
<p>行内引用: <q>这是一个短引用 (q)</q></p>
|
||||
|
||||
<h3>文本修饰元素</h3>
|
||||
<!--<p>-->
|
||||
<p><b>粗体文本 (b)</b</p>
|
||||
<p><strong>重要文本 (strong)</strong></p>
|
||||
<p><i>斜体文本 (i)</i></p>
|
||||
<p><em>强调文本 (em)</em></p>
|
||||
<p><u>下划线文本 (u)</u></p>
|
||||
<p><mark>标记文本 (mark)</mark></p>
|
||||
<p><small>较小文本 (small)</small></p>
|
||||
<p><del>删除文本 (del)</del></p>
|
||||
<p><ins>插入文本 (ins)</ins></p>
|
||||
<p><sub>下标文本 (sub)</sub></p>
|
||||
<p><sup>上标文本 (sup)</sup></p>
|
||||
<p><abbr title="Abbreviation">缩写 (abbr)</abbr></p>
|
||||
<p><dfn>定义术语 (dfn)</dfn></p>
|
||||
<p><kbd>键盘输入 (kbd)</kbd></p>
|
||||
<p><samp>示例输出 (samp)</samp></p>
|
||||
<p><var>变量 (var)</var></p>
|
||||
<p><code>代码片段 (code)</code></p>
|
||||
<p><time datetime="2023-10-01">时间元素 (time)</time></p>
|
||||
<p><data value="123">数据元素 (data)</data></p>
|
||||
<p><ruby>注音<rt>zhù yīn</rt> (ruby)</ruby></p>
|
||||
<!--</p>-->
|
||||
|
||||
<h3>列表元素</h3>
|
||||
<h4>无序列表 (ul)</h4>
|
||||
<ul>
|
||||
<li>列表项 (li) 1</li>
|
||||
<li>列表项 (li) 2</li>
|
||||
<li>列表项 (li) 3</li>
|
||||
</ul>
|
||||
|
||||
<h4>有序列表 (ol)</h4>
|
||||
<ol>
|
||||
<li>第一项</li>
|
||||
<li>第二项</li>
|
||||
<li>第三项</li>
|
||||
</ol>
|
||||
|
||||
<h4>描述列表 (dl)</h4>
|
||||
<dl>
|
||||
<dt>术语 (dt)</dt>
|
||||
<dd>描述 (dd)</dd>
|
||||
<dt>HTML</dt>
|
||||
<dd>超文本标记语言</dd>
|
||||
<dt>CSS</dt>
|
||||
<dd>层叠样式表</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section id="sections">
|
||||
<h2>(三)内容分区元素</h2>
|
||||
<article>
|
||||
<header>
|
||||
<h3>文章元素 (article)</h3>
|
||||
</header>
|
||||
<p>文章内容,独立的自包含内容</p>
|
||||
<footer>文章页脚</footer>
|
||||
</article>
|
||||
|
||||
<section>
|
||||
<h3>章节元素 (section)</h3>
|
||||
<p>文档中的一个章节</p>
|
||||
</section>
|
||||
|
||||
<aside>
|
||||
<h3>侧边栏元素 (aside)</h3>
|
||||
<p>与周围内容相关但不是主要内容的部分</p>
|
||||
</aside>
|
||||
|
||||
<div>
|
||||
<h3>通用容器 (div)</h3>
|
||||
<p>没有语义含义的通用容器元素</p>
|
||||
</div>
|
||||
|
||||
<address>
|
||||
<h3>联系信息 (address)</h3>
|
||||
<p>作者/所有者联系信息</p>
|
||||
</address>
|
||||
|
||||
<main>
|
||||
<h3>主要内容 (main)</h3>
|
||||
<p>文档的主要内容区域</p>
|
||||
</main>
|
||||
</section>
|
||||
|
||||
<section id="media">
|
||||
<h2>(四)媒体元素</h2>
|
||||
|
||||
<h3>图片元素 (img)</h3>
|
||||
<img src="https://via.placeholder.com/150" alt="占位图片" width="150" height="150">
|
||||
|
||||
<h3>图形元素 (figure 和 figcaption)</h3>
|
||||
<figure>
|
||||
<img src="https://via.placeholder.com/300x100" alt="示例图形" width="300" height="100">
|
||||
<figcaption>图形的标题 (figcaption)</figcaption>
|
||||
</figure>
|
||||
|
||||
<h3>音频元素 (audio)</h3>
|
||||
<audio controls>
|
||||
<source src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" type="audio/mpeg">
|
||||
您的浏览器不支持 audio 元素。
|
||||
</audio>
|
||||
|
||||
<h3>视频元素 (video)</h3>
|
||||
<video width="320" height="240" controls>
|
||||
<source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4">
|
||||
您的浏览器不支持 video 元素。
|
||||
</video>
|
||||
|
||||
<h3>画布元素 (canvas)</h3>
|
||||
<canvas id="myCanvas" width="200" height="100">
|
||||
您的浏览器不支持 canvas 元素。
|
||||
</canvas>
|
||||
|
||||
<h3>矢量图形 (svg)</h3>
|
||||
<svg width="100" height="100">
|
||||
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
|
||||
您的浏览器不支持 SVG
|
||||
</svg>
|
||||
|
||||
<h3>嵌入内容 (embed, iframe, object)</h3>
|
||||
<h4>嵌入元素 (embed)</h4>
|
||||
<embed src="https://via.placeholder.com/200x100" width="200" height="100">
|
||||
|
||||
<h4>内联框架 (iframe)</h4>
|
||||
<iframe src="https://example.com" title="示例页面" width="300" height="150">
|
||||
您的浏览器不支持 iframe
|
||||
</iframe>
|
||||
|
||||
<h4>对象元素 (object)</h4>
|
||||
<object data="https://via.placeholder.com/250x100" width="250" height="100">
|
||||
替代文本
|
||||
</object>
|
||||
|
||||
<h4>图片映射 (map 和 area)</h4>
|
||||
<img src="https://via.placeholder.com/300x200" alt="示例图片" usemap="#examplemap" width="300" height="200">
|
||||
<map name="examplemap">
|
||||
<area shape="rect" coords="0,0,150,100" alt="矩形区域" href="#">
|
||||
<area shape="circle" coords="225,150,50" alt="圆形区域" href="#">
|
||||
</map>
|
||||
|
||||
<h4>轨道路径 (track)</h4>
|
||||
<p>track 元素通常与 video 或 audio 元素一起使用,用于字幕等</p>
|
||||
|
||||
<h4>图片源 (picture 和 source)</h4>
|
||||
<picture>
|
||||
<source media="(min-width: 800px)" srcset="https://via.placeholder.com/800x200">
|
||||
<source media="(min-width: 400px)" srcset="https://via.placeholder.com/400x200">
|
||||
<img src="https://via.placeholder.com/200x200" alt="响应式图片">
|
||||
</picture>
|
||||
</section>
|
||||
|
||||
<section id="tables">
|
||||
<h2>(五)表格元素</h2>
|
||||
<table border="1">
|
||||
<caption>表格标题 (caption)</caption>
|
||||
<colgroup>
|
||||
<col span="2" style="background-color: #f0f0f0">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>表头单元格 (th)</th>
|
||||
<th>表头单元格 (th)</th>
|
||||
<th>表头单元格 (th)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>表格数据 (td)</td>
|
||||
<td>表格数据 (td)</td>
|
||||
<td>表格数据 (td)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>表格数据 (td)</td>
|
||||
<td>表格数据 (td)</td>
|
||||
<td>表格数据 (td)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3">表格页脚 (tfoot)</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section id="forms">
|
||||
<h2>(六)表单元素</h2>
|
||||
<form action="#" method="post">
|
||||
<fieldset>
|
||||
<legend>表单标题 (legend)</legend>
|
||||
|
||||
<label for="text">文本输入 (input type="text"):</label>
|
||||
<input type="text" id="text" name="text" placeholder="请输入文本"><br><br>
|
||||
|
||||
<label for="password">密码输入 (input type="password"):</label>
|
||||
<input type="password" id="password" name="password"><br><br>
|
||||
|
||||
<label for="email">邮箱输入 (input type="email"):</label>
|
||||
<input type="email" id="email" name="email"><br><br>
|
||||
|
||||
<label for="url">URL输入 (input type="url"):</label>
|
||||
<input type="url" id="url" name="url"><br><br>
|
||||
|
||||
<label for="number">数字输入 (input type="number"):</label>
|
||||
<input type="number" id="number" name="number" min="1" max="10"><br><br>
|
||||
|
||||
<label for="range">范围输入 (input type="range"):</label>
|
||||
<input type="range" id="range" name="range" min="0" max="100"><br><br>
|
||||
|
||||
<label for="date">日期输入 (input type="date"):</label>
|
||||
<input type="date" id="date" name="date"><br><br>
|
||||
|
||||
<label for="time">时间输入 (input type="time"):</label>
|
||||
<input type="time" id="time" name="time"><br><br>
|
||||
|
||||
<label for="color">颜色选择 (input type="color"):</label>
|
||||
<input type="color" id="color" name="color"><br><br>
|
||||
|
||||
<label for="file">文件上传 (input type="file"):</label>
|
||||
<input type="file" id="file" name="file"><br><br>
|
||||
|
||||
<label>单选按钮 (input type="radio"):</label><br>
|
||||
<input type="radio" id="option1" name="option" value="1">
|
||||
<label for="option1">选项 1</label><br>
|
||||
<input type="radio" id="option2" name="option" value="2">
|
||||
<label for="option2">选项 2</label><br><br>
|
||||
|
||||
<label>复选框 (input type="checkbox"):</label><br>
|
||||
<input type="checkbox" id="check1" name="check1" value="1">
|
||||
<label for="check1">复选框 1</label><br>
|
||||
<input type="checkbox" id="check2" name="check2" value="2">
|
||||
<label for="check2">复选框 2</label><br><br>
|
||||
|
||||
<label for="select">下拉选择 (select 和 option):</label>
|
||||
<select id="select" name="select">
|
||||
<option value="">请选择</option>
|
||||
<option value="1">选项 1</option>
|
||||
<option value="2">选项 2</option>
|
||||
<option value="3">选项 3</option>
|
||||
</select><br><br>
|
||||
|
||||
<label for="datalist">数据列表 (datalist):</label>
|
||||
<input list="browsers" id="datalist" name="datalist">
|
||||
<datalist id="browsers">
|
||||
<option value="Chrome">
|
||||
<option value="Firefox">
|
||||
<option value="Safari">
|
||||
<option value="Edge">
|
||||
</datalist><br><br>
|
||||
|
||||
<label for="textarea">文本区域 (textarea):</label><br>
|
||||
<textarea id="textarea" name="textarea" rows="4" cols="40">默认文本</textarea><br><br>
|
||||
|
||||
<label for="output">输出元素 (output):</label>
|
||||
<output id="output" name="output">计算结果</output><br><br>
|
||||
|
||||
<label for="progress">进度条 (progress):</label>
|
||||
<progress id="progress" value="70" max="100">70%</progress><br><br>
|
||||
|
||||
<label for="meter">度量衡 (meter):</label>
|
||||
<meter id="meter" value="0.6" min="0" max="1">60%</meter><br><br>
|
||||
|
||||
<input type="submit" value="提交按钮 (input type='submit')">
|
||||
<input type="reset" value="重置按钮 (input type='reset')">
|
||||
<input type="button" value="普通按钮 (input type='button')">
|
||||
<button type="submit">按钮元素 (button)</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section id="interactive">
|
||||
<h2>(七)交互元素</h2>
|
||||
|
||||
<h3>详情和摘要 (details 和 summary)</h3>
|
||||
<details>
|
||||
<summary>点击查看详情 (summary)</summary>
|
||||
<p>这是隐藏的详情内容 (details)</p>
|
||||
</details>
|
||||
|
||||
<h3>对话框 (dialog)</h3>
|
||||
<dialog open>
|
||||
<p>这是一个对话框 (dialog)</p>
|
||||
<form method="dialog">
|
||||
<button>关闭</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<h3>菜单 (menu)</h3>
|
||||
<menu>
|
||||
<li><button>菜单项 1</button></li>
|
||||
<li><button>菜单项 2</button></li>
|
||||
</menu>
|
||||
</section>
|
||||
|
||||
<section id="semantic">
|
||||
<h2>(八)语义元素</h2>
|
||||
|
||||
<p>我们已经展示了大部分语义元素,这里补充一些:</p>
|
||||
|
||||
<h3>导航区域 (nav)</h3>
|
||||
<p>已经在页面顶部使用了 nav 元素</p>
|
||||
|
||||
<h3>页眉页脚 (header, footer)</h3>
|
||||
<p>已经在页面中使用了 header 和 footer 元素</p>
|
||||
|
||||
<h3>时间元素 (time)</h3>
|
||||
<p>会议时间:<time datetime="2023-10-01T09:00">2023年10月1日 09:00</time></p>
|
||||
|
||||
<h3>标记元素 (mark)</h3>
|
||||
<p>搜索关键词:<mark>HTML5</mark> 是网页标准</p>
|
||||
|
||||
<h3>旁注元素 (aside)</h3>
|
||||
<p>已经在内容分区中展示</p>
|
||||
</section>
|
||||
|
||||
<section id="other">
|
||||
<h2>(九)其他元素</h2>
|
||||
|
||||
<h3>模板元素 (template)</h3>
|
||||
<template id="myTemplate">
|
||||
<p>这是一个模板内容,不会在页面加载时显示</p>
|
||||
</template>
|
||||
|
||||
<h3>脚本相关 (noscript)</h3>
|
||||
<noscript>
|
||||
<p>您的浏览器不支持 JavaScript 或已禁用</p>
|
||||
</noscript>
|
||||
|
||||
<h3>标题组 (hgroup)</h3>
|
||||
<hgroup>
|
||||
<h3>主标题</h3>
|
||||
<h4>副标题</h4>
|
||||
</hgroup>
|
||||
|
||||
<h3>方向性文本 (bdi 和 bdo)</h3>
|
||||
<p><bdi>隔离文本方向 (bdi)</bdi></p>
|
||||
<p><bdo dir="rtl">从右到左的文本 (bdo)</bdo></p>
|
||||
|
||||
<h3>换行机会 (wbr)</h3>
|
||||
<p>这是一个非常长的单词:super<wbr>cali<wbr>fragi<wbr>listic<wbr>expiali<wbr>docious</p>
|
||||
|
||||
<h3>嵌入对象参数 (param)</h3>
|
||||
<p>param 元素与 object 元素一起使用</p>
|
||||
|
||||
<h3>脚本元素 (script)</h3>
|
||||
<p>虽然本页面不使用 JavaScript,但 script 元素是 HTML 的一部分</p>
|
||||
|
||||
<h3>样式元素 (style)</h3>
|
||||
<p>虽然本页面不使用 CSS,但 style 元素是 HTML 的一部分</p>
|
||||
|
||||
<h3>标题元素 (title)</h3>
|
||||
<p>在 head 部分使用,定义页面标题</p>
|
||||
|
||||
<h3>元数据元素 (meta, link, base)</h3>
|
||||
<p>在 head 部分使用,提供文档元数据</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<h2>(十)页脚区域 (footer)</h2>
|
||||
<p>本页面展示了超过 100 个 HTML 元素,涵盖了现代浏览器支持的大部分元素。</p>
|
||||
<p>注意:某些元素(如 dialog)需要特定属性才能正确显示。</p>
|
||||
<p><a href="#">返回顶部</a></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
662
InfoGenie-frontend/public/toolbox/学习工具/HTML的div标签指南/index.html
Normal file
662
InfoGenie-frontend/public/toolbox/学习工具/HTML的div标签指南/index.html
Normal file
@@ -0,0 +1,662 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HTML div元素全面指南</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 3;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
color: #2c3e50;
|
||||
border-bottom: 2px solid #3498db;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
color: #2980b9;
|
||||
font-size: 2.5rem;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #3498db;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #2980b9;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.code-example {
|
||||
background: #2c3e50;
|
||||
color: #ecf0f1;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
font-family: 'Courier New', monospace;
|
||||
margin: 15px 0;
|
||||
overflow-x: auto;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #e74c3c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.demo-box {
|
||||
border: 2px dashed #3498db;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.demo-div {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.demo-item {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
flex-grow: 1;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
gap: 10px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
background: #9b59b6;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.flex-item {
|
||||
background: #2ecc71;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.table tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
display: flex;
|
||||
background: #2c3e50;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 12px 20px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: #3498db;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
background: #e74c3c;
|
||||
color: white;
|
||||
padding: 3px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.8rem;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #ddd;
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
background: #ecf0f1;
|
||||
color: #2c3e50;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
font-size: 0.9rem;
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-item {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1><i class="fas fa-cube"></i> HTML <span class="tag"><div></span> 元素全面指南</h1>
|
||||
<p><span class="tag"><div></span> 是 HTML 中最常用、最通用的容器元素。它是 "division"(分割)的缩写,用于将文档分割成独立的部分,没有特定的语义含义,主要用于布局和样式化。</p>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="main-content">
|
||||
<!-- 基础功能 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-bars"></i>-->
|
||||
<h2>1. 基础功能:作为通用容器</h2>
|
||||
</div>
|
||||
<p><span class="tag"><div></span> 是一个块级元素,用于将 HTML 文档划分为独立的区块,便于应用样式和布局。</p>
|
||||
|
||||
<div class="code-example">
|
||||
<div id="header">这是页头</div><br>
|
||||
<div id="main-content"><br>
|
||||
<div class="article">文章1</div><br>
|
||||
<div class="article">文章2</div><br>
|
||||
</div><br>
|
||||
<div id="footer">这是页脚</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-box">
|
||||
<h3>示例:div作为容器</h3>
|
||||
<div style="background-color: #e8f4f8; padding: 15px; margin-bottom: 10px; border-left: 4px solid #3498db;">
|
||||
这是一个带有样式的div容器
|
||||
</div>
|
||||
<div style="background-color: #f9f3e9; padding: 15px; border-left: 4px solid #e67e22;">
|
||||
这是另一个带有不同样式的div容器
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 布局功能 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-th-large"></i>-->
|
||||
<h2>2. 布局功能</h2>
|
||||
</div>
|
||||
<p>使用 <span class="tag"><div></span> 创建页面布局,结合 CSS 实现各种布局技术。</p>
|
||||
|
||||
<h3>2.1 传统浮动布局</h3>
|
||||
<div class="demo-box">
|
||||
<div style="overflow: hidden;">
|
||||
<div style="float: left; width: 30%; background: #3498db; color: white; padding: 15px; margin-right: 10px;">
|
||||
左侧边栏 (float: left)
|
||||
</div>
|
||||
<div style="float: right; width: 30%; background: #9b59b6; color: white; padding: 15px;">
|
||||
右侧边栏 (float: right)
|
||||
</div>
|
||||
<div style="background: #2ecc71; color: white; padding: 15px;">
|
||||
主要内容区域 (自适应宽度)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>2.2 Flexbox 布局</h3>
|
||||
<div class="flex-container demo-box">
|
||||
<div class="flex-item">Flex 项目 1</div>
|
||||
<div class="flex-item">Flex 项目 2</div>
|
||||
<div class="flex-item">Flex 项目 3</div>
|
||||
</div>
|
||||
|
||||
<h3>2.3 CSS Grid 布局</h3>
|
||||
<div class="demo-grid demo-box">
|
||||
<div class="grid-item">网格项目 1</div>
|
||||
<div class="grid-item">网格项目 2</div>
|
||||
<div class="grid-item">网格项目 3</div>
|
||||
<div class="grid-item">网格项目 4</div>
|
||||
<div class="grid-item">网格项目 5</div>
|
||||
<div class="grid-item">网格项目 6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分组与样式化 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-paint-brush"></i>-->
|
||||
<h2>3. 分组与样式化</h2>
|
||||
</div>
|
||||
<p>使用 <span class="tag"><div></span> 对相关元素进行分组,以便统一应用样式。</p>
|
||||
|
||||
<div class="demo-box">
|
||||
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px; margin-bottom: 15px;">
|
||||
<h3 style="color: white; margin-top: 0;">带背景渐变的分组</h3>
|
||||
<p>这个div包含标题和段落,并应用了渐变背景。</p>
|
||||
<button class="btn" style="background: rgba(255,255,255,0.2); border: 1px solid white;">了解更多</button>
|
||||
</div>
|
||||
|
||||
<div style="border: 2px solid #f39c12; padding: 15px; border-radius: 5px; background-color: #fff9e6;">
|
||||
<h3 style="color: #f39c12;">带边框的分组</h3>
|
||||
<p>这个div有自定义边框和背景色,用于突出显示内容。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 交互功能 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-mouse-pointer"></i>-->
|
||||
<h2>4. 交互功能</h2>
|
||||
</div>
|
||||
<p>通过 JavaScript 为 <span class="tag"><div></span> 添加交互功能。</p>
|
||||
|
||||
<div class="demo-box">
|
||||
<h3>4.1 点击事件</h3>
|
||||
<div id="clickable-div" style="background: #3498db; color: white; padding: 15px; border-radius: 5px; cursor: pointer; text-align: center; margin-bottom: 10px;">
|
||||
点击这个div,我会改变颜色和文本
|
||||
</div>
|
||||
|
||||
<h3>4.2 悬停效果</h3>
|
||||
<div id="hover-div" style="background: #2ecc71; color: white; padding: 15px; border-radius: 5px; text-align: center; transition: all 0.3s;">
|
||||
鼠标悬停在我上面试试
|
||||
</div>
|
||||
|
||||
<h3>4.3 显示/隐藏内容</h3>
|
||||
<button id="toggle-btn" class="btn">显示/隐藏内容</button>
|
||||
<div id="toggle-content" style="background: #f8f9fa; padding: 15px; border-radius: 5px; margin-top: 10px; display: none;">
|
||||
这个内容可以通过按钮显示或隐藏。这是通过切换div的display属性实现的。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模拟其他元素 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-shapes"></i>-->
|
||||
<h2>5. 模拟其他元素</h2>
|
||||
</div>
|
||||
<p><span class="tag"><div></span> 可以结合CSS模拟其他元素的功能。</p>
|
||||
|
||||
<div class="demo-box">
|
||||
<h3>5.1 模拟按钮</h3>
|
||||
<div class="btn" style="display: inline-block;">看起来像按钮的div</div>
|
||||
|
||||
<h3>5.2 模拟卡片</h3>
|
||||
<div style="border: 1px solid #ddd; border-radius: 8px; overflow: hidden; max-width: 300px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
|
||||
<div style="height: 150px; background: linear-gradient(to right, #3498db, #2c3e50);"></div>
|
||||
<div style="padding: 15px;">
|
||||
<h4 style="margin-top: 0;">卡片标题</h4>
|
||||
<p>这是一个使用div创建的卡片组件,具有图片区域和内容区域。</p>
|
||||
<div class="btn" style="padding: 8px 16px; font-size: 0.9rem;">查看详情</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>5.3 模拟表格</h3>
|
||||
<div class="table">
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); background: #3498db; color: white;">
|
||||
<div style="padding: 10px; font-weight: bold;">姓名</div>
|
||||
<div style="padding: 10px; font-weight: bold;">年龄</div>
|
||||
<div style="padding: 10px; font-weight: bold;">城市</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr);">
|
||||
<div style="padding: 10px; border-bottom: 1px solid #ddd;">张三</div>
|
||||
<div style="padding: 10px; border-bottom: 1px solid #ddd;">28</div>
|
||||
<div style="padding: 10px; border-bottom: 1px solid #ddd;">北京</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr);">
|
||||
<div style="padding: 10px;">李四</div>
|
||||
<div style="padding: 10px;">34</div>
|
||||
<div style="padding: 10px;">上海</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<!-- 属性总结 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-list-alt"></i>-->
|
||||
<h2>div 常用属性</h2>
|
||||
</div>
|
||||
<p><span class="tag"><div></span> 支持所有HTML全局属性:</p>
|
||||
<ul style="padding-left: 20px; margin-bottom: 15px;">
|
||||
<li><span class="highlight">id</span> - 唯一标识符</li>
|
||||
<li><span class="highlight">class</span> - 类名,用于CSS和JS选择</li>
|
||||
<li><span class="highlight">style</span> - 内联样式</li>
|
||||
<li><span class="highlight">title</span> - 提示文本</li>
|
||||
<li><span class="highlight">data-*</span> - 自定义数据属性</li>
|
||||
</ul>
|
||||
<p>以及所有HTML事件属性:</p>
|
||||
<ul style="padding-left: 20px;">
|
||||
<li><span class="highlight">onclick</span> - 点击事件</li>
|
||||
<li><span class="highlight">onmouseover</span> - 鼠标悬停</li>
|
||||
<li><span class="highlight">onload</span> - 加载完成</li>
|
||||
<li>等等...</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 最佳实践 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-check-circle"></i>-->
|
||||
<h2>使用最佳实践</h2>
|
||||
</div>
|
||||
<div class="alert alert-success">
|
||||
<p><i class="fas fa-lightbulb"></i> <strong>使用语义化元素替代</strong>:当有更合适的语义化元素时(如<header>、<section>、<article>等),应优先使用它们。</p>
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
<p><i class="fas fa-exclamation-triangle"></i> <strong>避免过度嵌套</strong>:减少不必要的div嵌套,保持HTML结构简洁。</p>
|
||||
</div>
|
||||
<div class="alert alert-success">
|
||||
<p><i class="fas fa-lightbulb"></i> <strong>合理使用类名</strong>:使用有意义的类名,遵循命名规范(如BEM)。</p>
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
<p><i class="fas fa-exclamation-triangle"></i> <strong>避免仅用于样式</strong>:尽量使用CSS而不是额外的div来实现纯样式效果。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 示例组件 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-code"></i>-->
|
||||
<h2>div 构建的组件</h2>
|
||||
</div>
|
||||
|
||||
<h3>导航菜单</h3>
|
||||
<div class="nav-menu">
|
||||
<a href="#" class="nav-item">首页</a>
|
||||
<a href="#" class="nav-item">产品</a>
|
||||
<a href="#" class="nav-item">服务</a>
|
||||
<a href="#" class="nav-item">关于我们</a>
|
||||
<a href="#" class="nav-item">联系</a>
|
||||
</div>
|
||||
|
||||
<h3>消息提示</h3>
|
||||
<div class="alert alert-success">
|
||||
<strong>成功!</strong> 操作已成功完成。
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
<strong>警告!</strong> 请检查您的输入。
|
||||
</div>
|
||||
|
||||
<h3>标签/徽章</h3>
|
||||
<div style="margin: 10px 0;">
|
||||
<span>通知 <span class="badge">5</span></span>
|
||||
<span style="margin-left: 15px;">消息 <span class="badge">12</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模态框 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- <i class="fas fa-window-maximize"></i>-->
|
||||
<h2>div 模态框示例</h2>
|
||||
</div>
|
||||
<p>使用div创建的模态对话框</p>
|
||||
<button id="open-modal" class="btn" style="width: 100%;">打开模态框</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模态框结构 -->
|
||||
<div id="modal-overlay" class="modal-overlay">
|
||||
<div class="modal">
|
||||
<h2>这是一个模态框</h2>
|
||||
<p>这个模态框完全使用div元素创建,通过CSS定位和JavaScript控制显示/隐藏。</p>
|
||||
<p>模态框是网页中常见的UI组件,通常用于显示重要信息或收集用户输入。</p>
|
||||
<div style="text-align: right; margin-top: 20px;">
|
||||
<button id="close-modal" class="btn">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© 2023 HTML div元素全面指南 | 使用单个HTML文件演示div元素的所有主要功能</p>
|
||||
<p>div是一个通用容器,没有特定语义,但却是现代网页布局的基石。</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 交互演示代码
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 1. 点击div改变颜色
|
||||
const clickableDiv = document.getElementById('clickable-div');
|
||||
let colorChanged = false;
|
||||
|
||||
clickableDiv.addEventListener('click', function() {
|
||||
if (!colorChanged) {
|
||||
this.style.background = '#e74c3c';
|
||||
this.innerHTML = '你点击了我!现在我是红色的';
|
||||
colorChanged = true;
|
||||
} else {
|
||||
this.style.background = '#3498db';
|
||||
this.innerHTML = '点击这个div,我会改变颜色和文本';
|
||||
colorChanged = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 悬停div效果
|
||||
const hoverDiv = document.getElementById('hover-div');
|
||||
|
||||
hoverDiv.addEventListener('mouseover', function() {
|
||||
this.style.background = '#27ae60';
|
||||
this.style.transform = 'scale(1.05)';
|
||||
this.innerHTML = '鼠标悬停中...';
|
||||
});
|
||||
|
||||
hoverDiv.addEventListener('mouseout', function() {
|
||||
this.style.background = '#2ecc71';
|
||||
this.style.transform = 'scale(1)';
|
||||
this.innerHTML = '鼠标悬停在我上面试试';
|
||||
});
|
||||
|
||||
// 3. 显示/隐藏内容
|
||||
const toggleBtn = document.getElementById('toggle-btn');
|
||||
const toggleContent = document.getElementById('toggle-content');
|
||||
let isVisible = false;
|
||||
|
||||
toggleBtn.addEventListener('click', function() {
|
||||
if (!isVisible) {
|
||||
toggleContent.style.display = 'block';
|
||||
isVisible = true;
|
||||
} else {
|
||||
toggleContent.style.display = 'none';
|
||||
isVisible = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 4. 模态框控制
|
||||
const openModalBtn = document.getElementById('open-modal');
|
||||
const closeModalBtn = document.getElementById('close-modal');
|
||||
const modalOverlay = document.getElementById('modal-overlay');
|
||||
|
||||
openModalBtn.addEventListener('click', function() {
|
||||
modalOverlay.style.display = 'flex';
|
||||
});
|
||||
|
||||
closeModalBtn.addEventListener('click', function() {
|
||||
modalOverlay.style.display = 'none';
|
||||
});
|
||||
|
||||
// 点击模态框外部关闭
|
||||
modalOverlay.addEventListener('click', function(e) {
|
||||
if (e.target === modalOverlay) {
|
||||
modalOverlay.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
65
InfoGenie-frontend/public/toolbox/学习工具/入党申请书/index.html
Normal file
65
InfoGenie-frontend/public/toolbox/学习工具/入党申请书/index.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>入党申请书</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
background-color: #f4f4f9;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
text-indent: 2em;
|
||||
margin-bottom: 1em;
|
||||
color: #555;
|
||||
}
|
||||
.signature {
|
||||
text-align: right;
|
||||
margin-top: 2em;
|
||||
}
|
||||
.signature p {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>入党申请书</h1>
|
||||
<p>敬爱的党组织:</p>
|
||||
<p>我志愿申请加入中国共产党,并愿意为党的事业奋斗终身。中国共产党是一个具有坚强意志、不断克服困难并始终走在正确道路上的伟大政党。党带领着工农阶级开辟了一条光辉的前进道路,让无数像我一样来自普通工农家庭的人民看到了希望和未来的美好。加入中国共产党一直是我内心最崇高的理想与追求。</p>
|
||||
<p>自中国共产党成立以来,党始终代表最广大人民的根本利益,全心全意为人民服务。正如党章所述,中国共产党是中国工人阶级的先锋队,是中国各族人民利益的忠实代表,是中国社会主义事业的领导核心。党以马克思列宁主义、毛泽东思想、邓小平理论、三个代表重要思想、科学发展观以及习近平新时代中国特色社会主义思想为指导,带领全国各族人民经过不懈努力,走出了一条具有中国特色的社会主义道路,取得了伟大的成就。</p>
|
||||
<p>党的根本宗旨是全心全意为人民服务,这一点从小便深深印在我的心中。自我开始接触政治以来,便逐步认识到,党不仅来自于人民,更是植根于人民,服务于人民。每一个共产党员都肩负着为人民服务的责任与使命,这是无产阶级政党的性质决定的,也是马克思主义建党学说的核心原则。中国共产党自成立以来,始终把人民的利益放在第一位,所有的政策与方针都以满足人民的需求为出发点。因此,我更加坚定了加入党组织的决心,愿为实现全心全意为人民服务的目标贡献自己的一切。</p>
|
||||
<p>自初中加入中国共青团以来,我逐渐意识到,作为一名共青团员,我的身份不仅仅是一名学生,更意味着肩负着宣传党的路线、方针和政策的责任。作为共青团员,我始终以严格的标准要求自己,在学习上刻苦努力,积极参加各种有益的社会实践活动,力求在各个方面发挥模范带头作用。我始终未曾忘记我的团员身份,并时刻保持自我约束与提高。</p>
|
||||
<p>通过共青团的组织生活,我逐步学习和理解了党的理论知识,思想觉悟有了进一步的提高。我明白,党是我们国家的中流砥柱,党员是为人民服务的先锋队。因此,在今后的学习和工作中,我一直以党员的标准严格要求自己,并将全心全意为人民服务作为自己行动的指导思想。</p>
|
||||
<p>我小时候看到爷爷佩戴着党颁发的勋章时,总是充满了好奇和敬畏。随着爷爷的讲述,我逐渐了解了中国共产党带领人民走过艰苦的革命斗争,赢得自由和安定的光辉历史,也认识到党在建设社会主义现代化国家中所作出的巨大贡献。在爷爷年幼时,他们生活在贫困和动荡中,连电灯都没有,而如今,老人们已经能够享受现代科技带来的便利。这一切都是党带领我们创造的。如果没有党的领导,就没有我们今天的幸福生活。因此,我更加坚定要加入党组织,为国家和人民的伟大事业贡献自己的青春和力量。</p>
|
||||
<p>进入大学后,我逐渐适应了校园生活和社会这个大家庭,并认识到自己在思想、生活和学习中还存在一些不足。比如,在一些场合中,我还无法充分展现自己的能力,表现得有些不够自信。不过,我通过参加学校的各类比赛和社团活动,积极提升自我。我清楚地知道,要成为一名合格的党员,不仅要有坚定的理想信念,还要在日常生活中克服自身的弱点,不断提高个人的综合素质,增强自己的社会责任感和服务意识。</p>
|
||||
<p>通过大学生活的磨练和党课的学习,我更加明确了加入中国共产党的决心。在参与党课学习的过程中,我不仅对党的基本理论有了更深刻的理解,还通过与优秀党员的接触,学会了如何在实际生活中践行党的宗旨。我发现,党员们无论是在学习、工作还是生活中,都始终保持着高度的责任感和使命感,时刻以全心全意为人民服务为己任。这让我深受鼓舞,也更加坚定了我入党的决心。</p>
|
||||
<p>我深知,成为一名党员意味着要承担更大的责任与使命。这不仅是一种光荣的身份,更是一份崇高的责任。党员不仅要在思想上与党保持高度一致,更要在实践中始终发挥模范带头作用。因此,在今后的学习和生活中,我将继续以党员的标准严格要求自己,努力克服自己的缺点和不足,不断提高个人修养,增强理论知识,做到理论联系实际,关心国内外大事,关注党和政府的重大方针政策。同时,我会更加注重明辨是非,树立正确的人生观和价值观,保持积极乐观的生活态度,做到不以善小而不为,不以恶小而为之。从小事做起,从点滴做起,时刻将党和人民的利益放在首位。</p>
|
||||
<p>除了严格要求自己、为他人服务以外,我还将通过自身的实际行动去影响他人,带动更多的人关心国家、集体和他人的利益,争取早日从思想上、行动上入党,进而实现从组织上加入中国共产党的愿望。</p>
|
||||
<p>如果党组织认为我还未完全符合党员的要求,我也不会因此气馁。我将继续严格要求自己,积极接受党员和群众的监督与帮助,努力克服自己的不足,不断完善自我,争取早日达到党组织的要求,真正做到在各方面发挥党员的先锋模范作用。我相信,在党组织的帮助与指导下,我一定能够不断进步,最终实现我加入中国共产党的愿望。</p>
|
||||
<p>请党组织在实践中考验我!</p>
|
||||
<div class="signature">
|
||||
<p>此致</p>
|
||||
<p>敬礼!</p>
|
||||
<p>申请人:xxx</p>
|
||||
<p>2024年xx月xx日</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
大学生入党申请书 2 敬爱的党组织: 在我很小的时候我就开始接受长辈的教导,说我将来长大后一定要加入中国共产党,当时我还不明白什么意思,只能似懂非懂的答应,他们都夸我是好孩子。当我渐渐的长大,通过在学校的学习,我知道了什么是中国共产党,我就坚定的加入中国共产党的决心。 作为一名的大学生,在一年多的大学生活中使我对党的认识有了进一步的加深。党是工人阶级的先进分子组成的,是工人阶级及广大劳动群众利益的忠实代表。党在制定政策、路线、方针时都无不围绕着人民群众的根本利益,切切实实地为人民群众谋求的福利;党员为人民群众鞠躬尽瘁、死而后已,坚决把人民利益放在个人利益之上,个人利益服从人民利益。 我立志加入中国共产党。我会以全心全意为人民服务为思想核心,以忧人民之所忧,急人民之所急,为座右铭去指导我的行动,用我有限的智慧去为广大人民群众谋求福利,吃苦在前,享乐在后,克己奉公,多作贡献。当然,在不断追求思想进步的同时,我时刻铭记自己是一名学生,学习是我的天职。 本人深知自己距离党的要求还有不少差距,存在不少缺点和不足,如理论知识掌握不扎实,政治素养不高等等。但是,我诚实勤奋,自尊、自强、自爱,与同学关系良好,各方面的能力正在逐步提高。我也将以党员的标准严格要求自己,自觉接受党组织和同学们的监督和帮助,坚持不懈地克服自身的缺点,弥补自己的不足,积极向党组织靠拢,争取早日加入伟大的中国共产党。 我在工作上和学习上还体现出了不足的方面,如工作上处理问题不够成熟,在学习上还存在方法上的错误等。我希望在日后的生活中能改正缺点,完善自己。也希望在党支部的生活中能得到思想和政治觉悟上的进一步提高,个人综合素质也不断提高,得到全面的发展。 如果党组织批准了我的入党申请书,使我成为正式党员,我一定会更加严格的要求自己,时时刻刻为同学做榜样,严于律己,把周围的环境带好,这我还是有信心的,我相信我有这个能力和魅力。 如果党组织没有批准我的入党申请书,说明我的条件还不成熟,我做的还是达不到党的要求。我一定不会灰心的,也不会自暴自弃,我智慧更加严格的要求自己,加强文化学习,提高个人素质,直到达到党组织的要求为止,我相信我会做到的,也请党组织相信我。 此致 敬礼! 申请人:xxx 20xx年xx月xx日
|
||||
@@ -0,0 +1 @@
|
||||
敬爱的党组织: 我申请加入中国共产党。 我是一名大学生,一直是沐浴在党的光辉下成长起来的青年人,在回顾自己成长的过程,心中感慨万分。我发现在我的成长中,无时无刻都能感受到党温暖的光辉,因为我每一次的阶段性进步,都蕴含着党对我的教育和培养。 作为一名合格的大学生就要有一个坚定的信仰,而我的信仰就是加入中国共产党,实现共产主义,这不仅仅是中国共产党崇高的理想的价值追求。同时,也是我们全中国人民的共同理想。而大学生作为国家的顶梁柱,是实现民族富强的中坚力量,是我国社会主义现在代化建设的先锋,作为大学生的我更应把实现共产主义作为理想,把加入中国共产党作为我的信仰。 在我回顾自己的学习生涯时,过去真的是拼尽了很大的努力,还留下了一些遗憾,我知道这些都是相应的付出,这些遗憾和代价让我迎来了大学美好的生活!在大学里,不仅要继续努力的学习,我还要通过不断的努力,争取加入我向往已久的中国共产党。 作为一名大学生,同时还作为一名共青团员,我一直都有严格的要求自己,并用实际的行动去证明我作为共青团员的价值,随着时间的推移,我的文化知识在不断的增长,而对党的认识也越发深刻,加入到党组织的愿望也越发强烈。为了有资格加入党组织,在平日里,我不断努力加强自身的修养,经常学习党的理论,期望做到用党性来武装自己的头脑,做一名优秀的共青团员。 中国共产党这个光荣而又神圣的组织,从1921年建党到今天,我们伟大的党已经走过了100年的光辉历程。其中经历了长期的革命斗争,终于建设了适合中国的具有中国特色的社会主义道路,并逐步实现了社会主义现代化。 我热爱我们伟大的党,因为她是中国工人阶级的先锋队,是中国各族人民利益的忠实代表,是中国特色社会主义事业的领导核心,她始终代表中国先进生产力的发展要求,代表中国先进文化的发展方向,代表中国最广大人民的根本利益,为实现国家和人民的根本利益而不懈奋斗。 在学习中,我也一直关注着党的一切,知道党的十九大是全面建成小康社会关键阶段和中国特色社会主义发展关键时期的一次十分重要的大会,对鼓舞和动员全国各族人民继续推进全面建成小康社会、坚持和发展中国特色社会主义具有重大意义。 敬爱的党组织,作为新时代大学生的我,今天郑重地递上入党的申请书,这将会是我人生精彩历程中最最庄严神圣的一件事。如果党组织在严格审查后能够将我的申请批准,我一定会认真履行党员应尽的义务,在往后的生活和学习中一定严格要求自己,并接受党组织和同学们的监督。做到生活中严于律己,学习中勤奋进取,尽的努力做一名合格且先进的共产党员,为党的事业和我国的社会主义现代化事业贡献我毕生的精力和热血。 即使组织上认为我尚未符合一个党员的资格,但我也将按党员的标准,严格要求自己,积极参加院、系、班的各项活动,充分发挥自己的特长,真正起到先锋模范作用。争取早日加入党组织。请党组织考验我! 此致 敬礼! 申请人:xxx 20xx年xx月xx日
|
||||
@@ -0,0 +1 @@
|
||||
尊敬的党组织: 我志愿加入中国共产党。中国共产党是一个有着坚强意志的,不断克服进步的政党。他带领着工农阶级走出了一条正确而光辉的道路。让众多类似我所处的工农家庭带来希望和笑容。所以加入中国共产党是我一直以来的梦想和追求! 全心全意为人民服务是我们党的根本宗旨,是由无产阶级政党的性质决定的。这自从接触政治就牢牢的印在我的脑海里。党来自人民,植根于人民,服务于人民。正是如此,每一个共产党员都肩负着一个服务于人的责任与义务。同时这也是马克思主义建党的基本原则,是中国共产党一贯坚持的根本宗旨。 自从初中加入了共青团,就让我感觉一个身份的改变。在作为学生的同时还肩负着一份责任。为宣传执行党的路线,方针和政策。不断提高为人民服务的本领。而且我一直未忘记我的这层身份。所以我好好学习,参加各种活动,全面发展,争取在各个方面起到一个模范带头作用。想起小时候,每次看到爷爷佩戴起那枚党颁发的勋章时总会昂起胸膛陷入回忆我总好奇,不解。后来爷爷总会更我讲述着他那历经岁月的记忆。中国共产党带领着人们获得自由与安定,在后期的建设中不断的积极进取,努力创新终于有了如今的繁荣。在爷爷上私塾那会连电都没有,但是在他的晚年却能用上手机。在我认为这些都是党带领我们创造出来的,没有党就不会有如今的生活!因此我会更加努力学习为党,为国家,为人民奉献出自己的一份力量。 刚步入大学,渐渐的开始步入社会这个大家庭,我也渐渐发现自己的缺点,尤其是在某些场合放不开,但是我已经在不断去纠正。我积极主动的参加了学校的一些比赛和社团活动。 在今后的学习生活中,我更会以党员的标准严格要求自己,努力克服自己的弱点和缺点,不断的提高自己。继续加强理论知识的学习,同时理论联系实际,关心国内外大事,关注党和政府的重大方针和政策,同时分清是非,正确认识各种社会现象。树立正确的人生观和价值观,积极乐观地对待生活。坚持不以善小而不为,不以恶小而为之,从小事做起,从点滴做起。除了严格要求自己、为他人服务以外,还要用自己的言行去影响他人,带动大家来关心国家、集体和他人的利益,争取先从思想上入党,进而从组织上入党。 如果组织暂时没有接受我的请求,我也不会气馁,我会自觉接受党员和群众的帮助和监督,努力克服自己的缺点,进一步注意自己的言行,加强组织性、纪律性,真正达到党员所要求的标准,起到模范带头作用。我相信在组织的帮助和指导下,我一定会加入党组织!请组织在实践中考验我。 此致 敬礼! 申请人:xxx 20xx年xx月xx日
|
||||
File diff suppressed because one or more lines are too long
21
InfoGenie-frontend/public/toolbox/学习工具/入党申请书/入党申请书范文5.txt
Normal file
21
InfoGenie-frontend/public/toolbox/学习工具/入党申请书/入党申请书范文5.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
入党申请书
|
||||
|
||||
敬爱的党组织:
|
||||
我志愿申请加入中国共产党,并愿意为党的事业奋斗终身。中国共产党是一个具有坚强意志、不断克服困难并始终走在正确道路上的伟大政党。党带领着工农阶级开辟了一条光辉的前进道路,让无数像我一样来自普通工农家庭的人民看到了希望和未来的美好。加入中国共产党一直是我内心最崇高的理想与追求。
|
||||
自中国共产党成立以来,党始终代表最广大人民的根本利益,全心全意为人民服务。正如党章所述,中国共产党是中国工人阶级的先锋队,是中国各族人民利益的忠实代表,是中国社会主义事业的领导核心。党以马克思列宁主义、毛泽东思想、邓小平理论、三个代表重要思想、科学发展观以及习近平新时代中国特色社会主义思想为指导,带领全国各族人民经过不懈努力,走出了一条具有中国特色的社会主义道路,取得了伟大的成就。
|
||||
党的根本宗旨是全心全意为人民服务,这一点从小便深深印在我的心中。自我开始接触政治以来,便逐步认识到,党不仅来自于人民,更是植根于人民,服务于人民。每一个共产党员都肩负着为人民服务的责任与使命,这是无产阶级政党的性质决定的,也是马克思主义建党学说的核心原则。中国共产党自成立以来,始终把人民的利益放在第一位,所有的政策与方针都以满足人民的需求为出发点。因此,我更加坚定了加入党组织的决心,愿为实现全心全意为人民服务的目标贡献自己的一切。
|
||||
自初中加入中国共青团以来,我逐渐意识到,作为一名共青团员,我的身份不仅仅是一名学生,更意味着肩负着宣传党的路线、方针和政策的责任。作为共青团员,我始终以严格的标准要求自己,在学习上刻苦努力,积极参加各种有益的社会实践活动,力求在各个方面发挥模范带头作用。我始终未曾忘记我的团员身份,并时刻保持自我约束与提高。
|
||||
通过共青团的组织生活,我逐步学习和理解了党的理论知识,思想觉悟有了进一步的提高。我明白,党是我们国家的中流砥柱,党员是为人民服务的先锋队。因此,在今后的学习和工作中,我一直以党员的标准严格要求自己,并将全心全意为人民服务作为自己行动的指导思想。
|
||||
我小时候看到爷爷佩戴着党颁发的勋章时,总是充满了好奇和敬畏。随着爷爷的讲述,我逐渐了解了中国共产党带领人民走过艰苦的革命斗争,赢得自由和安定的光辉历史,也认识到党在建设社会主义现代化国家中所作出的巨大贡献。在爷爷年幼时,他们生活在贫困和动荡中,连电灯都没有,而如今,老人们已经能够享受现代科技带来的便利。这一切都是党带领我们创造的。如果没有党的领导,就没有我们今天的幸福生活。因此,我更加坚定要加入党组织,为国家和人民的伟大事业贡献自己的青春和力量。
|
||||
进入大学后,我逐渐适应了校园生活和社会这个大家庭,并认识到自己在思想、生活和学习中还存在一些不足。比如,在一些场合中,我还无法充分展现自己的能力,表现得有些不够自信。不过,我通过参加学校的各类比赛和社团活动,积极提升自我。我清楚地知道,要成为一名合格的党员,不仅要有坚定的理想信念,还要在日常生活中克服自身的弱点,不断提高个人的综合素质,增强自己的社会责任感和服务意识。
|
||||
通过大学生活的磨练和党课的学习,我更加明确了加入中国共产党的决心。在参与党课学习的过程中,我不仅对党的基本理论有了更深刻的理解,还通过与优秀党员的接触,学会了如何在实际生活中践行党的宗旨。我发现,党员们无论是在学习、工作还是生活中,都始终保持着高度的责任感和使命感,时刻以全心全意为人民服务为己任。这让我深受鼓舞,也更加坚定了我入党的决心。
|
||||
我深知,成为一名党员意味着要承担更大的责任与使命。这不仅是一种光荣的身份,更是一份崇高的责任。党员不仅要在思想上与党保持高度一致,更要在实践中始终发挥模范带头作用。因此,在今后的学习和生活中,我将继续以党员的标准严格要求自己,努力克服自己的缺点和不足,不断提高个人修养,增强理论知识,做到理论联系实际,关心国内外大事,关注党和政府的重大方针政策。同时,我会更加注重明辨是非,树立正确的人生观和价值观,保持积极乐观的生活态度,做到不以善小而不为,不以恶小而为之。从小事做起,从点滴做起,时刻将党和人民的利益放在首位。
|
||||
除了严格要求自己、为他人服务以外,我还将通过自身的实际行动去影响他人,带动更多的人关心国家、集体和他人的利益,争取早日从思想上、行动上入党,进而实现从组织上加入中国共产党的愿望。
|
||||
如果党组织认为我还未完全符合党员的要求,我也不会因此气馁。我将继续严格要求自己,积极接受党员和群众的监督与帮助,努力克服自己的不足,不断完善自我,争取早日达到党组织的要求,真正做到在各方面发挥党员的先锋模范作用。我相信,在党组织的帮助与指导下,我一定能够不断进步,最终实现我加入中国共产党的愿望。
|
||||
请党组织在实践中考验我!
|
||||
此致
|
||||
|
||||
敬礼!
|
||||
申请人:xxx
|
||||
|
||||
2024年xx月xx日
|
||||
834
InfoGenie-frontend/public/toolbox/学习工具/文档类网页模板/index.html
Normal file
834
InfoGenie-frontend/public/toolbox/学习工具/文档类网页模板/index.html
Normal file
@@ -0,0 +1,834 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>专业文档模板 - 设计规范</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-dark: #2c3e50;
|
||||
--primary-blue: #3498db;
|
||||
--primary-light: #f5f7fa;
|
||||
--secondary-light: #c3cfe2;
|
||||
--card-bg: #ffffff;
|
||||
--code-bg: #2c3e50;
|
||||
--success: #2ecc71;
|
||||
--warning: #f39c12;
|
||||
--danger: #e74c3c;
|
||||
--info: #3498db;
|
||||
--text-primary: #333333;
|
||||
--text-secondary: #666666;
|
||||
--border-radius: 10px;
|
||||
--shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
|
||||
--shadow-hover: 0 15px 30px rgba(0, 0, 0, 0.15);
|
||||
--transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-primary);
|
||||
background: linear-gradient(135deg, var(--primary-light) 0%, var(--secondary-light) 100%);
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
flex: 0 0 280px;
|
||||
background-color: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 25px;
|
||||
height: fit-content;
|
||||
transition: var(--transition);
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.sidebar h3 {
|
||||
color: var(--primary-dark);
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid var(--primary-blue);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
list-style: none;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.nav-menu li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.nav-menu a {
|
||||
display: block;
|
||||
padding: 12px 15px;
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
border-radius: 8px;
|
||||
transition: var(--transition);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-menu a:hover, .nav-menu a.active {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.nav-menu a i {
|
||||
margin-right: 10px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.badge-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 主内容区样式 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 页眉样式 */
|
||||
header {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-area h1 {
|
||||
background: linear-gradient(90deg, var(--primary-dark), var(--primary-blue));
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.title-area p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: var(--shadow-hover);
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
color: var(--primary-dark);
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid rgba(52, 152, 219, 0.3);
|
||||
font-weight: 600;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
color: var(--primary-blue);
|
||||
margin: 20px 0 10px;
|
||||
font-weight: 600;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* 代码块样式 */
|
||||
.code-block {
|
||||
background-color: var(--code-bg);
|
||||
color: #ecf0f1;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 0.95rem;
|
||||
overflow-x: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.code-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
color: #bdc3c7;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: #bdc3c7;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.code-block code {
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* UI组件样式 */
|
||||
.demo-area {
|
||||
background-color: rgba(245, 247, 250, 0.5);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: var(--transition);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: var(--success);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background-color: #27ae60;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background-color: var(--warning);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-warning:hover {
|
||||
background-color: #e67e22;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: var(--danger);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: #c0392b;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background-color: transparent;
|
||||
border: 2px solid var(--primary-blue);
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background-color: var(--primary-blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px 20px;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
border-left: 5px solid;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: rgba(46, 204, 113, 0.1);
|
||||
border-left-color: var(--success);
|
||||
color: #27ae60;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: rgba(243, 156, 18, 0.1);
|
||||
border-left-color: var(--warning);
|
||||
color: #d35400;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background-color: rgba(231, 76, 60, 0.1);
|
||||
border-left-color: var(--danger);
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
border-left-color: var(--primary-blue);
|
||||
color: #2980b9;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 5px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-primary {
|
||||
background-color: rgba(52, 152, 219, 0.15);
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background-color: rgba(46, 204, 113, 0.15);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background-color: rgba(243, 156, 18, 0.15);
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background-color: rgba(231, 76, 60, 0.15);
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
/* 页脚样式 */
|
||||
footer {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 30px;
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: var(--primary-blue);
|
||||
text-decoration: none;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1024px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex: 1;
|
||||
position: static;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sidebar h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.nav-menu li {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-menu a {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
header, .card, footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.title-area h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.badge-container {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.title-area h1 {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 18px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-menu li {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-menu a {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 辅助类 */
|
||||
.highlight {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
color: var(--primary-blue);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
margin: 25px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 侧边栏 -->
|
||||
<aside class="sidebar">
|
||||
<h3><i class="fas fa-book"></i> 文档导航</h3>
|
||||
<ul class="nav-menu">
|
||||
<li><a href="#introduction" class="active"><i class="fas fa-home"></i> 介绍</a></li>
|
||||
<li><a href="#getting-started"><i class="fas fa-rocket"></i> 快速开始</a></li>
|
||||
<li><a href="#components"><i class="fas fa-cube"></i> UI 组件</a></li>
|
||||
<li><a href="#layout"><i class="fas fa-th-large"></i> 布局系统</a></li>
|
||||
<li><a href="#customization"><i class="fas fa-palette"></i> 自定义</a></li>
|
||||
<li><a href="#examples"><i class="fas fa-code"></i> 代码示例</a></li>
|
||||
</ul>
|
||||
|
||||
<h3><i class="fas fa-tags"></i> 标签分类</h3>
|
||||
<div class="badge-container">
|
||||
<span class="badge badge-primary">HTML</span>
|
||||
<span class="badge badge-success">CSS</span>
|
||||
<span class="badge badge-warning">JavaScript</span>
|
||||
<span class="badge badge-danger">响应式</span>
|
||||
<span class="badge badge-primary">设计规范</span>
|
||||
<span class="badge badge-success">组件</span>
|
||||
<span class="badge badge-warning">模板</span>
|
||||
<span class="badge badge-danger">文档</span>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="main-content">
|
||||
<!-- 标题区 -->
|
||||
<header class="title-area">
|
||||
<h1>专业文档网页模板</h1>
|
||||
<p>基于现代设计规范的响应式文档模板,采用柔和的蓝白色渐变背景和卡片式布局,提供优秀的阅读和开发体验。</p>
|
||||
</header>
|
||||
|
||||
<!-- 卡片1: 介绍 -->
|
||||
<section id="introduction" class="card">
|
||||
<h2><i class="fas fa-info-circle"></i> 模板介绍</h2>
|
||||
<p>这是一个专业文档类网页模板,完全遵循以下设计规范:</p>
|
||||
<ul>
|
||||
<li>整体采用柔和的蓝白色渐变背景(<span class="highlight">#f5f7fa</span> 到 <span class="highlight">#c3cfe2</span>)</li>
|
||||
<li>使用卡片式布局,卡片有圆角、阴影和悬停上浮效果</li>
|
||||
<li>标题采用<span class="highlight">#2c3e50</span>到<span class="highlight">#3498db</span>的蓝色系,有清晰的层级关系</li>
|
||||
<li>代码块使用深色背景(<span class="highlight">#2c3e50</span>)和等宽字体</li>
|
||||
<li>包含响应式侧边栏和主内容区布局</li>
|
||||
<li>集成常用UI组件:按钮、警告框、徽章、导航菜单</li>
|
||||
<li>所有交互都有平滑的过渡效果</li>
|
||||
<li>移动端适配:单列布局,适当调整内边距</li>
|
||||
</ul>
|
||||
|
||||
<div class="demo-area">
|
||||
<h3>设计特点演示</h3>
|
||||
<p>将鼠标悬停在卡片上体验上浮效果,或尝试调整浏览器窗口大小查看响应式布局变化。</p>
|
||||
<div class="button-group">
|
||||
<button class="btn btn-primary">主要按钮</button>
|
||||
<button class="btn btn-success">成功按钮</button>
|
||||
<button class="btn btn-warning">警告按钮</button>
|
||||
<button class="btn btn-danger">危险按钮</button>
|
||||
<button class="btn btn-outline">轮廓按钮</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 卡片2: UI组件 -->
|
||||
<section id="components" class="card">
|
||||
<h2><i class="fas fa-cubes"></i> UI 组件展示</h2>
|
||||
<p>本模板内置了一系列常用UI组件,所有组件都遵循统一的设计语言。</p>
|
||||
|
||||
<h3>警告框组件</h3>
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-check-circle"></i> <strong>成功提示:</strong> 操作已成功完成。
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> <strong>警告提示:</strong> 此操作可能带来风险。
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-times-circle"></i> <strong>错误提示:</strong> 操作过程中发生了错误。
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle"></i> <strong>信息提示:</strong> 这是一条普通信息提示。
|
||||
</div>
|
||||
|
||||
<h3>徽章组件</h3>
|
||||
<p>徽章用于显示状态、分类或计数:</p>
|
||||
<div class="badge-container">
|
||||
<span class="badge badge-primary">主要徽章</span>
|
||||
<span class="badge badge-success">成功徽章</span>
|
||||
<span class="badge badge-warning">警告徽章</span>
|
||||
<span class="badge badge-danger">危险徽章</span>
|
||||
<span class="badge badge-primary">计数 <span class="highlight">12</span></span>
|
||||
<span class="badge badge-success">新功能</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 卡片3: 代码示例 -->
|
||||
<section id="examples" class="card">
|
||||
<h2><i class="fas fa-code"></i> 代码示例</h2>
|
||||
<p>以下展示了代码块的设计样式,支持多种编程语言语法高亮。</p>
|
||||
|
||||
<h3>HTML结构示例</h3>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>index.html</span>
|
||||
<button class="copy-btn">复制代码</button>
|
||||
</div>
|
||||
<code>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>专业文档模板</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<aside class="sidebar">
|
||||
<!-- 侧边栏内容 -->
|
||||
</aside>
|
||||
<main class="main-content">
|
||||
<!-- 主内容区域 -->
|
||||
<div class="card">
|
||||
<h2>卡片标题</h2>
|
||||
<p>卡片内容描述</p>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<h3>CSS样式示例</h3>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>styles.css</span>
|
||||
<button class="copy-btn">复制代码</button>
|
||||
</div>
|
||||
<code>
|
||||
/* 卡片设计 */
|
||||
.card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<h3>JavaScript交互示例</h3>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>script.js</span>
|
||||
<button class="copy-btn">复制代码</button>
|
||||
</div>
|
||||
<code>
|
||||
// 复制代码功能
|
||||
document.querySelectorAll('.copy-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const code = this.parentElement.nextElementSibling.textContent;
|
||||
navigator.clipboard.writeText(code);
|
||||
|
||||
// 提示用户
|
||||
const originalText = this.textContent;
|
||||
this.textContent = '已复制!';
|
||||
|
||||
setTimeout(() => {
|
||||
this.textContent = originalText;
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
// 侧边栏导航激活状态
|
||||
document.querySelectorAll('.nav-menu a').forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
document.querySelectorAll('.nav-menu a').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
this.classList.add('active');
|
||||
});
|
||||
});
|
||||
</code>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 卡片4: 响应式布局 -->
|
||||
<section id="layout" class="card">
|
||||
<h2><i class="fas fa-mobile-alt"></i> 响应式布局</h2>
|
||||
<p>本模板采用移动优先的响应式设计,在不同设备上都能提供优秀的用户体验。</p>
|
||||
|
||||
<div class="demo-area">
|
||||
<h3>响应式断点</h3>
|
||||
<p>模板定义了以下响应式断点:</p>
|
||||
<ul>
|
||||
<li><span class="highlight">≥1024px</span>: 桌面布局,侧边栏+主内容并排</li>
|
||||
<li><span class="highlight">768px-1023px</span>: 平板布局,侧边栏与主内容垂直排列</li>
|
||||
<li><span class="highlight">≤767px</span>: 移动端布局,单列布局,适当调整内边距</li>
|
||||
</ul>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<h3>当前设备预览</h3>
|
||||
<p>尝试调整浏览器窗口大小,观察布局变化:</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 15px; margin-top: 15px;">
|
||||
<div style="flex: 1; min-width: 200px; background: linear-gradient(90deg, var(--primary-blue), #2c3e50); color: white; padding: 15px; border-radius: 8px; text-align: center;">
|
||||
<i class="fas fa-desktop fa-2x"></i>
|
||||
<p>桌面端布局</p>
|
||||
</div>
|
||||
<div style="flex: 1; min-width: 200px; background: linear-gradient(90deg, var(--success), #27ae60); color: white; padding: 15px; border-radius: 8px; text-align: center;">
|
||||
<i class="fas fa-tablet-alt fa-2x"></i>
|
||||
<p>平板端布局</p>
|
||||
</div>
|
||||
<div style="flex: 1; min-width: 200px; background: linear-gradient(90deg, var(--warning), #e67e22); color: white; padding: 15px; border-radius: 8px; text-align: center;">
|
||||
<i class="fas fa-mobile-alt fa-2x"></i>
|
||||
<p>移动端布局</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer>
|
||||
<p>© 2023 专业文档模板 | 设计规范演示</p>
|
||||
<div class="footer-links">
|
||||
<a href="#"><i class="fab fa-github"></i> GitHub</a>
|
||||
<a href="#"><i class="fas fa-book"></i> 文档</a>
|
||||
<a href="#"><i class="fas fa-question-circle"></i> 帮助</a>
|
||||
<a href="#"><i class="fas fa-envelope"></i> 联系</a>
|
||||
</div>
|
||||
<p>遵循现代网页设计规范,提供优秀的用户体验和开发体验。</p>
|
||||
<p style="margin-top: 15px; font-size: 0.9rem; color: #999;">最后更新: 2023年10月</p>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 复制代码功能
|
||||
document.querySelectorAll('.copy-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const code = this.parentElement.nextElementSibling.textContent;
|
||||
navigator.clipboard.writeText(code);
|
||||
|
||||
// 提示用户
|
||||
const originalText = this.textContent;
|
||||
this.textContent = '已复制!';
|
||||
this.style.backgroundColor = 'rgba(46, 204, 113, 0.3)';
|
||||
|
||||
setTimeout(() => {
|
||||
this.textContent = originalText;
|
||||
this.style.backgroundColor = '';
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
// 侧边栏导航激活状态
|
||||
document.querySelectorAll('.nav-menu a').forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
if (this.getAttribute('href').startsWith('#')) {
|
||||
e.preventDefault();
|
||||
document.querySelectorAll('.nav-menu a').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
this.classList.add('active');
|
||||
|
||||
// 滚动到对应区域
|
||||
const targetId = this.getAttribute('href');
|
||||
const targetElement = document.querySelector(targetId);
|
||||
if (targetElement) {
|
||||
window.scrollTo({
|
||||
top: targetElement.offsetTop - 20,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 卡片悬停效果增强
|
||||
document.querySelectorAll('.card').forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-10px)';
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
this.style.transform = 'translateY(0)';
|
||||
});
|
||||
});
|
||||
|
||||
// 移动端侧边栏折叠功能
|
||||
const sidebar = document.querySelector('.sidebar');
|
||||
const navMenu = document.querySelector('.nav-menu');
|
||||
|
||||
// 检测屏幕宽度,为移动端添加折叠功能
|
||||
function checkScreenSize() {
|
||||
if (window.innerWidth <= 768) {
|
||||
// 创建折叠按钮
|
||||
if (!document.querySelector('.collapse-btn')) {
|
||||
const collapseBtn = document.createElement('button');
|
||||
collapseBtn.className = 'btn btn-primary collapse-btn';
|
||||
collapseBtn.innerHTML = '<i class="fas fa-bars"></i> 导航菜单';
|
||||
collapseBtn.style.width = '100%';
|
||||
collapseBtn.style.marginBottom = '15px';
|
||||
|
||||
sidebar.insertBefore(collapseBtn, navMenu);
|
||||
|
||||
collapseBtn.addEventListener('click', function() {
|
||||
navMenu.style.display = navMenu.style.display === 'none' ? 'flex' : 'none';
|
||||
this.innerHTML = navMenu.style.display === 'none'
|
||||
? '<i class="fas fa-bars"></i> 显示导航'
|
||||
: '<i class="fas fa-times"></i> 隐藏导航';
|
||||
});
|
||||
|
||||
// 默认在移动端隐藏导航
|
||||
navMenu.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
// 移除折叠按钮并显示导航
|
||||
const collapseBtn = document.querySelector('.collapse-btn');
|
||||
if (collapseBtn) {
|
||||
collapseBtn.remove();
|
||||
}
|
||||
navMenu.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 初始检查和窗口调整时检查
|
||||
checkScreenSize();
|
||||
window.addEventListener('resize', checkScreenSize);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
261
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/app.js
Normal file
261
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/app.js
Normal file
@@ -0,0 +1,261 @@
|
||||
// 计算机英语词汇学习应用
|
||||
class VocabularyApp {
|
||||
constructor() {
|
||||
this.vocabulary = vocabularyData;
|
||||
this.currentIndex = 0;
|
||||
this.learningMode = 'sequential'; // sequential, random, reverse
|
||||
this.randomIndices = [];
|
||||
this.history = [];
|
||||
|
||||
this.initializeElements();
|
||||
this.initializeEventListeners();
|
||||
this.initializeApp();
|
||||
}
|
||||
|
||||
initializeElements() {
|
||||
// 获取DOM元素
|
||||
this.elements = {
|
||||
wordNumber: document.getElementById('wordNumber'),
|
||||
englishWord: document.getElementById('englishWord'),
|
||||
phonetic: document.getElementById('phonetic'),
|
||||
wordType: document.getElementById('wordType'),
|
||||
chineseMeaning: document.getElementById('chineseMeaning'),
|
||||
currentIndex: document.getElementById('currentIndex'),
|
||||
totalWords: document.getElementById('totalWords'),
|
||||
prevBtn: document.getElementById('prevBtn'),
|
||||
nextBtn: document.getElementById('nextBtn'),
|
||||
sequentialMode: document.getElementById('sequentialMode'),
|
||||
randomMode: document.getElementById('randomMode'),
|
||||
reverseMode: document.getElementById('reverseMode')
|
||||
};
|
||||
}
|
||||
|
||||
initializeEventListeners() {
|
||||
// 模式切换按钮
|
||||
this.elements.sequentialMode.addEventListener('click', () => this.setLearningMode('sequential'));
|
||||
this.elements.randomMode.addEventListener('click', () => this.setLearningMode('random'));
|
||||
this.elements.reverseMode.addEventListener('click', () => this.setLearningMode('reverse'));
|
||||
|
||||
// 绑定按钮事件
|
||||
this.elements.prevBtn.addEventListener('click', () => this.previousWord());
|
||||
this.elements.nextBtn.addEventListener('click', () => this.nextWord());
|
||||
|
||||
// 键盘快捷键
|
||||
document.addEventListener('keydown', (e) => this.handleKeyPress(e));
|
||||
}
|
||||
|
||||
initializeApp() {
|
||||
// 设置总词汇数
|
||||
this.elements.totalWords.textContent = this.vocabulary.length;
|
||||
|
||||
// 生成随机索引数组
|
||||
this.generateRandomIndices();
|
||||
|
||||
// 显示第一个词汇
|
||||
this.displayCurrentWord();
|
||||
|
||||
// 更新按钮状态
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
generateRandomIndices() {
|
||||
// 生成随机索引数组
|
||||
this.randomIndices = Array.from({length: this.vocabulary.length}, (_, i) => i);
|
||||
|
||||
// Fisher-Yates 洗牌算法
|
||||
for (let i = this.randomIndices.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[this.randomIndices[i], this.randomIndices[j]] = [this.randomIndices[j], this.randomIndices[i]];
|
||||
}
|
||||
}
|
||||
|
||||
setLearningMode(mode) {
|
||||
this.learningMode = mode;
|
||||
|
||||
// 更新按钮样式
|
||||
document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active'));
|
||||
this.elements[mode + 'Mode'].classList.add('active');
|
||||
|
||||
// 重置当前索引
|
||||
this.currentIndex = 0;
|
||||
this.history = [];
|
||||
|
||||
// 如果是随机模式,重新生成随机索引
|
||||
if (mode === 'random') {
|
||||
this.generateRandomIndices();
|
||||
}
|
||||
|
||||
// 显示当前词汇
|
||||
this.displayCurrentWord();
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
getCurrentWordIndex() {
|
||||
switch (this.learningMode) {
|
||||
case 'sequential':
|
||||
return this.currentIndex;
|
||||
case 'random':
|
||||
return this.randomIndices[this.currentIndex];
|
||||
case 'reverse':
|
||||
return this.vocabulary.length - 1 - this.currentIndex;
|
||||
default:
|
||||
return this.currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
displayCurrentWord() {
|
||||
const wordIndex = this.getCurrentWordIndex();
|
||||
const word = this.vocabulary[wordIndex];
|
||||
|
||||
if (word) {
|
||||
this.elements.wordNumber.textContent = word.id;
|
||||
this.elements.englishWord.textContent = word.word;
|
||||
this.elements.phonetic.textContent = word.phonetic;
|
||||
this.elements.wordType.textContent = word.type;
|
||||
this.elements.chineseMeaning.textContent = word.meaning;
|
||||
this.elements.currentIndex.textContent = this.currentIndex + 1;
|
||||
|
||||
// 添加动画效果
|
||||
this.addDisplayAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
addDisplayAnimation() {
|
||||
const card = document.querySelector('.vocabulary-card');
|
||||
card.style.transform = 'scale(0.95)';
|
||||
card.style.opacity = '0.7';
|
||||
|
||||
setTimeout(() => {
|
||||
card.style.transform = 'scale(1)';
|
||||
card.style.opacity = '1';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
nextWord() {
|
||||
// 记录当前位置到历史
|
||||
this.history.push(this.currentIndex);
|
||||
|
||||
// 移动到下一个词汇
|
||||
if (this.currentIndex < this.vocabulary.length - 1) {
|
||||
this.currentIndex++;
|
||||
} else {
|
||||
// 到达末尾,根据模式处理
|
||||
if (this.learningMode === 'random') {
|
||||
// 随机模式:重新洗牌
|
||||
this.generateRandomIndices();
|
||||
this.currentIndex = 0;
|
||||
} else {
|
||||
// 顺序或逆序模式:回到开头
|
||||
this.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.displayCurrentWord();
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
previousWord() {
|
||||
if (this.history.length > 0) {
|
||||
// 从历史记录中恢复
|
||||
this.currentIndex = this.history.pop();
|
||||
} else if (this.currentIndex > 0) {
|
||||
// 简单向前移动
|
||||
this.currentIndex--;
|
||||
} else {
|
||||
// 在开头时,跳到末尾
|
||||
this.currentIndex = this.vocabulary.length - 1;
|
||||
}
|
||||
|
||||
this.displayCurrentWord();
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
updateButtonStates() {
|
||||
// 更新上一个按钮状态
|
||||
const canGoPrevious = this.history.length > 0 || this.currentIndex > 0;
|
||||
this.elements.prevBtn.disabled = !canGoPrevious;
|
||||
|
||||
// 更新下一个按钮状态(总是可用)
|
||||
this.elements.nextBtn.disabled = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
handleKeyPress(e) {
|
||||
switch (e.key) {
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
this.previousWord();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
case ' ': // 空格键
|
||||
e.preventDefault();
|
||||
this.nextWord();
|
||||
break;
|
||||
case '1':
|
||||
this.setLearningMode('sequential');
|
||||
break;
|
||||
case '2':
|
||||
this.setLearningMode('random');
|
||||
break;
|
||||
case '3':
|
||||
this.setLearningMode('reverse');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取学习统计信息
|
||||
getStats() {
|
||||
return {
|
||||
totalWords: this.vocabulary.length,
|
||||
currentPosition: this.currentIndex + 1,
|
||||
learningMode: this.learningMode,
|
||||
historyLength: this.history.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.vocabularyApp = new VocabularyApp();
|
||||
|
||||
// 添加一些用户提示
|
||||
console.log('计算机英语词汇学习应用已启动!');
|
||||
console.log('快捷键提示:');
|
||||
console.log('← 上一个词汇');
|
||||
console.log('→ 或 空格 下一个词汇');
|
||||
console.log('1 顺序模式');
|
||||
console.log('2 随机模式');
|
||||
console.log('3 逆序模式');
|
||||
});
|
||||
|
||||
// 防止页面刷新时丢失进度(可选功能)
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (window.vocabularyApp) {
|
||||
const stats = window.vocabularyApp.getStats();
|
||||
localStorage.setItem('vocabularyProgress', JSON.stringify({
|
||||
currentIndex: window.vocabularyApp.currentIndex,
|
||||
learningMode: window.vocabularyApp.learningMode,
|
||||
timestamp: Date.now()
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
// 页面加载时恢复进度(可选功能)
|
||||
window.addEventListener('load', () => {
|
||||
const savedProgress = localStorage.getItem('vocabularyProgress');
|
||||
if (savedProgress && window.vocabularyApp) {
|
||||
try {
|
||||
const progress = JSON.parse(savedProgress);
|
||||
// 只在24小时内的进度才恢复
|
||||
if (Date.now() - progress.timestamp < 24 * 60 * 60 * 1000) {
|
||||
window.vocabularyApp.setLearningMode(progress.learningMode);
|
||||
window.vocabularyApp.currentIndex = progress.currentIndex;
|
||||
window.vocabularyApp.displayCurrentWord();
|
||||
window.vocabularyApp.updateButtonStates();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('无法恢复学习进度');
|
||||
}
|
||||
}
|
||||
});
|
||||
139
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/background.css
Normal file
139
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/background.css
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 背景样式文件 */
|
||||
|
||||
/* 主体背景 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
/* 淡绿色渐变背景变体 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #a8e6a3 50%, #81c784 100%);
|
||||
opacity: 0.3;
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
/* 动态背景装饰 */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 80%, rgba(39, 174, 96, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(46, 204, 113, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(52, 152, 219, 0.05) 0%, transparent 50%);
|
||||
z-index: -1;
|
||||
animation: backgroundFloat 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 背景动画 */
|
||||
@keyframes backgroundFloat {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-10px) rotate(1deg);
|
||||
}
|
||||
66% {
|
||||
transform: translateY(5px) rotate(-1deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 词汇卡片背景增强 */
|
||||
.vocabulary-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
/* 按钮背景效果 */
|
||||
.control-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.control-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
/* 模式按钮背景效果 */
|
||||
.mode-btn {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mode-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
transition: left 0.4s;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.mode-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 768px) {
|
||||
body::after {
|
||||
animation-duration: 15s;
|
||||
}
|
||||
|
||||
.vocabulary-card {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body::before {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
body::after {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色模式背景支持 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||||
}
|
||||
|
||||
body::before {
|
||||
background: linear-gradient(135deg, #27ae60 0%, #2ecc71 50%, #16a085 100%);
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.vocabulary-card {
|
||||
background: rgba(44, 62, 80, 0.95);
|
||||
color: #ecf0f1;
|
||||
border-color: rgba(39, 174, 96, 0.3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
计算机英语词汇数据转换脚本
|
||||
将txt格式的词汇文件转换为JavaScript数组格式
|
||||
"""
|
||||
|
||||
import re
|
||||
import json
|
||||
|
||||
def parse_vocabulary_file(file_path):
|
||||
"""解析词汇文件"""
|
||||
vocabulary_list = []
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
lines = file.readlines()
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('51 - 100') or line.startswith('101 - 150') or line.startswith('151 - 200'):
|
||||
continue
|
||||
|
||||
# 使用正则表达式匹配词汇条目
|
||||
# 格式: 数字. 单词 [音标] 词性 中文释义
|
||||
pattern = r'^(\d+)\. ([a-zA-Z\-\']+) \[([^\]]*(?:\][^\]]*)*) ([a-zA-Z\.&]+) (.+)$'
|
||||
match = re.match(pattern, line)
|
||||
|
||||
if match:
|
||||
word_id, word, phonetic, word_type, meaning = match.groups()
|
||||
|
||||
# 修复音标格式问题
|
||||
if not phonetic.endswith(']'):
|
||||
# 查找下一个有效的词性和释义
|
||||
parts = line.split()
|
||||
for i, part in enumerate(parts):
|
||||
if part in ['n.', 'v.', 'vt.', 'vi.', 'a.', 'ad.', 'prep.', 'conj.', 'pron.']:
|
||||
word_type = part
|
||||
meaning = ' '.join(parts[i+1:])
|
||||
# 从原始行中提取音标
|
||||
phonetic_start = line.find('[') + 1
|
||||
phonetic_end = line.find(word_type) - 1
|
||||
phonetic = line[phonetic_start:phonetic_end].strip()
|
||||
break
|
||||
|
||||
vocabulary_item = {
|
||||
'id': int(word_id),
|
||||
'word': word.strip(),
|
||||
'phonetic': f'[{phonetic}]',
|
||||
'type': word_type.strip(),
|
||||
'meaning': meaning.strip()
|
||||
}
|
||||
|
||||
vocabulary_list.append(vocabulary_item)
|
||||
|
||||
return vocabulary_list
|
||||
|
||||
def generate_js_file(vocabulary_list, output_path):
|
||||
"""生成JavaScript文件"""
|
||||
js_content = '''// 计算机英语词汇数据
|
||||
const vocabularyData = [
|
||||
'''
|
||||
|
||||
for i, item in enumerate(vocabulary_list):
|
||||
js_content += f' {{ id: {item["id"]}, word: "{item["word"]}", phonetic: "{item["phonetic"]}", type: "{item["type"]}", meaning: "{item["meaning"]}" }}'
|
||||
|
||||
if i < len(vocabulary_list) - 1:
|
||||
js_content += ',\n'
|
||||
else:
|
||||
js_content += '\n'
|
||||
|
||||
js_content += '''];
|
||||
|
||||
// 导出数据供其他文件使用
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = vocabularyData;
|
||||
}
|
||||
'''
|
||||
|
||||
with open(output_path, 'w', encoding='utf-8') as file:
|
||||
file.write(js_content)
|
||||
|
||||
print(f'成功生成JavaScript文件: {output_path}')
|
||||
print(f'共转换 {len(vocabulary_list)} 个词汇')
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
input_file = '计算机英语词汇.txt'
|
||||
output_file = 'vocabulary-data.js'
|
||||
|
||||
try:
|
||||
print('开始解析词汇文件...')
|
||||
vocabulary_list = parse_vocabulary_file(input_file)
|
||||
|
||||
print('开始生成JavaScript文件...')
|
||||
generate_js_file(vocabulary_list, output_file)
|
||||
|
||||
print('转换完成!')
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f'错误: 找不到文件 {input_file}')
|
||||
except Exception as e:
|
||||
print(f'转换过程中发生错误: {e}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
46
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/index.html
Normal file
46
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/index.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>计算机英语词汇学习</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>计算机英语词汇学习</h1>
|
||||
<div class="mode-selector">
|
||||
<button id="sequentialMode" class="mode-btn active">顺序</button>
|
||||
<button id="randomMode" class="mode-btn">随机</button>
|
||||
<button id="reverseMode" class="mode-btn">逆序</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="vocabulary-card">
|
||||
<div class="word-number" id="wordNumber">1</div>
|
||||
<div class="word-content">
|
||||
<div class="english-word" id="englishWord">file</div>
|
||||
<div class="phonetic" id="phonetic">[fail]</div>
|
||||
<div class="word-type" id="wordType">n.</div>
|
||||
<div class="chinese-meaning" id="chineseMeaning">文件;v. 保存文件</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-info">
|
||||
<span id="currentIndex">1</span> / <span id="totalWords">1731</span>
|
||||
</div>
|
||||
|
||||
<div class="control-buttons">
|
||||
<button id="prevBtn" class="control-btn">上一个</button>
|
||||
<button id="nextBtn" class="control-btn">下一个</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="vocabulary-data.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
364
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/styles.css
Normal file
364
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/styles.css
Normal file
@@ -0,0 +1,364 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #2c3e50;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #27ae60;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 模式选择器 */
|
||||
.mode-selector {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #27ae60;
|
||||
background: transparent;
|
||||
color: #27ae60;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mode-btn:hover {
|
||||
background: #27ae60;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.mode-btn.active {
|
||||
background: #27ae60;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 词汇卡片 */
|
||||
.vocabulary-card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 10px 30px rgba(39, 174, 96, 0.1);
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
border: 2px solid #e8f5e8;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.vocabulary-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 40px rgba(39, 174, 96, 0.15);
|
||||
}
|
||||
|
||||
.word-number {
|
||||
color: #95a5a6;
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.word-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.english-word {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: #27ae60;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 1.2rem;
|
||||
color: #7f8c8d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.word-type {
|
||||
font-size: 1rem;
|
||||
color: #e67e22;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.chinese-meaning {
|
||||
font-size: 1.3rem;
|
||||
color: #2c3e50;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 进度信息 */
|
||||
.progress-info {
|
||||
color: #7f8c8d;
|
||||
font-size: 16px;
|
||||
margin-bottom: 30px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 控制按钮 */
|
||||
.control-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 12px 24px;
|
||||
border: 2px solid #27ae60;
|
||||
background: transparent;
|
||||
color: #27ae60;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: #27ae60;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.control-btn.primary {
|
||||
background: #27ae60;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.control-btn.primary:hover {
|
||||
background: #219a52;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.control-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.control-btn:disabled:hover {
|
||||
background: transparent;
|
||||
color: #27ae60;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.vocabulary-card {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.english-word {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 15px 30px;
|
||||
font-size: 18px;
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端适配 (1024px+) */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.vocabulary-card {
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
.english-word {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.chinese-meaning {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 15px 35px;
|
||||
font-size: 18px;
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* 手机端优化 (最大767px) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.mode-selector {
|
||||
gap: 8px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.vocabulary-card {
|
||||
padding: 25px 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.english-word {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.word-type {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.chinese-meaning {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.word-content {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 10px 16px;
|
||||
font-size: 14px;
|
||||
min-width: 80px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 超小屏幕优化 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.vocabulary-card {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.english-word {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
1677
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/vocabulary-data.js
Normal file
1677
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/vocabulary-data.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
1.生成为静态网页,js,css,html分离出来,不要混合在一起放入html里,难以阅读
|
||||
2.网页要适配手机端,电脑端和平板端三个设备分别做不同的css格式,优先优化手机端用户体验
|
||||
3.网页默认风格以淡绿色清新风格为主,除非用户要求
|
||||
4.尽量不要引用外部css,js,实在要引用就使用中国国内的cdn,否则用户可能加载不出来
|
||||
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
|
||||
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
||||
1731
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/计算机英语词汇.txt
Normal file
1731
InfoGenie-frontend/public/toolbox/学习工具/计算机英语词汇学习/计算机英语词汇.txt
Normal file
File diff suppressed because it is too large
Load Diff
113
InfoGenie-frontend/public/toolbox/网页小玩具/人生倒计时/人生倒计时.html
Normal file
113
InfoGenie-frontend/public/toolbox/网页小玩具/人生倒计时/人生倒计时.html
Normal file
@@ -0,0 +1,113 @@
|
||||
<div id="life-countdown" style="border: 1px solid #ddd; padding: 10px; border-radius: 5px;">
|
||||
<h3 class="colorful-text" style="text-align: center;">人生倒计时</h3>
|
||||
<div class="countdown-section" style="margin: 10px 0;">
|
||||
<div class="countdown-text" style="color: red;">今天已过去 <span id="hours-today"></span> 小时</div>
|
||||
<div class="progress-bar" style="background: #ddd; border-radius: 10px; overflow: hidden;">
|
||||
<div id="bar-today" class="progress" style="background: #f39c12;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="countdown-section" style="margin: 10px 0;">
|
||||
<div class="countdown-text" style="color: orange;">本周已过去 <span id="days-week"></span> 天</div>
|
||||
<div class="progress-bar" style="background: #ddd; border-radius: 10px; overflow: hidden;">
|
||||
<div id="bar-week" class="progress" style="background: #3498db;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="countdown-section" style="margin: 10px 0;">
|
||||
<div class="countdown-text" style="color: yellow;">本月已过去 <span id="days-month"></span> 天</div>
|
||||
<div class="progress-bar" style="background: #ddd; border-radius: 10px; overflow: hidden;">
|
||||
<div id="bar-month" class="progress" style="background: #2ecc71;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="countdown-section" style="margin: 10px 0;">
|
||||
<div class="countdown-text" style="color: green;">今年已过去 <span id="days-year"></span> 天</div>
|
||||
<div class="progress-bar" style="background: #ddd; border-radius: 10px; overflow: hidden;">
|
||||
<div id="bar-year" class="progress" style="background: #9b59b6;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="countdown-section" style="margin: 10px 0;">
|
||||
<div class="countdown-text" style="color: blue;">距离春节还有 <span id="days-chunjie"></span> 天</div>
|
||||
<div class="progress-bar" style="background: #ddd; border-radius: 10px; overflow: hidden;">
|
||||
<div id="bar-chunjie" class="progress" style="background: #2980b9;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="countdown-section" style="margin: 10px 0;">
|
||||
<div class="countdown-text" style="color: purple;">距离我的生日还有 <span id="days-birthday"></span> 天</div>
|
||||
<div class="progress-bar" style="background: #ddd; border-radius: 10px; overflow: hidden;">
|
||||
<div id="bar-birthday" class="progress" style="background: #e74c3c;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.colorful-text {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #ff6347; /* 可以根据需要调整颜色 */
|
||||
}
|
||||
|
||||
.countdown-text {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 20px;
|
||||
transition: width 1s ease-in-out;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function updateCountdown() {
|
||||
const now = new Date();
|
||||
|
||||
// 今天已过去的小时数
|
||||
const hoursToday = now.getHours();
|
||||
document.getElementById('hours-today').innerText = hoursToday;
|
||||
document.getElementById('bar-today').style.width = (hoursToday / 24 * 100) + '%';
|
||||
|
||||
// 本周已过去的天数
|
||||
const daysWeek = now.getDay();
|
||||
document.getElementById('days-week').innerText = daysWeek;
|
||||
document.getElementById('bar-week').style.width = (daysWeek / 7 * 100) + '%';
|
||||
|
||||
// 本月已过去的天数
|
||||
const daysMonth = now.getDate();
|
||||
const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
|
||||
document.getElementById('days-month').innerText = daysMonth;
|
||||
document.getElementById('bar-month').style.width = (daysMonth / daysInMonth * 100) + '%';
|
||||
|
||||
// 今年已过去的天数
|
||||
const start = new Date(now.getFullYear(), 0, 0);
|
||||
const diff = now - start;
|
||||
const oneDay = 1000 * 60 * 60 * 24;
|
||||
const daysYear = Math.floor(diff / oneDay);
|
||||
document.getElementById('days-year').innerText = daysYear;
|
||||
document.getElementById('bar-year').style.width = (daysYear / 365 * 100) + '%';
|
||||
|
||||
// 离春节还有多少天(2025年1月29日)
|
||||
const chunjieDate = new Date(2025, 0, 29); // 春节日期假设为2025年1月29日
|
||||
const diffChunjie = chunjieDate - now;
|
||||
const daysChunjie = Math.floor(diffChunjie / oneDay);
|
||||
document.getElementById('days-chunjie').innerText = daysChunjie;
|
||||
document.getElementById('bar-chunjie').style.width = ((365 - daysChunjie) / 365 * 100) + '%';
|
||||
|
||||
// 离生日还有多少天(每年的10月25日)
|
||||
let birthdayDate = new Date(now.getFullYear(), 9, 25);
|
||||
if (now > birthdayDate) {
|
||||
birthdayDate = new Date(now.getFullYear() + 1, 9, 25);
|
||||
}
|
||||
const diffBirthday = birthdayDate - now;
|
||||
const daysBirthday = Math.floor(diffBirthday / (1000 * 60 * 60 * 24));
|
||||
document.getElementById('days-birthday').innerText = daysBirthday;
|
||||
document.getElementById('bar-birthday').style.width = ((365 - daysBirthday) / 365 * 100) + '%';
|
||||
}
|
||||
|
||||
updateCountdown();
|
||||
setInterval(updateCountdown, 3600000); // 每小时更新一次
|
||||
</script>
|
||||
51
InfoGenie-frontend/public/toolbox/网页小玩具/代码雨/index.html
Normal file
51
InfoGenie-frontend/public/toolbox/网页小玩具/代码雨/index.html
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>飞雪前端艺术</title>
|
||||
<style>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script>
|
||||
// 获取 canvas 元素的宽度和高度,并将宽度和高度设置为屏幕的可用宽度和高度。
|
||||
const width = document.getElementById("canvas").width = screen.availWidth;
|
||||
const height = document.getElementById("canvas").height = screen.availHeight;
|
||||
// 获取 canvas 的绘图上下文
|
||||
const ctx = document.getElementById("canvas").getContext("2d");
|
||||
// 创建一个大小为 width/10 的数组并填充为 0
|
||||
const arr = Array(Math.ceil(width / 10)).fill(0);
|
||||
// 创建一个字符串数组,用于存储字符。
|
||||
const str = "✧︎︎︎︎︎︎♾♲✰︎✦︎☭︎︎︎︎✵︎︎⚘︎✞︎♘♞☆︎★☼︎☾◎☽︎Ω℞№︎❂❁︎✣✶✺✷◦◉⦿☒✗☐☞◇☛︎︎⌘✘︎".split("");
|
||||
ctx.font = "10px 优设标题黑";
|
||||
function rain() {
|
||||
// 设置颜色,并绘制一个全屏的矩形
|
||||
ctx.fillStyle = "rgba(0,0,20,0.05)";
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
// 设置文字的颜色
|
||||
ctx.fillStyle = '#00c8aa';
|
||||
arr.forEach(function (value, index) {
|
||||
// 根据数组的索引值来绘制文字,x 坐标为索引值 * 10,y 坐标为 value + 10。
|
||||
ctx.fillText(str[Math.floor(Math.random() * str.length)], index * 10, value + 10);
|
||||
|
||||
// 从上一次绘制的位置开始,将数组值设置为下一次绘制位置。
|
||||
arr[index] = value >= height || value > 8888 * Math.random() ? 0 : value + 10;
|
||||
});
|
||||
}
|
||||
// 每 30 毫秒执行一次 rain() 函数。
|
||||
setInterval(rain, 30);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
509
InfoGenie-frontend/public/toolbox/网页小玩具/粒子圣诞树/粒子圣诞树.html
Normal file
509
InfoGenie-frontend/public/toolbox/网页小玩具/粒子圣诞树/粒子圣诞树.html
Normal file
@@ -0,0 +1,509 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>圣诞快乐</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
html, body { height: 100%; }
|
||||
body { margin: 0; overflow: hidden; background: #000; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, PingFangSC, "Microsoft YaHei", sans-serif; }
|
||||
#app { position: relative; width: 100vw; height: 100vh; }
|
||||
#canvas-container { position: absolute; inset: 0; }
|
||||
|
||||
.barrage { position: fixed; inset: 0; pointer-events: none; z-index: 9; }
|
||||
.barrage-item { position: fixed; left: 100vw; white-space: nowrap; font-weight: 700; text-shadow: 0 0 8px rgba(255,255,255,0.35), 0 0 16px rgba(255,255,255,0.2); filter: drop-shadow(0 2px 4px rgba(0,0,0,0.35)); animation-name: fly; animation-timing-function: linear; animation-fill-mode: forwards; }
|
||||
@keyframes fly { 0% { transform: translateX(0); } 100% { transform: translateX(-120vw); } }
|
||||
.fullscreen-btn { position: fixed; right: 14px; bottom: 14px; z-index: 11; padding: 10px 14px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.25); background: rgba(255,255,255,0.12); color: #fff; font-weight: 600; letter-spacing: 0.5px; backdrop-filter: blur(4px); cursor: pointer; }
|
||||
.fullscreen-btn:hover { background: rgba(255,255,255,0.18); }
|
||||
@media (max-width: 768px) { .fullscreen-btn { padding: 9px 12px; right: 10px; bottom: 10px; font-size: 13px; } }
|
||||
@media (max-width: 768px) {
|
||||
.barrage-item { font-size: clamp(14px, 3.5vw, 20px); }
|
||||
}
|
||||
@media (min-width: 769px) {
|
||||
.barrage-item { font-size: clamp(16px, 2.1vw, 28px); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
import { createApp, ref, onMounted, h } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';
|
||||
import * as THREE from 'https://unpkg.com/three@0.164.1/build/three.module.js';
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
const isFs = ref(false);
|
||||
const barrageItems = ref([]);
|
||||
const containerEl = ref(null);
|
||||
|
||||
let renderer, scene, camera, group;
|
||||
let geoLeaves, matLeaves, pointsLeaves, posLeaves, colLeaves, velLeaves, tgtLeaves, startMorphLeaves;
|
||||
let geoOrnaments, matOrnaments, pointsOrnaments, posOrnaments, colOrnaments, velOrnaments, tgtOrnaments, startMorphOrnaments;
|
||||
let geoTrunk, matTrunk, pointsTrunk, posTrunk, colTrunk, velTrunk, tgtTrunk, startMorphTrunk;
|
||||
let geoStar, matStar, pointsStar, posStar, colStar, velStar, tgtStar, startMorphStar;
|
||||
let geoAccents, matAccents, pointsAccents, posAccents, colAccents, velAccents, tgtAccents, startMorphAccents;
|
||||
let snowGroup, snowSprites, snowVel, snowRot, snowScale, snowOpacityPhase;
|
||||
let startTime = 0;
|
||||
const disperseDuration = 3500;
|
||||
const morphDuration = 3000;
|
||||
const particleCountLeaves = 5400;
|
||||
const particleCountOrnaments = 400;
|
||||
const particleCountTrunk = 1200;
|
||||
const particleCountStar = 320;
|
||||
const particleCountAccents = 160;
|
||||
const snowCount = 200;
|
||||
const treeHeight = 14;
|
||||
const baseRadius = 6.2;
|
||||
let running = true;
|
||||
const topY = treeHeight + 1.2;
|
||||
const bottomY = -1.4;
|
||||
|
||||
const messages = [
|
||||
'圣诞快乐', '平安喜乐', '万事胜意', '心想事成', '前程似锦', '阖家幸福',
|
||||
'福星高照', '岁岁常欢愉', '诸事顺遂', '新年好运常在', '健康平安', '幸福常伴'
|
||||
];
|
||||
|
||||
function randomHsl() {
|
||||
const h = Math.floor(Math.random() * 360);
|
||||
const s = 68 + Math.random() * 22;
|
||||
const l = 50 + Math.random() * 10;
|
||||
return `hsl(${h}deg, ${s}%, ${l}%)`;
|
||||
}
|
||||
|
||||
function createSnowflakeTexture() {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 128; c.height = 128;
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.clearRect(0, 0, 128, 128);
|
||||
ctx.translate(64, 64);
|
||||
ctx.strokeStyle = 'rgba(255,255,255,0.95)';
|
||||
ctx.lineWidth = 2;
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const ang = (Math.PI * 2 / 6) * i;
|
||||
const x = Math.cos(ang) * 48;
|
||||
const y = Math.sin(ang) * 48;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(x, y);
|
||||
ctx.stroke();
|
||||
for (let b = 1; b <= 3; b++) {
|
||||
const t = b / 4;
|
||||
const bx = Math.cos(ang) * 48 * t;
|
||||
const by = Math.sin(ang) * 48 * t;
|
||||
const sideAng = ang + Math.PI / 2;
|
||||
const len = 10 * (1 - t);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(bx, by);
|
||||
ctx.lineTo(bx + Math.cos(sideAng) * len, by + Math.sin(sideAng) * len);
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(bx, by);
|
||||
ctx.lineTo(bx - Math.cos(sideAng) * len, by - Math.sin(sideAng) * len);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
const tex = new THREE.CanvasTexture(c);
|
||||
tex.minFilter = THREE.LinearMipmapLinearFilter;
|
||||
tex.magFilter = THREE.LinearFilter;
|
||||
tex.needsUpdate = true;
|
||||
return tex;
|
||||
}
|
||||
|
||||
function createSnowGlowTexture() {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 128; c.height = 128;
|
||||
const ctx = c.getContext('2d');
|
||||
const grd = ctx.createRadialGradient(64, 64, 0, 64, 64, 64);
|
||||
grd.addColorStop(0, 'rgba(255,255,255,0.57)');
|
||||
grd.addColorStop(0.4, 'rgba(255,255,255,0.23)');
|
||||
grd.addColorStop(1, 'rgba(255,255,255,0.0)');
|
||||
ctx.fillStyle = grd;
|
||||
ctx.fillRect(0, 0, 128, 128);
|
||||
const tex = new THREE.CanvasTexture(c);
|
||||
tex.minFilter = THREE.LinearMipmapLinearFilter;
|
||||
tex.magFilter = THREE.LinearFilter;
|
||||
tex.needsUpdate = true;
|
||||
return tex;
|
||||
}
|
||||
|
||||
function spawnBarrage() {
|
||||
const count = 2 + Math.floor(Math.random() * 3);
|
||||
const now = Date.now();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const text = messages[Math.floor(Math.random() * messages.length)];
|
||||
const top = Math.random() * 90 + 5;
|
||||
const duration = 10 + Math.random() * 8;
|
||||
const color = randomHsl();
|
||||
const id = `${now}-${i}-${Math.random().toString(36).slice(2,7)}`;
|
||||
barrageItems.value.push({ id, text, top: `${top}%`, duration: `${duration}s`, color, shadow: color });
|
||||
setTimeout(() => {
|
||||
const idx = barrageItems.value.findIndex(x => x.id === id);
|
||||
if (idx >= 0) barrageItems.value.splice(idx, 1);
|
||||
}, duration * 1000 + 400);
|
||||
}
|
||||
}
|
||||
|
||||
function enterFs() {
|
||||
const el = document.getElementById('app') || containerEl.value || document.documentElement;
|
||||
const rfs = el.requestFullscreen || el.webkitRequestFullscreen || el.msRequestFullscreen;
|
||||
if (rfs) rfs.call(el);
|
||||
}
|
||||
|
||||
function exitFs() {
|
||||
const efs = document.exitFullscreen || document.webkitExitFullscreen || document.msExitFullscreen;
|
||||
if (efs) efs.call(document);
|
||||
}
|
||||
|
||||
function toggleFs() { if (document.fullscreenElement) exitFs(); else enterFs(); }
|
||||
|
||||
function computeLeafTargets(count) {
|
||||
const arr = new Float32Array(count * 3);
|
||||
const layers = 12;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const layer = Math.floor(Math.random() * layers);
|
||||
const yBase = (layer / (layers - 1)) * treeHeight;
|
||||
const y = yBase + (Math.random() - 0.5) * (treeHeight * 0.035 + (1 - layer / layers) * 0.12);
|
||||
const t = y / treeHeight;
|
||||
const shelf = 0.45 * Math.sin(3.5 * (1 - t));
|
||||
const wobble = 0.25 * Math.sin(8 * t + Math.random() * 0.8);
|
||||
const r = Math.max(0.04, (baseRadius * (1 - t)) + shelf + wobble);
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const x = r * Math.cos(a);
|
||||
const z = r * Math.sin(a);
|
||||
arr[i * 3] = x;
|
||||
arr[i * 3 + 1] = y;
|
||||
arr[i * 3 + 2] = z;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function computeTrunkTargets(count) {
|
||||
const arr = new Float32Array(count * 3);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const ty = -1 + Math.random() * (treeHeight * 0.22);
|
||||
const tr = 0.35 + Math.random() * 0.22;
|
||||
const ta = Math.random() * Math.PI * 2;
|
||||
arr[i * 3] = tr * Math.cos(ta);
|
||||
arr[i * 3 + 1] = ty;
|
||||
arr[i * 3 + 2] = tr * Math.sin(ta);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function computeOrnamentTargets(count) {
|
||||
const arr = new Float32Array(count * 3);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const y = Math.random() * treeHeight * 0.95;
|
||||
const t = y / treeHeight;
|
||||
const r = Math.max(0.05, (baseRadius * (1 - t)) + 0.3 * Math.sin(7 * t + Math.random()));
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const x = r * Math.cos(a);
|
||||
const z = r * Math.sin(a);
|
||||
arr[i * 3] = x;
|
||||
arr[i * 3 + 1] = y;
|
||||
arr[i * 3 + 2] = z;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function computeStarTargets(count) {
|
||||
const arr = new Float32Array(count * 3);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const rBase = 0.75;
|
||||
const r = rBase + 0.25 * Math.cos(5 * a) + (Math.random() - 0.5) * 0.12;
|
||||
const x = r * Math.cos(a);
|
||||
const z = r * Math.sin(a);
|
||||
const y = treeHeight + 0.9 + (Math.random() - 0.5) * 0.35;
|
||||
arr[i * 3] = x;
|
||||
arr[i * 3 + 1] = y;
|
||||
arr[i * 3 + 2] = z;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function computeAccentsTargets(count) {
|
||||
const arr = new Float32Array(count * 3);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const y = Math.random() * treeHeight;
|
||||
const t = y / treeHeight;
|
||||
const r = Math.max(0.05, (baseRadius * (1 - t)) + 0.35 * Math.sin(6 * t + Math.random()));
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const x = r * Math.cos(a);
|
||||
const z = r * Math.sin(a);
|
||||
arr[i * 3] = x;
|
||||
arr[i * 3 + 1] = y;
|
||||
arr[i * 3 + 2] = z;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function easeInOutCubic(t) {
|
||||
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
|
||||
function ornamentColors(index) {
|
||||
const palette = [
|
||||
new THREE.Color(0xff4d6d), new THREE.Color(0xffb703), new THREE.Color(0x32c3ff),
|
||||
new THREE.Color(0x8ce99a), new THREE.Color(0xb197fc), new THREE.Color(0xffaad4)
|
||||
];
|
||||
return palette[index % palette.length];
|
||||
}
|
||||
|
||||
|
||||
function initSet(count, size, colorFn, computeTargets) {
|
||||
const geo = new THREE.BufferGeometry();
|
||||
const pos = new Float32Array(count * 3);
|
||||
const col = new Float32Array(count * 3);
|
||||
const vel = new Float32Array(count * 3);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const ix = i * 3;
|
||||
pos[ix] = (Math.random() - 0.5) * 0.4;
|
||||
pos[ix + 1] = (Math.random() - 0.5) * 0.4;
|
||||
pos[ix + 2] = (Math.random() - 0.5) * 0.4;
|
||||
let vx = (Math.random() - 0.5);
|
||||
let vy = (Math.random() - 0.5) * 0.6;
|
||||
let vz = (Math.random() - 0.5);
|
||||
const s = 0.012 + Math.random() * 0.018;
|
||||
vel[ix] = vx * s;
|
||||
vel[ix + 1] = vy * s;
|
||||
vel[ix + 2] = vz * s;
|
||||
const c = colorFn(i);
|
||||
col[ix] = c.r;
|
||||
col[ix + 1] = c.g;
|
||||
col[ix + 2] = c.b;
|
||||
}
|
||||
geo.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
|
||||
geo.setAttribute('color', new THREE.Float32BufferAttribute(col, 3));
|
||||
const mat = new THREE.PointsMaterial({ size, sizeAttenuation: true, transparent: true, opacity: 0.95, vertexColors: true, blending: THREE.AdditiveBlending, depthWrite: false });
|
||||
const points = new THREE.Points(geo, mat);
|
||||
const targets = computeTargets(count);
|
||||
group.add(points);
|
||||
return { geo, pos, col, vel, mat, points, targets };
|
||||
}
|
||||
|
||||
function initThree() {
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.FogExp2(0x06101a, 0.035);
|
||||
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
camera.position.set(0, 6.5, 20);
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
containerEl.value.appendChild(renderer.domElement);
|
||||
|
||||
group = new THREE.Group();
|
||||
scene.add(group);
|
||||
|
||||
const ambient = new THREE.AmbientLight(0xffffff, 0.35);
|
||||
scene.add(ambient);
|
||||
const point = new THREE.PointLight(0xfff3e0, 3.2, 100);
|
||||
point.position.set(4, treeHeight + 2, 6);
|
||||
scene.add(point);
|
||||
({ geo: geoLeaves, pos: posLeaves, col: colLeaves, vel: velLeaves, mat: matLeaves, points: pointsLeaves, targets: tgtLeaves } = initSet(
|
||||
particleCountLeaves,
|
||||
0.12,
|
||||
() => {
|
||||
const g = 0.35 + Math.random() * 0.65;
|
||||
const r = 0.2 + 0.8 * Math.random();
|
||||
const green = 0.5 + 0.5 * Math.random();
|
||||
return new THREE.Color(r * g, green * g, 0.2 * g);
|
||||
},
|
||||
computeLeafTargets
|
||||
));
|
||||
|
||||
({ geo: geoOrnaments, pos: posOrnaments, col: colOrnaments, vel: velOrnaments, mat: matOrnaments, points: pointsOrnaments, targets: tgtOrnaments } = initSet(
|
||||
particleCountOrnaments,
|
||||
0.14,
|
||||
(i) => ornamentColors(i),
|
||||
computeOrnamentTargets
|
||||
));
|
||||
|
||||
({ geo: geoTrunk, pos: posTrunk, col: colTrunk, vel: velTrunk, mat: matTrunk, points: pointsTrunk, targets: tgtTrunk } = initSet(
|
||||
particleCountTrunk,
|
||||
0.12,
|
||||
() => new THREE.Color(0.36, 0.24, 0.18),
|
||||
computeTrunkTargets
|
||||
));
|
||||
({ geo: geoStar, pos: posStar, col: colStar, vel: velStar, mat: matStar, points: pointsStar, targets: tgtStar } = initSet(
|
||||
particleCountStar,
|
||||
0.12,
|
||||
() => new THREE.Color(1.0, 0.83, 0.3),
|
||||
computeStarTargets
|
||||
));
|
||||
({ geo: geoAccents, pos: posAccents, col: colAccents, vel: velAccents, mat: matAccents, points: pointsAccents, targets: tgtAccents } = initSet(
|
||||
particleCountAccents,
|
||||
0.22,
|
||||
(i) => ornamentColors(i + 3),
|
||||
computeAccentsTargets
|
||||
));
|
||||
|
||||
const snowTex = createSnowflakeTexture();
|
||||
const glowTex = createSnowGlowTexture();
|
||||
snowGroup = new THREE.Group();
|
||||
snowGroup.renderOrder = 0;
|
||||
scene.add(snowGroup);
|
||||
snowSprites = new Array(snowCount);
|
||||
const snowHalos = new Array(snowCount);
|
||||
snowVel = new Float32Array(snowCount * 2);
|
||||
snowRot = new Float32Array(snowCount);
|
||||
snowScale = new Float32Array(snowCount);
|
||||
snowOpacityPhase = new Float32Array(snowCount);
|
||||
for (let i = 0; i < snowCount; i++) {
|
||||
const mat = new THREE.SpriteMaterial({ map: snowTex, color: 0xffffff, transparent: true, opacity: 1.0, depthWrite: false, depthTest: false, blending: THREE.AdditiveBlending, fog: false });
|
||||
const spr = new THREE.Sprite(mat);
|
||||
const s = (1.6 + Math.random() * 1.6) * (2/3);
|
||||
spr.scale.set(s, s, 1);
|
||||
const x = (Math.random() - 0.5) * 120;
|
||||
const y = 20 + Math.random() * 40;
|
||||
const z = -22 - Math.random() * 12;
|
||||
spr.position.set(x, y, z);
|
||||
snowScale[i] = s;
|
||||
snowVel[i * 2] = (Math.random() - 0.5) * 0.006;
|
||||
snowVel[i * 2 + 1] = -0.008 - Math.random() * 0.01;
|
||||
snowRot[i] = (-0.004 + Math.random() * 0.008);
|
||||
snowOpacityPhase[i] = Math.random() * Math.PI * 2;
|
||||
snowSprites[i] = spr;
|
||||
snowGroup.add(spr);
|
||||
const haloMat = new THREE.SpriteMaterial({ map: glowTex, color: 0xffffff, transparent: true, opacity: 0.6, depthWrite: false, depthTest: false, blending: THREE.AdditiveBlending, fog: false });
|
||||
const halo = new THREE.Sprite(haloMat);
|
||||
halo.scale.set(s * 2.6, s * 2.6, 1);
|
||||
halo.position.copy(spr.position);
|
||||
snowHalos[i] = halo;
|
||||
snowGroup.add(halo);
|
||||
}
|
||||
pointsLeaves.renderOrder = 1;
|
||||
pointsOrnaments.renderOrder = 1;
|
||||
pointsTrunk.renderOrder = 1;
|
||||
pointsStar.renderOrder = 1;
|
||||
pointsAccents.renderOrder = 1;
|
||||
fitCamera();
|
||||
startTime = performance.now();
|
||||
animate();
|
||||
window.addEventListener('resize', onResize);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
if (!running) return;
|
||||
const now = performance.now();
|
||||
const elapsed = now - startTime;
|
||||
function updateSet(geo, posArr, velArr, startMorphArr, targetsArr, count) {
|
||||
const attr = geo.getAttribute('position');
|
||||
const arr = attr.array;
|
||||
if (elapsed < disperseDuration) {
|
||||
const dt = 16;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const ix = i * 3;
|
||||
arr[ix] += velArr[ix] * dt;
|
||||
arr[ix + 1] += velArr[ix + 1] * dt;
|
||||
arr[ix + 2] += velArr[ix + 2] * dt;
|
||||
}
|
||||
} else {
|
||||
if (!startMorphArr) {
|
||||
startMorphArr = Float32Array.from(arr);
|
||||
if (geo === geoLeaves) startMorphLeaves = startMorphArr;
|
||||
if (geo === geoOrnaments) startMorphOrnaments = startMorphArr;
|
||||
if (geo === geoTrunk) startMorphTrunk = startMorphArr;
|
||||
if (geo === geoStar) startMorphStar = startMorphArr;
|
||||
if (geo === geoAccents) startMorphAccents = startMorphArr;
|
||||
}
|
||||
const t = Math.min(1, (elapsed - disperseDuration) / morphDuration);
|
||||
const e = easeInOutCubic(t);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const ix = i * 3;
|
||||
arr[ix] = startMorphArr[ix] + (targetsArr[ix] - startMorphArr[ix]) * e;
|
||||
arr[ix + 1] = startMorphArr[ix + 1] + (targetsArr[ix + 1] - startMorphArr[ix + 1]) * e;
|
||||
arr[ix + 2] = startMorphArr[ix + 2] + (targetsArr[ix + 2] - startMorphArr[ix + 2]) * e;
|
||||
}
|
||||
}
|
||||
attr.needsUpdate = true;
|
||||
}
|
||||
|
||||
const dt = 16;
|
||||
for (let i = 0; i < snowCount; i++) {
|
||||
const spr = snowSprites[i];
|
||||
spr.position.x += snowVel[i * 2] * dt;
|
||||
spr.position.y += snowVel[i * 2 + 1] * dt;
|
||||
spr.material.rotation += snowRot[i];
|
||||
const f = Math.min(1.0, 0.9 + 0.45 * Math.abs(Math.sin(now * 0.0015 + snowOpacityPhase[i])));
|
||||
spr.material.opacity = 0.67 * f;
|
||||
const halo = snowGroup.children[(i * 2) + 1];
|
||||
if (halo) {
|
||||
halo.position.copy(spr.position);
|
||||
halo.material.opacity = Math.min(1.0, 0.47 * f);
|
||||
}
|
||||
if (spr.position.y < -30) {
|
||||
spr.position.set((Math.random() - 0.5) * 120, 20 + Math.random() * 40, -22 - Math.random() * 12);
|
||||
spr.material.rotation = Math.random() * Math.PI * 2;
|
||||
if (halo) {
|
||||
halo.position.copy(spr.position);
|
||||
halo.material.rotation = spr.material.rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateSet(geoLeaves, posLeaves, velLeaves, startMorphLeaves, tgtLeaves, particleCountLeaves);
|
||||
updateSet(geoOrnaments, posOrnaments, velOrnaments, startMorphOrnaments, tgtOrnaments, particleCountOrnaments);
|
||||
updateSet(geoTrunk, posTrunk, velTrunk, startMorphTrunk, tgtTrunk, particleCountTrunk);
|
||||
updateSet(geoStar, posStar, velStar, startMorphStar, tgtStar, particleCountStar);
|
||||
updateSet(geoAccents, posAccents, velAccents, startMorphAccents, tgtAccents, particleCountAccents);
|
||||
group.rotation.y += 0.0032;
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
camera.aspect = w / h;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(w, h);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
fitCamera();
|
||||
}
|
||||
|
||||
function fitCamera() {
|
||||
const aspect = camera.aspect;
|
||||
const vfov = THREE.MathUtils.degToRad(camera.fov);
|
||||
const halfH = (topY - bottomY) * 0.5 * 1.06;
|
||||
const radNeeded = (baseRadius + 1.6) * 1.06;
|
||||
const zH = halfH / Math.tan(vfov / 2);
|
||||
const zW = radNeeded / (Math.tan(vfov / 2) * aspect);
|
||||
const z = Math.max(zH, zW);
|
||||
camera.position.z = Math.max(z, 20);
|
||||
const centerY = (topY + bottomY) * 0.5;
|
||||
camera.position.y = centerY;
|
||||
camera.lookAt(0, centerY, 0);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const el = document.createElement('div');
|
||||
el.id = 'canvas-container';
|
||||
document.getElementById('app').appendChild(el);
|
||||
containerEl.value = el;
|
||||
initThree();
|
||||
setInterval(spawnBarrage, 1200);
|
||||
document.addEventListener('fullscreenchange', () => { isFs.value = !!document.fullscreenElement; });
|
||||
});
|
||||
|
||||
return () => h('div', { style: { width: '100%', height: '100%' } }, [
|
||||
h('div', { class: 'barrage' }, barrageItems.value.map(item => h('div', {
|
||||
key: item.id,
|
||||
class: 'barrage-item',
|
||||
style: {
|
||||
top: item.top,
|
||||
color: item.color,
|
||||
animationDuration: item.duration,
|
||||
}
|
||||
}, item.text))),
|
||||
h('button', { class: 'fullscreen-btn', onClick: toggleFs }, isFs.value ? '退出全屏' : '全屏'),
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
createApp(App).mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
39
InfoGenie-frontend/public/toolbox/网页小玩具/网页挂钟/index.html
Normal file
39
InfoGenie-frontend/public/toolbox/网页小玩具/网页挂钟/index.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
#clock {
|
||||
font-size: 48px;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="clock">00:00:00</div>
|
||||
<script>
|
||||
function updateTime() {
|
||||
const clock = document.getElementById('clock');
|
||||
const now = new Date();
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
clock.textContent = `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
setInterval(updateTime, 1000);
|
||||
updateTime();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user