<!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://cdn.jsdelivr.net/npm/lucide-icons@0.309.0/dist/umd/lucide-icons.min.css">
<style>
/* 基础样式 - 黑白漫画风格 */
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=ZCOOL+KuaiLe&display=swap');
:root {
--color-background: #ffffff;
--color-foreground: #000000;
--color-foreground-muted: #333333;
--color-foreground-subtle: #666666;
--color-surface: #f0f0f0;
--color-surface-hover: #e0e0e0;
--color-surface-active: #d0d0d0;
--color-border: #000000;
--color-accent: #000000;
--shadow-comic: 5px 5px 0px #000000;
--border-comic: 3px solid #000000;
--border-comic-thin: 2px solid #000000;
--border-comic-dashed: 3px dashed #000000;
--font-comic: 'Bangers', 'ZCOOL KuaiLe', cursive, sans-serif;
--font-sans: 'Arial', sans-serif;
--font-mono: 'Courier New', monospace;
}
/* 重置样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-sans);
background-color: var(--color-background);
background-image: url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z' fill='%23000000' fill-opacity='0.03' fill-rule='evenodd'/%3E%3C/svg%3E");
color: var(--color-foreground);
line-height: 1.5;
min-height: 100vh;
}
button, input, select, textarea {
font-family: inherit;
font-size: inherit;
color: inherit;
}
button {
cursor: pointer;
background: none;
border: none;
}
a {
color: inherit;
text-decoration: none;
}
/* 漫画风格元素 */
.comic-title {
font-family: var(--font-comic);
text-transform: uppercase;
letter-spacing: 1px;
transform: rotate(-2deg);
display: inline-block;
position: relative;
z-index: 1;
}
.comic-title::after {
content: '';
position: absolute;
bottom: -5px;
left: 0;
width: 100%;
height: 10px;
background-color: #ffff00;
z-index: -1;
transform: rotate(1deg) skewX(-15deg);
}
.comic-box {
border: var(--border-comic);
box-shadow: var(--shadow-comic);
position: relative;
background-color: white;
transform: rotate(0.5deg);
}
.comic-box-alt {
border: var(--border-comic);
box-shadow: var(--shadow-comic);
position: relative;
background-color: white;
transform: rotate(-0.5deg);
}
.comic-button {
font-family: var(--font-comic);
text-transform: uppercase;
border: var(--border-comic-thin);
background-color: white;
padding: 8px 16px;
position: relative;
box-shadow: 3px 3px 0 #000000;
transform: rotate(-1deg);
transition: all 0.1s;
}
.comic-button:hover {
transform: rotate(0deg) translate(-2px, -2px);
box-shadow: 5px 5px 0 #000000;
}
.comic-button:active {
transform: rotate(0deg) translate(2px, 2px);
box-shadow: 1px 1px 0 #000000;
}
.comic-input {
border: var(--border-comic-thin);
padding: 8px 12px;
background-color: white;
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
}
.comic-input:focus {
outline: none;
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.5);
}
.comic-badge {
font-family: var(--font-comic);
background-color: black;
color: white;
padding: 2px 8px;
transform: rotate(-3deg);
display: inline-block;
}
.comic-divider {
height: 3px;
background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000000' fill-opacity='1' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 5v1H5z'/%3E%3Cpath d='M6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
margin: 20px 0;
}
.comic-speech-bubble {
position: relative;
background-color: white;
border: var(--border-comic-thin);
padding: 15px;
border-radius: 30px;
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
}
.comic-speech-bubble::after {
content: '';
position: absolute;
bottom: -20px;
left: 30px;
width: 30px;
height: 30px;
background-color: white;
border-right: var(--border-comic-thin);
border-bottom: var(--border-comic-thin);
transform: rotate(45deg) skew(10deg, 10deg);
z-index: -1;
}
/* 布局 */
.app-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.container {
width: 100%;
max-width: 1280px;
margin: 0 auto;
padding: 0 20px;
}
.main-content {
display: flex;
flex-direction: column;
gap: 24px;
padding-top: 24px;
padding-bottom: 24px;
}
@media (min-width: 768px) {
.main-content {
flex-direction: row;
}
}
/* 头部 */
.header {
position: sticky;
top: 0;
z-index: 10;
background-color: white;
border-bottom: var(--border-comic);
padding: 10px 0;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo-container {
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 50px;
height: 50px;
background-color: black;
border: var(--border-comic-thin);
display: flex;
align-items: center;
justify-content: center;
transform: rotate(-5deg);
}
.logo-icon svg {
width: 30px;
height: 30px;
color: white;
}
.logo-title {
font-family: var(--font-comic);
font-size: 24px;
transform: rotate(-2deg);
}
.logo-subtitle {
font-size: 12px;
color: var(--color-foreground-muted);
transform: rotate(-2deg);
}
.header-actions {
display: none;
}
@media (min-width: 768px) {
.header-actions {
display: flex;
align-items: center;
gap: 8px;
}
}
.search-container {
position: relative;
width: 250px;
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--color-foreground-muted);
width: 16px;
height: 16px;
}
.search-input {
width: 100%;
border: var(--border-comic-thin);
border-radius: 20px;
padding: 6px 16px 6px 32px;
background-color: white;
}
.icon-button {
width: 40px;
height: 40px;
border: var(--border-comic-thin);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: white;
box-shadow: 2px 2px 0 #000000;
}
.icon-button:hover {
transform: translate(-1px, -1px);
box-shadow: 3px 3px 0 #000000;
}
.icon-button svg {
width: 20px;
height: 20px;
}
.menu-button {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border: var(--border-comic-thin);
background-color: white;
box-shadow: 2px 2px 0 #000000;
}
.menu-button:hover {
transform: translate(-1px, -1px);
box-shadow: 3px 3px 0 #000000;
}
.menu-button svg {
width: 24px;
height: 24px;
}
@media (min-width: 768px) {
.menu-button {
display: none;
}
}
/* 下拉菜单 */
.dropdown {
position: relative;
}
.dropdown-content {
position: absolute;
top: 100%;
right: 0;
margin-top: 8px;
background-color: white;
border: var(--border-comic);
box-shadow: var(--shadow-comic);
min-width: 160px;
z-index: 20;
display: none;
}
.dropdown.active .dropdown-content {
display: block;
}
.dropdown-item {
display: flex;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid #000;
}
.dropdown-item:last-child {
border-bottom: none;
}
.dropdown-item:hover {
background-color: var(--color-surface-hover);
}
.dropdown-item svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
/* 侧边栏 */
.sidebar {
display: none;
flex-shrink: 0;
transition: all 0.3s ease-in-out;
overflow: hidden;
border: var(--border-comic);
background-color: white;
box-shadow: var(--shadow-comic);
transform: rotate(-0.5deg);
}
.sidebar.open {
display: block;
}
@media (min-width: 768px) {
.sidebar {
display: block;
width: 250px;
}
}
.sidebar-content {
display: flex;
flex-direction: column;
gap: 24px;
padding: 20px;
}
.main-nav,
.category-nav {
display: flex;
flex-direction: column;
gap: 4px;
}
.category-title {
font-family: var(--font-comic);
font-size: 18px;
margin-bottom: 8px;
text-transform: uppercase;
position: relative;
display: inline-block;
}
.category-title::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 5px;
background-color: #ffff00;
z-index: -1;
}
.nav-button {
display: flex;
align-items: center;
padding: 8px 12px;
border: 1px solid transparent;
border-radius: 4px;
transition: all 0.2s;
text-align: left;
width: 100%;
font-weight: 500;
}
.nav-button:hover {
border-color: #000;
background-color: var(--color-surface-hover);
transform: translate(-2px, -2px);
box-shadow: 2px 2px 0 #000000;
}
.nav-button.active {
border: 1px solid #000;
background-color: var(--color-surface);
transform: translate(-2px, -2px);
box-shadow: 2px 2px 0 #000000;
}
.nav-button svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
.sidebar-footer {
color: var(--color-foreground-muted);
font-size: 12px;
border-top: 1px dashed #000;
padding-top: 16px;
}
.footer-links {
display: flex;
gap: 16px;
margin-top: 8px;
}
.footer-links a {
text-decoration: underline;
}
.footer-links a:hover {
color: var(--color-foreground);
}
/* 主内容 */
.main {
flex: 1;
display: flex;
flex-direction: column;
gap: 24px;
}
.breadcrumb {
font-size: 14px;
color: var(--color-foreground-muted);
margin-bottom: 16px;
font-style: italic;
}
/* 工具容器 */
.tool-container {
margin-bottom: 24px;
}
.tool-section {
border: var(--border-comic);
background-color: white;
box-shadow: var(--shadow-comic);
overflow: hidden;
}
.tool-header {
background-color: #000;
padding: 16px 24px;
position: relative;
overflow: hidden;
}
.tool-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='0.1' fill-rule='evenodd'%3E%3Ccircle cx='3' cy='3' r='3'/%3E%3Ccircle cx='13' cy='13' r='3'/%3E%3C/g%3E%3C/svg%3E");
}
.tool-title {
font-family: var(--font-comic);
font-size: 24px;
color: white;
text-transform: uppercase;
position: relative;
}
.tool-description {
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
position: relative;
}
.tool-content {
padding: 24px;
display: flex;
flex-direction: column;
gap: 24px;
}
/* 转换步骤 */
.conversion-steps {
display: flex;
align-items: center;
justify-content: space-between;
}
.steps-container {
display: flex;
align-items: center;
}
.step-item {
display: flex;
align-items: center;
}
.step-icon {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border: 2px solid #000;
}
.step-icon.active {
background-color: #ffff00;
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.step-icon.completed {
background-color: #000;
color: white;
}
.step-line {
height: 2px;
width: 48px;
background-color: #ccc;
}
.step-line.active {
background-color: #000;
}
.reset-button {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 12px;
border: 1px solid #000;
border-radius: 4px;
font-size: 14px;
background-color: white;
}
.reset-button:hover {
background-color: var(--color-surface-hover);
}
.reset-button svg {
width: 16px;
height: 16px;
}
/* 上传区域 */
.upload-area {
border: var(--border-comic-dashed);
border-radius: 8px;
padding: 32px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
background-color: #f9f9f9;
}
.upload-area:hover {
background-color: #f0f0f0;
transform: scale(1.01);
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.upload-icon {
width: 64px;
height: 64px;
background-color: #000;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.upload-icon svg {
width: 32px;
height: 32px;
}
.upload-primary {
font-weight: 700;
font-size: 18px;
}
.upload-secondary {
font-size: 14px;
color: var(--color-foreground-subtle);
}
.upload-button {
position: relative;
padding: 8px 16px;
border: 2px solid #000;
background-color: white;
font-weight: 500;
box-shadow: 3px 3px 0 #000;
}
.upload-button:hover {
transform: translate(-1px, -1px);
box-shadow: 4px 4px 0 #000;
}
.file-input {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
}
/* 转换区域 */
.conversion-grid {
display: grid;
grid-template-columns: 1fr;
gap: 24px;
}
@media (min-width: 768px) {
.conversion-grid {
grid-template-columns: 1fr 1fr;
}
}
.preview-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.image-preview {
aspect-ratio: 1 / 1;
background-color: #f0f0f0;
border: var(--border-comic-thin);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.image-preview img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.file-info {
font-size: 14px;
color: var(--color-foreground-muted);
border: 1px dashed #000;
padding: 8px;
background-color: #f9f9f9;
}
/* 转换设置 */
.conversion-settings {
display: flex;
flex-direction: column;
gap: 24px;
}
.settings-group {
display: flex;
flex-direction: column;
gap: 16px;
border: 1px solid #000;
padding: 16px;
background-color: #f9f9f9;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-label {
font-size: 14px;
font-weight: 700;
}
.form-select {
width: 100%;
padding: 8px 12px;
border: 2px solid #000;
background-color: white;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
}
.slider-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.slider-value {
font-size: 14px;
font-weight: 700;
}
.slider {
width: 100%;
height: 8px;
background-color: #ccc;
border-radius: 4px;
appearance: none;
margin: 8px 0;
}
.slider::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
background-color: #000;
border-radius: 50%;
cursor: pointer;
border: 2px solid white;
}
.options-group {
display: flex;
justify-content: space-between;
}
.option-item {
display: flex;
align-items: center;
gap: 8px;
}
.checkbox {
appearance: none;
width: 18px;
height: 18px;
border: 2px solid #000;
background-color: white;
cursor: pointer;
position: relative;
}
.checkbox:checked {
background-color: #000;
}
.checkbox:checked::after {
content: '';
position: absolute;
left: 5px;
top: 2px;
width: 6px;
height: 10px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.checkbox-label {
font-size: 14px;
}
/* 进度条 */
.progress-bar {
width: 100%;
height: 10px;
background-color: #f0f0f0;
border: 1px solid #000;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #000;
transition: width 0.3s ease;
}
.progress-fill-green {
background-color: #000;
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='0.2' fill-rule='evenodd'%3E%3Cpath d='M0 20L20 0h20v20L20 40H0z'/%3E%3C/g%3E%3C/svg%3E");
}
.progress-fill-yellow {
background-color: #000;
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='0.2' fill-rule='evenodd'%3E%3Cpath d='M0 38.59l2.83-2.83 1.41 1.41L1.41 40H0v-1.41zM0 1.4l2.83 2.83 1.41-1.41L1.41 0H0v1.41zM38.59 40l-2.83-2.83 1.41-1.41L40 38.59V40h-1.41zM40 1.41l-2.83 2.83-1.41-1.41L38.59 0H40v1.41zM20 18.6l2.83-2.83 1.41 1.41L21.41 20l2.83 2.83-1.41 1.41L20 21.41l-2.83 2.83-1.41-1.41L18.59 20l-2.83-2.83 1.41-1.41L20 18.59z'/%3E%3C/g%3E%3C/svg%3E");
}
.progress-text {
font-size: 14px;
text-align: center;
margin-top: 8px;
font-weight: 500;
}
/* 成功消息 */
.success-message {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px;
background-color: #f0f0f0;
border: 2px solid #000;
font-weight: 700;
}
.success-message svg {
width: 20px;
height: 20px;
}
/* 按钮 */
.convert-button,
.download-button,
.translate-button,
.format-button {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 16px;
background-color: #000;
color: white;
font-weight: 700;
border: none;
position: relative;
overflow: hidden;
z-index: 1;
}
.convert-button::before,
.download-button::before,
.translate-button::before,
.format-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='0.1' fill-rule='evenodd'%3E%3Cpath d='M0 20L20 0h20v20L20 40H0z'/%3E%3C/g%3E%3C/svg%3E");
z-index: -1;
transition: all 0.3s;
}
.convert-button:hover::before,
.download-button:hover::before,
.translate-button:hover::before,
.format-button:hover::before {
left: 0;
}
.convert-button:disabled,
.translate-button:disabled,
.format-button:disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.convert-button svg,
.download-button svg,
.translate-button svg,
.format-button svg {
width: 16px;
height: 16px;
}
/* 文本翻译工具 */
.language-controls {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}
.language-selector-group {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.language-select {
padding: 8px 12px;
border: 2px solid #000;
background-color: white;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
}
.swap-button {
width: 32px;
height: 32px;
border: 2px solid #000;
display: flex;
align-items: center;
justify-content: center;
background-color: white;
}
.swap-button:hover {
background-color: #f0f0f0;
}
.swap-button svg {
width: 16px;
height: 16px;
}
.clear-button {
padding: 4px 12px;
border: 1px solid #000;
background-color: white;
font-size: 14px;
}
.clear-button:hover {
background-color: #f0f0f0;
}
.translator-grid {
display: grid;
grid-template-columns: 1fr;
gap: 24px;
}
@media (min-width: 768px) {
.translator-grid {
grid-template-columns: 1fr 1fr;
}
}
.text-area-container {
display: flex;
flex-direction: column;
gap: 8px;
}
.text-area-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.text-area-label {
font-size: 14px;
font-weight: 700;
}
.text-area {
width: 100%;
height: 160px;
padding: 12px;
border: 2px solid #000;
background-color: white;
resize: none;
font-family: var(--font-sans);
}
.text-area:focus {
outline: none;
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.1);
}
.text-area-footer {
font-size: 12px;
color: var(--color-foreground-subtle);
text-align: right;
}
.copy-button {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
font-size: 12px;
border: 1px solid #000;
background-color: white;
}
.copy-button:hover {
background-color: #f0f0f0;
}
.copy-button svg {
width: 12px;
height: 12px;
}
/* JSON 格式化工具 */
.json-controls {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}
.json-options {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
.indent-selector {
display: flex;
align-items: center;
gap: 8px;
}
.option-label {
font-size: 14px;
font-weight: 500;
}
.indent-select {
background-color: white;
border: 2px solid #000;
padding: 4px 8px;
font-size: 14px;
}
.sort-option {
display: flex;
align-items: center;
gap: 8px;
}
.sample-button {
padding: 4px 12px;
border: 1px solid #000;
background-color: white;
font-size: 14px;
}
.sample-button:hover {
background-color: #f0f0f0;
}
.json-grid {
display: grid;
grid-template-columns: 1fr;
gap: 24px;
}
@media (min-width: 768px) {
.json-grid {
grid-template-columns: 1fr 1fr;
}
}
.json-area-container {
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
}
.json-area-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.json-area-label {
font-size: 14px;
font-weight: 700;
}
.json-actions {
display: flex;
gap: 8px;
}
.json-action-button {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
font-size: 12px;
border: 1px solid #000;
background-color: white;
}
.json-action-button:hover {
background-color: #f0f0f0;
}
.json-action-button svg {
width: 12px;
height: 12px;
}
.json-area {
width: 100%;
height: 240px;
padding: 12px;
border: 2px solid #000;
background-color: white;
resize: none;
font-family: var(--font-mono);
font-size: 14px;
}
.json-area:focus {
outline: none;
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.1);
}
.json-error {
position: absolute;
inset: 0;
margin-top: 40px;
padding: 12px;
background-color: #fff0f0;
border: 2px solid #ff0000;
color: #ff0000;
font-size: 14px;
overflow: auto;
}
/* 二维码生成器 */
.qrcode-container {
display: flex;
flex-direction: column;
gap: 24px;
}
.qrcode-input-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.qrcode-input {
width: 100%;
padding: 12px;
border: 2px solid #000;
background-color: white;
}
.qrcode-options {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin-top: 8px;
}
.qrcode-preview {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
padding: 24px;
border: 2px solid #000;
background-color: white;
}
.qrcode-image {
width: 200px;
height: 200px;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #000;
}
/* 密码生成器 */
.password-container {
display: flex;
flex-direction: column;
gap: 24px;
}
.password-result {
position: relative;
}
.password-display {
width: 100%;
padding: 12px;
border: 2px solid #000;
background-color: white;
font-family: var(--font-mono);
font-size: 18px;
text-align: center;
letter-spacing: 2px;
}
.password-actions {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
display: flex;
gap: 8px;
}
.password-action {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #000;
background-color: white;
}
.password-action:hover {
background-color: #f0f0f0;
}
.password-action svg {
width: 16px;
height: 16px;
}
.password-options {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 768px) {
.password-options {
grid-template-columns: 1fr 1fr;
}
}
.password-length {
display: flex;
flex-direction: column;
gap: 8px;
}
.password-checkboxes {
display: flex;
flex-direction: column;
gap: 12px;
}
/* 颜色选择器 */
.color-picker-container {
display: flex;
flex-direction: column;
gap: 24px;
}
.color-display {
height: 100px;
border: 2px solid #000;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-mono);
font-size: 24px;
color: white;
text-shadow: 1px 1px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;
}
.color-inputs {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 768px) {
.color-inputs {
grid-template-columns: 1fr 1fr 1fr;
}
}
.color-input-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.color-input-label {
font-size: 14px;
font-weight: 700;
}
.color-input {
width: 100%;
padding: 8px;
border: 2px solid #000;
background-color: white;
font-family: var(--font-mono);
}
.color-formats {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 16px;
}
.color-format {
display: flex;
justify-content: space-between;
padding: 8px;
border: 1px solid #000;
background-color: #f9f9f9;
}
.color-format-label {
font-weight: 700;
}
.color-format-value {
font-family: var(--font-mono);
}
/* 单位转换工具 */
.unit-converter-container {
display: flex;
flex-direction: column;
gap: 24px;
}
.unit-converter-type {
display: flex;
overflow-x: auto;
gap: 8px;
padding-bottom: 8px;
}
.unit-type-button {
padding: 8px 16px;
border: 2px solid #000;
background-color: white;
white-space: nowrap;
}
.unit-type-button.active {
background-color: #000;
color: white;
}
.unit-converter-inputs {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 768px) {
.unit-converter-inputs {
grid-template-columns: 1fr 1fr;
}
}
.unit-input-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.unit-input-label {
font-size: 14px;
font-weight: 700;
}
.unit-input-container {
display: flex;
}
.unit-input {
flex: 1;
padding: 8px 12px;
border: 2px solid #000;
border-right: none;
background-color: white;
}
.unit-select {
width: 100px;
padding: 8px;
border: 2px solid #000;
background-color: white;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
}
.unit-swap {
display: flex;
align-items: center;
justify-content: center;
margin: 16px 0;
}
.unit-swap-button {
width: 40px;
height: 40px;
border: 2px solid #000;
background-color: white;
display: flex;
align-items: center;
justify-content: center;
}
.unit-swap-button:hover {
background-color: #f0f0f0;
}
.unit-swap-button svg {
width: 20px;
height: 20px;
}
/* 倒计时/计时器 */
.timer-container {
display: flex;
flex-direction: column;
gap: 24px;
}
.timer-tabs {
display: flex;
border-bottom: 2px solid #000;
}
.timer-tab {
padding: 8px 16px;
border: 2px solid #000;
border-bottom: none;
background-color: white;
margin-right: 8px;
position: relative;
bottom: -2px;
}
.timer-tab.active {
background-color: #000;
color: white;
}
.timer-display {
font-family: var(--font-mono);
font-size: 48px;
text-align: center;
padding: 24px;
border: 2px solid #000;
background-color: white;
letter-spacing: 2px;
}
.timer-controls {
display: flex;
justify-content: center;
gap: 16px;
}
.timer-button {
padding: 8px 16px;
border: 2px solid #000;
background-color: white;
display: flex;
align-items: center;
gap: 8px;
font-weight: 700;
}
.timer-button:hover {
background-color: #f0f0f0;
}
.timer-button svg {
width: 16px;
height: 16px;
}
.timer-input-group {
display: flex;
justify-content: center;
gap: 8px;
}
.timer-input {
width: 80px;
padding: 8px;
border: 2px solid #000;
background-color: white;
text-align: center;
font-family: var(--font-mono);
font-size: 18px;
}
.timer-input-label {
font-size: 12px;
text-align: center;
margin-top: 4px;
}
/* 工具列表 */
.tools-section {
border: var(--border-comic);
background-color: white;
box-shadow: var(--shadow-comic);
transform: rotate(0.5deg);
}
.section-header {
padding: 16px 24px;
border-bottom: var(--border-comic-thin);
background-color: #f9f9f9;
}
.section-title {
font-family: var(--font-comic);
font-size: 20px;
text-transform: uppercase;
}
.tabs {
padding: 24px;
}
.tabs-list {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 4px;
margin-bottom: 24px;
background-color: #f0f0f0;
border: 1px solid #000;
padding: 4px;
}
.tab-button {
padding: 8px 0;
text-align: center;
font-weight: 500;
transition: all 0.2s;
}
.tab-button:hover {
background-color: #e0e0e0;
}
.tab-button.active {
background-color: #000;
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
#tools-grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 768px) {
#tools-grid {
grid-template-columns: repeat(3, 1fr);
}
}
.tool-card {
border: 2px solid #000;
background-color: white;
padding: 20px;
transition: all 0.2s;
cursor: pointer;
position: relative;
box-shadow: 4px 4px 0 #000;
}
.tool-card:hover {
transform: translate(-2px, -2px);
box-shadow: 6px 6px 0 #000;
}
.tool-card.active {
background-color: #f9f9f9;
transform: translate(-2px, -2px);
box-shadow: 6px 6px 0 #000;
}
.tool-card-icon {
width: 40px;
height: 40px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.tool-card-icon svg {
width: 20px;
height: 20px;
}
.tool-card-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 4px;
}
.tool-card-description {
font-size: 14px;
color: var(--color-foreground-muted);
margin-bottom: 12px;
}
.tool-card-button {
font-size: 14px;
text-decoration: underline;
font-weight: 500;
}
/* 底部区域 */
.bottom-sections {
display: grid;
grid-template-columns: 1fr;
gap: 24px;
}
@media (min-width: 768px) {
.bottom-sections {
grid-template-columns: 1fr 1fr;
}
}
.info-section {
border: var(--border-comic);
background-color: white;
box-shadow: var(--shadow-comic);
transform: rotate(-0.5deg);
}
.section-content {
padding: 24px;
}
.tool-list {
list-style: none;
display: flex;
flex-direction: column;
gap: 12px;
}
.tool-list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
border: 1px solid #000;
background-color: white;
transition: all 0.2s;
}
.tool-list-item:hover {
background-color: #f9f9f9;
transform: translate(-2px, -2px);
box-shadow: 2px 2px 0 #000;
}
.tool-indicator {
width: 8px;
height: 8px;
background-color: #000;
border-radius: 50%;
margin-right: 12px;
}
.tool-meta {
display: flex;
align-items: center;
gap: 8px;
}
.tool-date {
font-size: 12px;
color: var(--color-foreground-subtle);
}
.tool-tag {
font-size: 10px;
background-color: #000;
color: white;
padding: 2px 8px;
text-transform: uppercase;
transform: rotate(-2deg);
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
border: 2px solid #000;
background-color: white;
padding: 16px;
box-shadow: 3px 3px 0 #000;
}
.stat-value {
font-size: 32px;
font-weight: 700;
margin-bottom: 4px;
font-family: var(--font-comic);
}
.stat-label {
font-size: 14px;
color: var(--color-foreground-muted);
}
.progress-stats {
display: flex;
flex-direction: column;
gap: 16px;
}
.progress-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.progress-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
margin-bottom: 4px;
}
/* 漫画风格装饰 */
.comic-dots {
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000000' fill-opacity='0.1' fill-rule='evenodd'%3E%3Ccircle cx='3' cy='3' r='3'/%3E%3Ccircle cx='13' cy='13' r='3'/%3E%3C/g%3E%3C/svg%3E");
}
.comic-lines {
background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000000' fill-opacity='0.1' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 5v1H5z'/%3E%3Cpath d='M6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
}
.comic-zigzag {
background-image: url("data:image/svg+xml,%3Csvg width='40' height='12' viewBox='0 0 40 12' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 6.172L6.172 0h5.656L0 11.828V6.172zm40 5.656L28.172 0h5.656L40 6.172v5.656zM6.172 12l12-12h3.656l12 12h-5.656L20 3.828 11.828 12H6.172zm12 0L20 10.172 21.828 12h-3.656z' fill='%23000000' fill-opacity='0.1' fill-rule='evenodd'/%3E%3C/svg%3E");
}
.comic-burst {
position: relative;
}
.comic-burst::before {
content: '';
position: absolute;
top: -10px;
right: -10px;
width: 60px;
height: 60px;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M50 0 L60 40 L100 50 L60 60 L50 100 L40 60 L0 50 L40 40 Z' fill='%23ffff00' stroke='%23000000' stroke-width='3'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
transform: rotate(15deg);
z-index: 1;
}
.comic-burst-text {
position: absolute;
top: 5px;
right: 5px;
font-family: var(--font-comic);
font-size: 12px;
transform: rotate(15deg);
z-index: 2;
}
/* 动画效果 */
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.shake {
animation: shake 0.5s;
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.shake {
animation: shake 0.5s;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.bounce {
animation: bounce 0.5s;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spin {
animation: spin 1s linear infinite;
}
</style>
<!-- 工具模板 -->
<template id="image-converter-template">
<section class="tool-section image-converter">
<div class="tool-header">
<h2 class="tool-title">图片格式转换</h2>
<p class="tool-description">支持多种格式互转,快速简便</p>
</div>
<div class="tool-content">
<!-- 转换流程 -->
<div class="conversion-steps">
<div class="steps-container">
<div class="step-item" id="step-upload">
<div class="step-icon">
<i data-lucide="check"></i>
</div>
<div class="step-line"></div>
</div>
<div class="step-item" id="step-convert">
<div class="step-icon">
<i data-lucide="refresh-cw"></i>
</div>
<div class="step-line"></div>
</div>
<div class="step-item" id="step-download">
<div class="step-icon">
<i data-lucide="download"></i>
</div>
</div>
</div>
<button class="reset-button" id="reset-conversion">
<i data-lucide="refresh-cw"></i> 重置
</button>
</div>
<!-- 上传区域 -->
<div class="upload-area" id="upload-area">
<div class="upload-content">
<div class="upload-icon">
<i data-lucide="upload"></i>
</div>
<div class="upload-text">
<p class="upload-primary">拖放图片到这里,或点击上传</p>
<p class="upload-secondary">支持 JPG, PNG, WEBP, GIF, SVG 等格式</p>
</div>
<button class="upload-button">
选择文件
<input type="file" id="file-input" accept="image/*" class="file-input">
</button>
</div>
</div>
<!-- 转换区域 -->
<div class="conversion-area" id="conversion-area" style="display: none;">
<div class="conversion-grid">
<!-- 预览区 -->
<div class="preview-container">
<div class="image-preview" id="image-preview">
<!-- 图片预览将在这里显示 -->
</div>
<div class="file-info" id="file-info">
<!-- 文件信息将在这里显示 -->
</div>
</div>
<!-- 转换设置 -->
<div class="conversion-settings">
<div class="settings-group">
<div class="form-group">
<label class="form-label">转换为</label>
<select class="form-select" id="format-select">
<option value="png">PNG</option>
<option value="jpg">JPG</option>
<option value="webp">WEBP</option>
<option value="gif">GIF</option>
<option value="svg">SVG</option>
</select>
</div>
<div class="form-group">
<div class="slider-header">
<label class="form-label">图片质量</label>
<span class="slider-value" id="quality-value">80%</span>
</div>
<input type="range" min="10" max="100" value="80" class="slider" id="quality-slider">
</div>
<div class="options-group">
<div class="option-item">
<input type="checkbox" id="maintain-ratio" class="checkbox">
<label for="maintain-ratio" class="checkbox-label">保持宽高比</label>
</div>
<div class="option-item">
<input type="checkbox" id="optimize" class="checkbox">
<label for="optimize" class="checkbox-label">优化大小</label>
</div>
</div>
</div>
<div class="conversion-progress" id="conversion-progress" style="display: none;">
<div class="progress-bar">
<div class="progress-fill" id="progress-bar-fill" style="width: 0%;"></div>
</div>
<p class="progress-text" id="progress-text">转换中... 0%</p>
</div>
<div class="conversion-success" id="conversion-success" style="display: none;">
<div class="success-message">
<i data-lucide="check"></i>
<span>转换完成!</span>
</div>
<button class="download-button" id="download-button">
<i data-lucide="download"></i> 下载 <span id="download-format">PNG</span> 文件
</button>
</div>
<button class="convert-button" id="convert-button">
<i data-lucide="refresh-cw"></i> 开始转换
</button>
</div>
</div>
</div>
</div>
</section>
</template>
<template id="text-translator-template">
<section class="tool-section text-translator">
<div class="tool-header">
<h2 class="tool-title">文本翻译</h2>
<p class="tool-description">支持多种语言互译,快速准确</p>
</div>
<div class="tool-content">
<div class="language-controls">
<div class="language-selector-group">
<select class="language-select" id="source-language">
<option value="zh">中文</option>
<option value="en">英语</option>
<option value="ja">日语</option>
<option value="ko">韩语</option>
<option value="fr">法语</option>
<option value="de">德语</option>
<option value="es">西班牙语</option>
<option value="ru">俄语</option>
</select>
<button class="swap-button" id="swap-languages">
<i data-lucide="arrow-right"></i>
</button>
<select class="language-select" id="target-language">
<option value="zh">中文</option>
<option value="en" selected>英语</option>
<option value="ja">日语</option>
<option value="ko">韩语</option>
<option value="fr">法语</option>
<option value="de">德语</option>
<option value="es">西班牙语</option>
<option value="ru">俄语</option>
</select>
</div>
<button class="clear-button" id="clear-text">
清空
</button>
</div>
<div class="translator-grid">
<div class="text-area-container">
<label class="text-area-label">输入文本</label>
<textarea class="text-area" id="source-text" placeholder="请输入要翻译的文本..."></textarea>
<div class="text-area-footer">
<span class="character-count" id="source-count">0</span> 个字符
</div>
</div>
<div class="text-area-container">
<div class="text-area-header">
<label class="text-area-label">翻译结果</label>
<button class="copy-button" id="copy-result" style="display: none;">
<i data-lucide="copy"></i>
<span>复制</span>
</button>
</div>
<textarea class="text-area" id="translated-text" placeholder="翻译结果将显示在这里..." readonly></textarea>
</div>
</div>
<div class="translation-progress" id="translation-progress" style="display: none;">
<div class="progress-bar">
<div class="progress-fill" id="translation-progress-bar" style="width: 0%;"></div>
</div>
<p class="progress-text" id="translation-progress-text">翻译中... 0%</p>
</div>
<button class="translate-button" id="translate-button">
<i data-lucide="languages"></i> 开始翻译
</button>
</div>
</section>
</template>
<template id="json-formatter-template">
<section class="tool-section json-formatter">
<div class="tool-header">
<h2 class="tool-title">JSON 格式化</h2>
<p class="tool-description">格式化和验证 JSON 数据</p>
</div>
<div class="tool-content">
<div class="json-controls">
<div class="json-options">
<div class="indent-selector">
<label for="indent-size" class="option-label">缩进大小:</label>
<select id="indent-size" class="indent-select">
<option value="2">2</option>
<option value="4">4</option>
<option value="8">8</option>
</select>
</div>
<div class="sort-option">
<input type="checkbox" id="sort-keys" class="checkbox">
<label for="sort-keys" class="checkbox-label">排序键名</label>
</div>
</div>
<button class="sample-button" id="sample-data">
示例数据
</button>
</div>
<div class="json-grid">
<div class="json-area-container">
<label class="json-area-label">输入 JSON</label>
<textarea class="json-area" id="json-input" placeholder="请输入 JSON 数据..."></textarea>
</div>
<div class="json-area-container">
<div class="json-area-header">
<label class="json-area-label">格式化结果</label>
<div class="json-actions" id="json-actions" style="display: none;">
<button class="json-action-button" id="copy-json">
<i data-lucide="copy"></i>
<span>复制</span>
</button>
<button class="json-action-button" id="download-json">
<i data-lucide="download"></i>
<span>下载</span>
</button>
</div>
</div>
<textarea class="json-area" id="json-output" placeholder="格式化结果将显示在这里..." readonly></textarea>
<div class="json-error" id="json-error" style="display: none;"></div>
</div>
</div>
<button class="format-button" id="format-json">
<i data-lucide="code"></i> 格式化 JSON
</button>
</div>
</section>
</template>
<!-- 新增工具模板 -->
<template id="qrcode-generator-template">
<section class="tool-section qrcode-generator">
<div class="tool-header">
<h2 class="tool-title">二维码生成器</h2>
<p class="tool-description">快速生成自定义二维码</p>
</div>
<div class="tool-content">
<div class="qrcode-container">
<div class="qrcode-input-group">
<label class="form-label">输入内容</label>
<input type="text" id="qrcode-input" class="qrcode-input" placeholder="输入网址、文本或联系方式...">
<div class="qrcode-options">
<div class="option-item">
<input type="checkbox" id="qrcode-logo" class="checkbox">
<label for="qrcode-logo" class="checkbox-label">添加Logo</label>
</div>
<div class="form-group" style="width: 120px;">
<label class="form-label">尺寸</label>
<select class="form-select" id="qrcode-size">
<option value="128">小</option>
<option value="200" selected>中</option>
<option value="300">大</option>
</select>
</div>
</div>
</div>
<div class="qrcode-preview">
<div class="qrcode-image" id="qrcode-result">
<p>二维码将显示在这里</p>
</div>
<button class="download-button" id="download-qrcode" disabled>
<i data-lucide="download"></i> 下载二维码
</button>
</div>
</div>
</div>
</section>
</template>
<template id="password-generator-template">
<section class="tool-section password-generator">
<div class="tool-header">
<h2 class="tool-title">密码生成器</h2>
<p class="tool-description">生成安全、强壮的随机密码</p>
</div>
<div class="tool-content">
<div class="password-container">
<div class="password-result">
<div class="password-display" id="password-display">点击生成按钮创建密码</div>
<div class="password-actions">
<button class="password-action" id="copy-password" title="复制密码">
<i data-lucide="copy"></i>
</button>
<button class="password-action" id="refresh-password" title="重新生成">
<i data-lucide="refresh-cw"></i>
</button>
</div>
</div>
<div class="password-options">
<div class="password-length">
<div class="slider-header">
<label class="form-label">密码长度</label>
<span class="slider-value" id="length-value">12</span>
</div>
<input type="range" min="6" max="32" value="12" class="slider" id="length-slider">
</div>
<div class="password-checkboxes">
<div class="option-item">
<input type="checkbox" id="include-uppercase" class="checkbox" checked>
<label for="include-uppercase" class="checkbox-label">包含大写字母 (A-Z)</label>
</div>
<div class="option-item">
<input type="checkbox" id="include-lowercase" class="checkbox" checked>
<label for="include-lowercase" class="checkbox-label">包含小写字母 (a-z)</label>
</div>
<div class="option-item">
<input type="checkbox" id="include-numbers" class="checkbox" checked>
<label for="include-numbers" class="checkbox-label">包含数字 (0-9)</label>
</div>
<div class="option-item">
<input type="checkbox" id="include-symbols" class="checkbox">
<label for="include-symbols" class="checkbox-label">包含特殊符号 (!@#$%)</label>
</div>
</div>
</div>
<button class="convert-button" id="generate-password">
<i data-lucide="key"></i> 生成安全密码
</button>
</div>
</div>
</section>
</template>
<template id="color-picker-template">
<section class="tool-section color-picker">
<div class="tool-header">
<h2 class="tool-title">颜色选择器</h2>
<p class="tool-description">选择、转换和复制颜色代码</p>
</div>
<div class="tool-content">
<div class="color-picker-container">
<div class="color-display" id="color-display" style="background-color: #3498db;">
#3498db
</div>
<div class="color-inputs">
<div class="color-input-group">
<label class="color-input-label">HEX</label>
<input type="text" id="hex-input" class="color-input" value="#3498db">
</div>
<div class="color-input-group">
<label class="color-input-label">RGB</label>
<input type="text" id="rgb-input" class="color-input" value="52, 152, 219">
</div>
<div class="color-input-group">
<label class="color-input-label">HSL</label>
<input type="text" id="hsl-input" class="color-input" value="204, 70%, 53%">
</div>
</div>
<div class="form-group">
<label class="form-label">颜色选择器</label>
<input type="color" id="color-picker-input" value="#3498db" style="width: 100%; height: 40px;">
</div>
<div class="color-formats">
<div class="color-format">
<span class="color-format-label">CSS 变量:</span>
<span class="color-format-value" id="css-var-value">--color: #3498db;</span>
</div>
<div class="color-format">
<span class="color-format-label">Tailwind:</span>
<span class="color-format-value" id="tailwind-value">bg-blue-500</span>
</div>
</div>
</div>
</div>
</section>
</template>
<template id="unit-converter-template">
<section class="tool-section unit-converter">
<div class="tool-header">
<h2 class="tool-title">单位转换工具</h2>
<p class="tool-description">快速转换各种计量单位</p>
</div>
<div class="tool-content">
<div class="unit-converter-container">
<div class="unit-converter-type" id="unit-types">
<button class="unit-type-button active" data-type="length">长度</button>
<button class="unit-type-button" data-type="weight">重量</button>
<button class="unit-type-button" data-type="temperature">温度</button>
<button class="unit-type-button" data-type="area">面积</button>
<button class="unit-type-button" data-type="volume">体积</button>
<button class="unit-type-button" data-type="time">时间</button>
</div>
<div class="unit-converter-inputs">
<div class="unit-input-group">
<label class="unit-input-label">从</label>
<div class="unit-input-container">
<input type="number" id="from-value" class="unit-input" value="1">
<select id="from-unit" class="unit-select">
<option value="m">米</option>
<option value="km">千米</option>
<option value="cm">厘米</option>
<option value="mm">毫米</option>
<option value="in">英寸</option>
<option value="ft">英尺</option>
<option value="yd">码</option>
<option value="mi">英里</option>
</select>
</div>
</div>
<div class="unit-swap">
<button class="unit-swap-button" id="swap-units">
<i data-lucide="arrow-down-up"></i>
</button>
</div>
<div class="unit-input-group">
<label class="unit-input-label">到</label>
<div class="unit-input-container">
<input type="number" id="to-value" class="unit-input" readonly>
<select id="to-unit" class="unit-select">
<option value="m">米</option>
<option value="km">千米</option>
<option value="cm" selected>厘米</option>
<option value="mm">毫米</option>
<option value="in">英寸</option>
<option value="ft">英尺</option>
<option value="yd">码</option>
<option value="mi">英里</option>
</select>
</div>
</div>
</div>
<div class="comic-speech-bubble" style="margin-top: 20px;">
<p id="conversion-formula">1米 = 100厘米</p>
</div>
</div>
</div>
</section>
</template>
<template id="timer-template">
<section class="tool-section timer">
<div class="tool-header">
<h2 class="tool-title">倒计时/计时器</h2>
<p class="tool-description">简单易用的时间工具</p>
</div>
<div class="tool-content">
<div class="timer-container">
<div class="timer-tabs">
<button class="timer-tab active" data-tab="timer">倒计时</button>
<button class="timer-tab" data-tab="stopwatch">秒表</button>
</div>
<div class="timer-display" id="timer-display">
00:00:00
</div>
<div id="timer-inputs" class="timer-input-group">
<div>
<input type="number" min="0" max="99" value="0" class="timer-input" id="hours-input">
<div class="timer-input-label">时</div>
</div>
<div>
<input type="number" min="0" max="59" value="0" class="timer-input" id="minutes-input">
<div class="timer-input-label">分</div>
</div>
<div>
<input type="number" min="0" max="59" value="0" class="timer-input" id="seconds-input">
<div class="timer-input-label">秒</div>
</div>
</div>
<div class="timer-controls">
<button class="timer-button" id="start-button">
<i data-lucide="play"></i> 开始
</button>
<button class="timer-button" id="pause-button" style="display: none;">
<i data-lucide="pause"></i> 暂停
</button>
<button class="timer-button" id="reset-button">
<i data-lucide="refresh-cw"></i> 重置
</button>
</div>
</div>
</div>
</section>
</template>
<!-- 工具卡片模板 -->
<template id="tool-card-template">
<div class="tool-card">
<div class="tool-card-icon">
<i data-lucide="image"></i>
</div>
<h3 class="tool-card-title">工具名称</h3>
<p class="tool-card-description">工具描述</p>
<button class="tool-card-button">
使用工具
</button>
</div>
</template>
<div class="app-container">
<!-- 顶部导航栏 -->
<header class="header">
<div class="container header-content">
<div class="logo-container">
<div class="logo-icon">
<i data-lucide="zap"></i>
</div>
<div>
<h1 class="logo-title comic-title">漫画风工具箱</h1>
<p class="logo-subtitle">多功能工具集合</p>
</div>
</div>
<div class="header-actions">
<div class="search-container">
<i data-lucide="search" class="search-icon"></i>
<input type="text" placeholder="搜索工具..." class="search-input comic-input">
</div>
<div class="dropdown">
<button class="icon-button dropdown-trigger">
<i data-lucide="settings"></i>
</button>
<div class="dropdown-content">
<a href="#" class="dropdown-item">
<i data-lucide="info"></i> 关于我们
</a>
<a href="#" class="dropdown-item">
<i data-lucide="settings"></i> 设置
</a>
</div>
</div>
</div>
<button class="menu-button" id="menu-toggle">
<i data-lucide="menu"></i>
</button>
</div>
</header>
<div class="container main-content">
<!-- 左侧边栏 -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-content">
<!-- 主导航 -->
<nav class="main-nav">
<button class="nav-button" data-tool="首页">
<i data-lucide="home"></i> 首页
</button>
<button class="nav-button" data-tool="热门工具">
<i data-lucide="star"></i> 热门工具
</button>
<button class="nav-button" data-tool="最近使用">
<i data-lucide="clock"></i> 最近使用
</button>
</nav>
<!-- 工具分类 -->
<div class="category-nav">
<h3 class="category-title">工具分类</h3>
<div class="category-buttons">
<button class="nav-button active" data-category="image">
<i data-lucide="image"></i> 图片工具
</button>
<button class="nav-button" data-category="text">
<i data-lucide="file-text"></i> 文本工具
</button>
<button class="nav-button" data-category="video">
<i data-lucide="film"></i> 视频工具
</button>
<button class="nav-button" data-category="audio">
<i data-lucide="music"></i> 音频工具
</button>
<button class="nav-button" data-category="dev">
<i data-lucide="code"></i> 开发工具
</button>
<button class="nav-button" data-category="utility">
<i data-lucide="tool"></i> 实用工具
</button>
</div>
</div>
<!-- 页脚 -->
<div class="sidebar-footer">
<p>© 漫画风工具箱 2025</p>
<div class="footer-links">
<a href="#">提交工具</a>
<a href="#">关于我们</a>
</div>
</div>
</div>
</aside>
<!-- 主内容区 -->
<main class="main">
<!-- 面包屑导航 -->
<div class="breadcrumb" id="breadcrumb">
首页 / 图片工具 / 图片格式转换
</div>
<!-- 当前工具 -->
<div id="current-tool" class="tool-container">
<!-- 工具内容将通过JavaScript动态加载 -->
</div>
<!-- 工具列表 -->
<section class="tools-section">
<div class="section-header">
<h2 class="section-title">热门工具</h2>
</div>
<div class="tabs">
<div class="tabs-list" id="category-tabs">
<button class="tab-button active" data-tab="image">图片</button>
<button class="tab-button" data-tab="text">文本</button>
<button class="tab-button" data-tab="video">视频</button>
<button class="tab-button" data-tab="audio">音频</button>
<button class="tab-button" data-tab="dev">开发</button>
<button class="tab-button" data-tab="utility">实用</button>
</div>
<div class="tab-content active" id="tools-grid">
<!-- 工具卡片将通过JavaScript动态加载 -->
</div>
</div>
</section>
<!-- 底部区域 -->
<div class="bottom-sections">
<!-- 最新工具 -->
<section class="info-section">
<div class="section-header">
<h2 class="section-title">最新工具</h2>
</div>
<div class="section-content">
<ul class="tool-list">
<li class="tool-list-item">
<div class="tool-indicator"></div>
<span>二维码生成器</span>
<div class="tool-meta">
<span class="tool-date">2025-05-10</span>
<span class="tool-tag">新功能</span>
</div>
</li>
<li class="tool-list-item">
<div class="tool-indicator"></div>
<span>密码生成器</span>
<div class="tool-meta">
<span class="tool-date">2025-05-08</span>
<span class="tool-tag">新功能</span>
</div>
</li>
<li class="tool-list-item">
<div class="tool-indicator"></div>
<span>颜色选择器</span>
<div class="tool-meta">
<span class="tool-date">2025-05-05</span>
<span class="tool-tag">更新</span>
</div>
</li>
<li class="tool-list-item">
<div class="tool-indicator"></div>
<span>单位转换工具</span>
<div class="tool-meta">
<span class="tool-date">2025-05-01</span>
<span class="tool-tag">新功能</span>
</div>
</li>
</ul>
</div>
</section>
<!-- 统计数据 -->
<section class="info-section">
<div class="section-header">
<h2 class="section-title">统计数据</h2>
</div>
<div class="section-content">
<div class="stats-grid">
<div class="stat-card">
<h3 class="stat-value">100+</h3>
<p class="stat-label">可用工具</p>
</div>
<div class="stat-card">
<h3 class="stat-value">10K+</h3>
<p class="stat-label">每日用户</p>
</div>
</div>
<div class="progress-stats">
<div class="progress-item">
<div class="progress-header">
<span class="progress-label">免费工具</span>
<span class="progress-value">85%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 85%;"></div>
</div>
</div>
<div class="progress-item">
<div class="progress-header">
<span class="progress-label">每日转换</span>
<span class="progress-value">50,000+</span>
</div>
<div class="progress-bar">
<div class="progress-fill progress-fill-green" style="width: 70%;"></div>
</div>
</div>
<div class="progress-item">
<div class="progress-header">
<span class="progress-label">用户满意度</span>
<span class="progress-value">98%</span>
</div>
<div class="progress-bar">
<div class="progress-fill progress-fill-yellow" style="width: 98%;"></div>
</div>
</div>
</div>
</div>
</section>
</div>
</main>
</div>
</div>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
// 初始化Lucide图标
lucide.createIcons();
// 工具数据
const toolCategories = [
{
tab: "image",
name: "图片工具",
icon: "image",
tools: [
{
name: "图片格式转换",
description: "支持多种格式互转,快速简便",
color: "rgb(0, 0, 0)",
component: "image-converter",
},
{ name: "图片压缩", description: "减小图片文件大小,保持质量", color: "rgb(0, 0, 0)", component: "" },
{ name: "图片裁剪", description: "调整图片尺寸和比例", color: "rgb(0, 0, 0)", component: "" },
],
},
{
tab: "text",
name: "文本工具",
icon: "file-text",
tools: [
{
name: "文本翻译",
description: "多语言文本翻译工具",
color: "rgb(0, 0, 0)",
component: "text-translator",
},
{ name: "文本对比", description: "比较两段文本的差异", color: "rgb(0, 0, 0)", component: "" },
{ name: "字数统计", description: "统计文本字数和行数", color: "rgb(0, 0, 0)", component: "" },
],
},
{
tab: "video",
name: "视频工具",
icon: "film",
tools: [
{ name: "视频转GIF", description: "将视频转换为GIF动图", color: "rgb(0, 0, 0)", component: "" },
{ name: "视频压缩", description: "减小视频文件大小", color: "rgb(0, 0, 0)", component: "" },
{ name: "视频剪辑", description: "简单的视频剪辑工具", color: "rgb(0, 0, 0)", component: "" },
],
},
{
tab: "audio",
name: "音频工具",
icon: "music",
tools: [
{ name: "音频格式转换", description: "转换音频文件格式", color: "rgb(0, 0, 0)", component: "" },
{ name: "音频剪辑", description: "剪辑和编辑音频文件", color: "rgb(0, 0, 0)", component: "" },
{ name: "语音转文字", description: "将语音内容转换为文本", color: "rgb(0, 0, 0)", component: "" },
],
},
{
tab: "dev",
name: "开发工具",
icon: "code",
tools: [
{
name: "JSON格式化",
description: "格式化和验证JSON数据",
color: "rgb(0, 0, 0)",
component: "json-formatter",
},
{ name: "代码美化", description: "美化HTML/CSS/JS代码", color: "rgb(0, 0, 0)", component: "" },
{ name: "正则测试", description: "测试和验证正则表达式", color: "rgb(0, 0, 0)", component: "" },
],
},
{
tab: "utility",
name: "实用工具",
icon: "tool",
tools: [
{
name: "二维码生成器",
description: "快速生成自定义二维码",
color: "rgb(0, 0, 0)",
component: "qrcode-generator",
},
{
name: "密码生成器",
description: "生成安全、强壮的随机密码",
color: "rgb(0, 0, 0)",
component: "password-generator",
},
{
name: "颜色选择器",
description: "选择、转换和复制颜色代码",
color: "rgb(0, 0, 0)",
component: "color-picker",
},
{
name: "单位转换工具",
description: "快速转换各种计量单位",
color: "rgb(0, 0, 0)",
component: "unit-converter",
},
{
name: "倒计时/计时器",
description: "简单易用的时间工具",
color: "rgb(0, 0, 0)",
component: "timer",
},
],
},
];
// DOM元素
const menuToggle = document.getElementById("menu-toggle");
const sidebar = document.getElementById("sidebar");
const categoryButtons = document.querySelectorAll(".nav-button[data-category]");
const categoryTabs = document.getElementById("category-tabs");
const toolsGrid = document.getElementById("tools-grid");
const currentTool = document.getElementById("current-tool");
const breadcrumb = document.getElementById("breadcrumb");
// 当前状态
let activeCategory = "image";
let activeTool = "图片格式转换";
// 切换侧边栏
menuToggle.addEventListener("click", () => {
sidebar.classList.toggle("open");
});
// 初始化下拉菜单
document.querySelectorAll(".dropdown-trigger").forEach((trigger) => {
trigger.addEventListener("click", function () {
this.closest(".dropdown").classList.toggle("active");
});
});
// 点击外部关闭下拉菜单
document.addEventListener("click", (event) => {
if (!event.target.closest(".dropdown")) {
document.querySelectorAll(".dropdown.active").forEach((dropdown) => {
dropdown.classList.remove("active");
});
}
});
// 初始化分类按钮
categoryButtons.forEach((button) => {
button.addEventListener("click", function () {
const category = this.dataset.category;
setActiveCategory(category);
});
});
// 初始化分类标签
categoryTabs.querySelectorAll(".tab-button").forEach((tab) => {
tab.addEventListener("click", function () {
const category = this.dataset.tab;
setActiveCategory(category);
});
});
// 设置活跃分类
function setActiveCategory(category) {
activeCategory = category;
// 更新侧边栏按钮
categoryButtons.forEach((button) => {
if (button.dataset.category === category) {
button.classList.add("active");
} else {
button.classList.remove("active");
}
});
// 更新标签
categoryTabs.querySelectorAll(".tab-button").forEach((tab) => {
if (tab.dataset.tab === category) {
tab.classList.add("active");
} else {
tab.classList.remove("active");
}
});
// 更新工具网格
renderToolsGrid(category);
}
// 渲染工具网格
function renderToolsGrid(category) {
const categoryData = toolCategories.find((cat) => cat.tab === category);
if (!categoryData) return;
toolsGrid.innerHTML = "";
categoryData.tools.forEach((tool) => {
const toolCard = document.createElement("div");
toolCard.className = `tool-card ${tool.name === activeTool ? "active" : ""}`;
toolCard.dataset.tool = tool.name;
toolCard.dataset.category = category;
const iconDiv = document.createElement("div");
iconDiv.className = "tool-card-icon";
iconDiv.style.backgroundColor = "#f0f0f0";
const icon = document.createElement("i");
icon.setAttribute("data-lucide", categoryData.icon);
icon.style.color = tool.color;
const title = document.createElement("h3");
title.className = "tool-card-title";
title.textContent = tool.name;
const description = document.createElement("p");
description.className = "tool-card-description";
description.textContent = tool.description;
const button = document.createElement("button");
button.className = "tool-card-button";
button.textContent = "使用工具";
iconDiv.appendChild(icon);
toolCard.appendChild(iconDiv);
toolCard.appendChild(title);
toolCard.appendChild(description);
toolCard.appendChild(button);
toolCard.addEventListener("click", () => {
setActiveTool(tool.name, category);
});
toolsGrid.appendChild(toolCard);
});
// 重新初始化图标
lucide.createIcons();
}
// 设置活跃工具
function setActiveTool(toolName, category) {
activeTool = toolName;
// 更新面包屑
const categoryData = toolCategories.find((cat) => cat.tab === category);
breadcrumb.textContent = `首页 / ${categoryData.name} / ${toolName}`;
// 更新工具卡片
document.querySelectorAll(".tool-card").forEach((card) => {
if (card.dataset.tool === toolName) {
card.classList.add("active");
} else {
card.classList.remove("active");
}
});
// 加载工具组件
loadToolComponent(toolName, category);
}
// 加载工具组件
function loadToolComponent(toolName, category) {
const categoryData = toolCategories.find((cat) => cat.tab === category);
if (!categoryData) return;
const tool = categoryData.tools.find((t) => t.name === toolName);
if (!tool) return;
if (!tool.component) {
currentTool.innerHTML = `
<section class="tool-section">
<div class="tool-header">
<h2 class="tool-title">${toolName}</h2>
<p class="tool-description">${tool.description}</p>
</div>
<div class="tool-content" style="text-align: center; padding: 3rem 1rem;">
<h2 style="font-size: 24px; font-weight: 700; margin-bottom: 16px;">工具开发中</h2>
<p style="color: #666;">该工具正在开发中,敬请期待!</p>
<div class="comic-speech-bubble" style="max-width: 300px; margin: 30px auto;">
<p>我们正在努力开发这个工具,很快就会与您见面!</p>
</div>
</div>
</section>
`;
return;
}
// 获取模板
const template = document.getElementById(`${tool.component}-template`);
if (!template) return;
// 克隆模板内容
const content = template.content.cloneNode(true);
// 清空并添加新内容
currentTool.innerHTML = "";
currentTool.appendChild(content);
// 初始化工具功能
initToolFunctionality(tool.component);
// 重新初始化图标
lucide.createIcons();
}
// 初始化工具功能
function initToolFunctionality(toolComponent) {
switch (toolComponent) {
case "image-converter":
initImageConverter();
break;
case "text-translator":
initTextTranslator();
break;
case "json-formatter":
initJsonFormatter();
break;
case "qrcode-generator":
initQRCodeGenerator();
break;
case "password-generator":
initPasswordGenerator();
break;
case "color-picker":
initColorPicker();
break;
case "unit-converter":
initUnitConverter();
break;
case "timer":
initTimer();
break;
}
}
// 初始化图片转换工具
function initImageConverter() {
const uploadArea = document.getElementById("upload-area");
const conversionArea = document.getElementById("conversion-area");
const fileInput = document.getElementById("file-input");
const imagePreview = document.getElementById("image-preview");
const fileInfo = document.getElementById("file-info");
const formatSelect = document.getElementById("format-select");
const qualitySlider = document.getElementById("quality-slider");
const qualityValue = document.getElementById("quality-value");
const convertButton = document.getElementById("convert-button");
const resetButton = document.getElementById("reset-conversion");
const conversionProgress = document.getElementById("conversion-progress");
const progressBarFill = document.getElementById("progress-bar-fill");
const progressText = document.getElementById("progress-text");
const conversionSuccess = document.getElementById("conversion-success");
const downloadButton = document.getElementById("download-button");
const downloadFormat = document.getElementById("download-format");
const stepUpload = document.getElementById("step-upload");
const stepConvert = document.getElementById("step-convert");
const stepDownload = document.getElementById("step-download");
let selectedFile = null;
let previewUrl = null;
let convertedUrl = null;
// 拖放上传
uploadArea.addEventListener("dragover", function (e) {
e.preventDefault();
this.style.borderColor = "#000";
});
uploadArea.addEventListener("dragleave", function () {
this.style.borderColor = "";
});
uploadArea.addEventListener("drop", function (e) {
e.preventDefault();
this.style.borderColor = "";
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
const file = e.dataTransfer.files[0];
if (!file.type.startsWith("image/")) {
alert("请选择图片文件");
return;
}
handleFileSelect(file);
}
});
// 点击上传
uploadArea.addEventListener("click", () => {
fileInput.click();
});
fileInput.addEventListener("change", function () {
if (this.files && this.files[0]) {
const file = this.files[0];
if (!file.type.startsWith("image/")) {
alert("请选择图片文件");
return;
}
handleFileSelect(file);
}
});
// 处理文件选择
function handleFileSelect(file) {
selectedFile = file;
// 创建预览URL
previewUrl = URL.createObjectURL(file);
// 显示预览
imagePreview.innerHTML = `<img src="${previewUrl}" alt="预览">`;
fileInfo.innerHTML = `
<p>文件名: ${file.name}</p>
<p>大小: ${(file.size / 1024).toFixed(2)} KB</p>
`;
// 切换显示
uploadArea.style.display = "none";
conversionArea.style.display = "block";
// 更新步骤
stepUpload.querySelector(".step-icon").classList.add("completed");
stepUpload.querySelector(".step-line").classList.add("active");
}
// 质量滑块
qualitySlider.addEventListener("input", function () {
qualityValue.textContent = `${this.value}%`;
});
// 格式选择
formatSelect.addEventListener("change", function () {
downloadFormat.textContent = this.value.toUpperCase();
});
// 转换按钮
convertButton.addEventListener("click", function () {
if (!selectedFile) return;
// 添加动画效果
this.classList.add("shake");
setTimeout(() => {
this.classList.remove("shake");
}, 500);
// 隐藏转换按钮,显示进度
this.style.display = "none";
conversionProgress.style.display = "block";
// 更新步骤
stepConvert.querySelector(".step-icon").classList.add("active");
// 模拟转换进度
let progress = 0;
const interval = setInterval(() => {
progress += 5;
progressBarFill.style.width = `${progress}%`;
progressText.textContent = `转换中... ${progress}%`;
if (progress >= 100) {
clearInterval(interval);
// 模拟转换完成
setTimeout(() => {
// 隐藏进度,显示成功
conversionProgress.style.display = "none";
conversionSuccess.style.display = "block";
// 更新步骤
stepConvert.querySelector(".step-icon").classList.remove("active");
stepConvert.querySelector(".step-icon").classList.add("completed");
stepConvert.querySelector(".step-line").classList.add("active");
stepDownload.querySelector(".step-icon").classList.add("completed");
// 设置转换后的URL(实际应用中这里应该是转换后的图片)
convertedUrl = previewUrl;
}, 500);
}
}, 100);
});
// 下载按钮
downloadButton.addEventListener("click", () => {
if (!convertedUrl || !selectedFile) return;
// 添加动画效果
downloadButton.classList.add("bounce");
setTimeout(() => {
downloadButton.classList.remove("bounce");
}, 500);
// 创建下载链接
const link = document.createElement("a");
link.href = convertedUrl;
const fileName = selectedFile.name.split(".")[0];
link.download = `${fileName}.${formatSelect.value}`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
// 重置按钮
resetButton.addEventListener("click", () => {
// 重置状态
selectedFile = null;
previewUrl = null;
convertedUrl = null;
// 重置UI
uploadArea.style.display = "block";
conversionArea.style.display = "none";
convertButton.style.display = "block";
conversionProgress.style.display = "none";
conversionSuccess.style.display = "none";
progressBarFill.style.width = "0%";
// 重置步骤
stepUpload.querySelector(".step-icon").classList.remove("completed");
stepUpload.querySelector(".step-line").classList.remove("active");
stepConvert.querySelector(".step-icon").classList.remove("active", "completed");
stepConvert.querySelector(".step-line").classList.remove("active");
stepDownload.querySelector(".step-icon").classList.remove("completed");
// 重置文件输入
fileInput.value = "";
});
}
// 初始化文本翻译工具
function initTextTranslator() {
const sourceText = document.getElementById("source-text");
const translatedText = document.getElementById("translated-text");
const sourceLanguage = document.getElementById("source-language");
const targetLanguage = document.getElementById("target-language");
const translateButton = document.getElementById("translate-button");
const clearButton = document.getElementById("clear-text");
const swapButton = document.getElementById("swap-languages");
const copyButton = document.getElementById("copy-result");
const sourceCount = document.getElementById("source-count");
const translationProgress = document.getElementById("translation-progress");
const translationProgressBar = document.getElementById("translation-progress-bar");
const translationProgressText = document.getElementById("translation-progress-text");
// 字符计数
sourceText.addEventListener("input", function () {
sourceCount.textContent = this.value.length;
// 启用/禁用翻译按钮
if (this.value.trim()) {
translateButton.disabled = false;
} else {
translateButton.disabled = true;
}
});
// 清空按钮
clearButton.addEventListener("click", () => {
sourceText.value = "";
translatedText.value = "";
sourceCount.textContent = "0";
copyButton.style.display = "none";
translateButton.disabled = true;
});
// 交换语言
swapButton.addEventListener("click", () => {
const tempLang = sourceLanguage.value;
sourceLanguage.value = targetLanguage.value;
targetLanguage.value = tempLang;
const tempText = sourceText.value;
sourceText.value = translatedText.value;
translatedText.value = tempText;
sourceCount.textContent = sourceText.value.length;
});
// 翻译按钮
translateButton.addEventListener("click", function () {
if (!sourceText.value.trim()) return;
// 添加动画效果
this.classList.add("shake");
setTimeout(() => {
this.classList.remove("shake");
}, 500);
// 显示进度
this.style.display = "none";
translationProgress.style.display = "block";
// 模拟翻译进度
let progress = 0;
const interval = setInterval(() => {
progress += 10;
translationProgressBar.style.width = `${progress}%`;
translationProgressText.textContent = `翻译中... ${progress}%`;
if (progress >= 100) {
clearInterval(interval);
// 模拟翻译结果
setTimeout(() => {
// 简单模拟翻译结果
if (targetLanguage.value === "en" && sourceLanguage.value === "zh") {
translatedText.value = sourceText.value
.replace(/你好/g, "Hello")
.replace(/世界/g, "World")
.replace(/翻译/g, "Translation")
.replace(/工具/g, "Tool")
.replace(/文本/g, "Text");
} else if (targetLanguage.value === "zh" && sourceLanguage.value === "en") {
translatedText.value = sourceText.value
.replace(/Hello/g, "你好")
.replace(/World/g, "世界")
.replace(/Translation/g, "翻译")
.replace(/Tool/g, "工具")
.replace(/Text/g, "文本");
} else {
// 对于其他语言对,简单地添加目标语言标记
translatedText.value = `[${targetLanguage.value.toUpperCase()}] ${sourceText.value}`;
}
// 隐藏进度,显示翻译按钮
translationProgress.style.display = "none";
translateButton.style.display = "block";
// 显示复制按钮
copyButton.style.display = "flex";
}, 1000);
}
}, 100);
});
// 复制按钮
copyButton.addEventListener("click", function () {
if (!translatedText.value) return;
navigator.clipboard.writeText(translatedText.value);
// 显示复制成功
const originalText = this.innerHTML;
this.innerHTML = '<i data-lucide="check"></i><span>已复制</span>';
lucide.createIcons();
setTimeout(() => {
this.innerHTML = originalText;
lucide.createIcons();
}, 2000);
});
}
// 初始化JSON格式化工具
function initJsonFormatter() {
const jsonInput = document.getElementById("json-input");
const jsonOutput = document.getElementById("json-output");
const indentSize = document.getElementById("indent-size");
const sortKeys = document.getElementById("sort-keys");
const formatButton = document.getElementById("format-json");
const sampleButton = document.getElementById("sample-data");
const jsonError = document.getElementById("json-error");
const jsonActions = document.getElementById("json-actions");
const copyJson = document.getElementById("copy-json");
const downloadJson = document.getElementById("download-json");
// 格式化按钮
formatButton.addEventListener("click", () => {
if (!jsonInput.value.trim()) return;
// 添加动画效果
formatButton.classList.add("shake");
setTimeout(() => {
formatButton.classList.remove("shake");
}, 500);
try {
// 解析JSON
const parsedJson = JSON.parse(jsonInput.value);
// 根据选项格式化
const formatted = JSON.stringify(
parsedJson,
sortKeys.checked ? Object.keys(parsedJson).sort() : null,
Number(indentSize.value)
);
// 显示格式化结果
jsonOutput.value = formatted;
jsonError.style.display = "none";
jsonActions.style.display = "flex";
} catch (err) {
// 显示错误
jsonError.textContent = `JSON 解析错误: ${err.message}`;
jsonError.style.display = "block";
jsonOutput.value = "";
jsonActions.style.display = "none";
}
});
// 示例数据
sampleButton.addEventListener("click", () => {
const sample = {
name: "漫画风工具箱",
version: "1.0.0",
description: "多功能在线工具集合",
tools: [
{ id: 1, name: "图片转换", category: "图片" },
{ id: 2, name: "文本翻译", category: "文本" },
{ id: 3, name: "JSON格式化", category: "开发" },
],
settings: {
theme: "comic",
language: "zh-CN",
},
};
jsonInput.value = JSON.stringify(sample);
});
// 复制按钮
copyJson.addEventListener("click", function () {
if (!jsonOutput.value) return;
navigator.clipboard.writeText(jsonOutput.value);
// 显示复制成功
const originalText = this.innerHTML;
this.innerHTML = '<i data-lucide="check"></i><span>已复制</span>';
lucide.createIcons();
setTimeout(() => {
this.innerHTML = originalText;
lucide.createIcons();
}, 2000);
});
// 下载按钮
downloadJson.addEventListener("click", () => {
if (!jsonOutput.value) return;
const blob = new Blob([jsonOutput.value], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "formatted.json";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
}
// 初始化二维码生成器
function initQRCodeGenerator() {
const qrcodeInput = document.getElementById("qrcode-input");
const qrcodeResult = document.getElementById("qrcode-result");
const qrcodeLogo = document.getElementById("qrcode-logo");
const qrcodeSize = document.getElementById("qrcode-size");
const downloadQrcode = document.getElementById("download-qrcode");
// 模拟生成二维码
function generateQRCode() {
const text = qrcodeInput.value.trim();
if (!text) {
qrcodeResult.innerHTML = "<p>请输入内容</p>";
downloadQrcode.disabled = true;
return;
}
// 显示加载状态
qrcodeResult.innerHTML = "<p>生成中...</p>";
// 模拟生成延迟
setTimeout(() => {
// 在实际应用中,这里应该使用真正的二维码生成库
// 这里我们只是模拟一个二维码图像
const size = qrcodeSize.value;
const useLogo = qrcodeLogo.checked;
// 创建一个简单的二维码模拟图
qrcodeResult.innerHTML = "";
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
// 绘制白色背景
ctx.fillStyle = "white";
ctx.fillRect(0, 0, size, size);
//绘制黑色方块模拟二维码
ctx.fillStyle = "black";
const blockSize = Math.floor(size / 25);
// 随机生成二维码图案
for (let i = 0; i < 25; i++) {
for (let j = 0; j < 25; j++) {
if (
// 保留三个角落的定位图案
!((i < 7 && j < 7) || (i < 7 && j > 17) || (i > 17 && j < 7)) &&
Math.random() > 0.6
) {
ctx.fillRect(i * blockSize, j * blockSize, blockSize, blockSize);
}
}
}
// 绘制三个角落的定位图案
ctx.fillRect(0, 0, 7 * blockSize, 7 * blockSize);
ctx.fillRect(0, 18 * blockSize, 7 * blockSize, 7 * blockSize);
ctx.fillRect(18 * blockSize, 0, 7 * blockSize, 7 * blockSize);
ctx.fillStyle = "white";
ctx.fillRect(blockSize, blockSize, 5 * blockSize, 5 * blockSize);
ctx.fillRect(blockSize, 19 * blockSize, 5 * blockSize, 5 * blockSize);
ctx.fillRect(19 * blockSize, blockSize, 5 * blockSize, 5 * blockSize);
ctx.fillStyle = "black";
ctx.fillRect(2 * blockSize, 2 * blockSize, 3 * blockSize, 3 * blockSize);
ctx.fillRect(2 * blockSize, 20 * blockSize, 3 * blockSize, 3 * blockSize);
ctx.fillRect(20 * blockSize, 2 * blockSize, 3 * blockSize, 3 * blockSize);
// 如果选择添加Logo
if (useLogo) {
const logoSize = size / 5;
const logoX = (size - logoSize) / 2;
const logoY = (size - logoSize) / 2;
ctx.fillStyle = "white";
ctx.fillRect(logoX, logoY, logoSize, logoSize);
ctx.font = `bold ${logoSize/2}px Arial`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "black";
ctx.fillText("LOGO", size/2, size/2);
}
qrcodeResult.appendChild(canvas);
// 启用下载按钮
downloadQrcode.disabled = false;
}, 1000);
}
// 输入变化时生成二维码
qrcodeInput.addEventListener("input", generateQRCode);
qrcodeLogo.addEventListener("change", generateQRCode);
qrcodeSize.addEventListener("change", generateQRCode);
// 下载二维码
downloadQrcode.addEventListener("click", () => {
const canvas = qrcodeResult.querySelector("canvas");
if (!canvas) return;
// 添加动画效果
downloadQrcode.classList.add("bounce");
setTimeout(() => {
downloadQrcode.classList.remove("bounce");
}, 500);
const link = document.createElement("a");
link.download = "qrcode.png";
link.href = canvas.toDataURL("image/png");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
}
// 初始化密码生成器
function initPasswordGenerator() {
const passwordDisplay = document.getElementById("password-display");
const lengthSlider = document.getElementById("length-slider");
const lengthValue = document.getElementById("length-value");
const includeUppercase = document.getElementById("include-uppercase");
const includeLowercase = document.getElementById("include-lowercase");
const includeNumbers = document.getElementById("include-numbers");
const includeSymbols = document.getElementById("include-symbols");
const generateButton = document.getElementById("generate-password");
const copyPassword = document.getElementById("copy-password");
const refreshPassword = document.getElementById("refresh-password");
// 更新密码长度显示
lengthSlider.addEventListener("input", () => {
lengthValue.textContent = lengthSlider.value;
});
// 生成密码
function generatePassword() {
const length = parseInt(lengthSlider.value);
const hasUpper = includeUppercase.checked;
const hasLower = includeLowercase.checked;
const hasNumbers = includeNumbers.checked;
const hasSymbols = includeSymbols.checked;
// 确保至少选择了一种字符类型
if (!hasUpper && !hasLower && !hasNumbers && !hasSymbols) {
passwordDisplay.textContent = "请至少选择一种字符类型";
return;
}
// 定义字符集
const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const lowercase = "abcdefghijklmnopqrstuvwxyz";
const numbers = "0123456789";
const symbols = "!@#$%^&*()_+-=[]{}|;:,.<>?";
let chars = "";
if (hasUpper) chars += uppercase;
if (hasLower) chars += lowercase;
if (hasNumbers) chars += numbers;
if (hasSymbols) chars += symbols;
// 生成密码
let password = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * chars.length);
password += chars[randomIndex];
}
// 显示密码
passwordDisplay.textContent = password;
// 添加动画效果
passwordDisplay.classList.add("shake");
setTimeout(() => {
passwordDisplay.classList.remove("shake");
}, 500);
}
// 生成按钮点击事件
generateButton.addEventListener("click", generatePassword);
// 刷新按钮点击事件
refreshPassword.addEventListener("click", generatePassword);
// 复制按钮点击事件
copyPassword.addEventListener("click", () => {
const password = passwordDisplay.textContent;
if (password === "点击生成按钮创建密码" || password === "请至少选择一种字符类型") return;
navigator.clipboard.writeText(password);
// 显示复制成功
const originalIcon = copyPassword.innerHTML;
copyPassword.innerHTML = '<i data-lucide="check"></i>';
lucide.createIcons();
setTimeout(() => {
copyPassword.innerHTML = originalIcon;
lucide.createIcons();
}, 2000);
});
}
// 初始化颜色选择器
function initColorPicker() {
const colorDisplay = document.getElementById("color-display");
const hexInput = document.getElementById("hex-input");
const rgbInput = document.getElementById("rgb-input");
const hslInput = document.getElementById("hsl-input");
const colorPickerInput = document.getElementById("color-picker-input");
const cssVarValue = document.getElementById("css-var-value");
const tailwindValue = document.getElementById("tailwind-value");
// 辅助函数:将RGB转换为HEX
function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
// 辅助函数:将HEX转换为RGB
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null;
}
// 辅助函数:将RGB转换为HSL
function rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // 灰色
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100),
};
}
// 更新所有颜色值
function updateAllColors(hex) {
// 更新颜色显示
colorDisplay.style.backgroundColor = hex;
colorDisplay.textContent = hex;
// 更新HEX输入
hexInput.value = hex;
// 更新RGB输入
const rgb = hexToRgb(hex);
if (rgb) {
rgbInput.value = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
// 更新HSL输入
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
hslInput.value = `${hsl.h}, ${hsl.s}%, ${hsl.l}%`;
// 更新CSS变量
cssVarValue.textContent = `--color: ${hex};`;
// 更新Tailwind值(简化版,实际应用中需要更复杂的映射)
let tailwind = "custom";
if (hex === "#3498db") tailwind = "bg-blue-500";
else if (hex === "#e74c3c") tailwind = "bg-red-500";
else if (hex === "#2ecc71") tailwind = "bg-green-500";
tailwindValue.textContent = tailwind;
}
// 更新颜色选择器
colorPickerInput.value = hex;
}
// 颜色选择器变化事件
colorPickerInput.addEventListener("input", () => {
updateAllColors(colorPickerInput.value);
});
// HEX输入变化事件
hexInput.addEventListener("input", () => {
let hex = hexInput.value;
if (hex.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i)) {
if (!hex.startsWith("#")) hex = "#" + hex;
updateAllColors(hex);
}
});
// RGB输入变化事件
rgbInput.addEventListener("input", () => {
const rgbMatch = rgbInput.value.match(/(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
if (rgbMatch) {
const r = parseInt(rgbMatch[1]);
const g = parseInt(rgbMatch[2]);
const b = parseInt(rgbMatch[3]);
if (r <= 255 && g <= 255 && b <= 255) {
const hex = rgbToHex(r, g, b);
updateAllColors(hex);
}
}
});
// 初始化颜色
updateAllColors("#3498db");
}
// 初始化单位转换工具
function initUnitConverter() {
const unitTypes = document.getElementById("unit-types");
const fromValue = document.getElementById("from-value");
const toValue = document.getElementById("to-value");
const fromUnit = document.getElementById("from-unit");
const toUnit = document.getElementById("to-unit");
const swapUnits = document.getElementById("swap-units");
const conversionFormula = document.getElementById("conversion-formula");
// 单位转换数据
const unitData = {
length: {
m: { name: "米", factor: 1 },
km: { name: "千米", factor: 1000 },
cm: { name: "厘米", factor: 0.01 },
mm: { name: "毫米", factor: 0.001 },
in: { name: "英寸", factor: 0.0254 },
ft: { name: "英尺", factor: 0.3048 },
yd: { name: "码", factor: 0.9144 },
mi: { name: "英里", factor: 1609.344 },
},
weight: {
kg: { name: "千克", factor: 1 },
g: { name: "克", factor: 0.001 },
mg: { name: "毫克", factor: 0.000001 },
lb: { name: "磅", factor: 0.45359237 },
oz: { name: "盎司", factor: 0.028349523125 },
t: { name: "吨", factor: 1000 },
},
temperature: {
c: { name: "摄氏度", factor: 1 },
f: { name: "华氏度", factor: 1 },
k: { name: "开尔文", factor: 1 },
},
area: {
m2: { name: "平方米", factor: 1 },
km2: { name: "平方千米", factor: 1000000 },
cm2: { name: "平方厘米", factor: 0.0001 },
mm2: { name: "平方毫米", factor: 0.000001 },
ha: { name: "公顷", factor: 10000 },
acre: { name: "英亩", factor: 4046.8564224 },
ft2: { name: "平方英尺", factor: 0.09290304 },
},
volume: {
m3: { name: "立方米", factor: 1 },
l: { name: "升", factor: 0.001 },
ml: { name: "毫升", factor: 0.000001 },
gal: { name: "加仑(美)", factor: 0.00378541 },
qt: { name: "夸脱(美)", factor: 0.000946353 },
pt: { name: "品脱(美)", factor: 0.000473176 },
},
time: {
s: { name: "秒", factor: 1 },
min: { name: "分钟", factor: 60 },
h: { name: "小时", factor: 3600 },
day: { name: "天", factor: 86400 },
week: { name: "周", factor: 604800 },
month: { name: "月(30天)", factor: 2592000 },
year: { name: "年(365天)", factor: 31536000 },
},
};
// 当前单位类型
let currentType = "length";
// 更新单位选择器
function updateUnitSelectors(type) {
fromUnit.innerHTML = "";
toUnit.innerHTML = "";
const units = unitData[type];
for (const unit in units) {
const fromOption = document.createElement("option");
fromOption.value = unit;
fromOption.textContent = units[unit].name;
fromUnit.appendChild(fromOption);
const toOption = document.createElement("option");
toOption.value = unit;
toOption.textContent = units[unit].name;
toUnit.appendChild(toOption);
}
// 设置默认值
if (type === "length") {
fromUnit.value = "m";
toUnit.value = "cm";
} else {
fromUnit.selectedIndex = 0;
toUnit.selectedIndex = 1;
}
// 执行转换
convert();
}
// 转换单位
function convert() {
const from = fromUnit.value;
const to = toUnit.value;
const value = parseFloat(fromValue.value);
if (isNaN(value)) {
toValue.value = "";
conversionFormula.textContent = "请输入有效数字";
return;
}
// 特殊处理温度转换
if (currentType === "temperature") {
let result;
if (from === "c" && to === "f") {
result = (value * 9/5) + 32;
conversionFormula.textContent = `${value}°C = (${value} × 9/5) + 32 = ${result.toFixed(2)}°F`;
} else if (from === "f" && to === "c") {
result = (value - 32) * 5/9;
conversionFormula.textContent = `${value}°F = (${value} - 32) × 5/9 = ${result.toFixed(2)}°C`;
} else if (from === "c" && to === "k") {
result = value + 273.15;
conversionFormula.textContent = `${value}°C = ${value} + 273.15 = ${result.toFixed(2)}K`;
} else if (from === "k" && to === "c") {
result = value - 273.15;
conversionFormula.textContent = `${value}K = ${value} - 273.15 = ${result.toFixed(2)}°C`;
} else if (from === "f" && to === "k") {
result = (value - 32) * 5/9 + 273.15;
conversionFormula.textContent = `${value}°F = (${value} - 32) × 5/9 + 273.15 = ${result.toFixed(2)}K`;
} else if (from === "k" && to === "f") {
result = (value - 273.15) * 9/5 + 32;
conversionFormula.textContent = `${value}K = (${value} - 273.15) × 9/5 + 32 = ${result.toFixed(2)}°F`;
} else {
result = value;
conversionFormula.textContent = `${value}${from === "c" ? "°C" : from === "f" ? "°F" : "K"} = ${value}${to === "c" ? "°C" : to === "f" ? "°F" : "K"}`;
}
toValue.value = result.toFixed(2);
} else {
// 常规单位转换
const fromFactor = unitData[currentType][from].factor;
const toFactor = unitData[currentType][to].factor;
const result = (value * fromFactor) / toFactor;
toValue.value = result.toFixed(4);
// 更新转换公式
const fromName = unitData[currentType][from].name;
const toName = unitData[currentType][to].name;
const ratio = (fromFactor / toFactor).toFixed(6);
conversionFormula.textContent = `1${fromName} = ${ratio}${toName}`;
}
}
// 切换单位类型
unitTypes.addEventListener("click", (e) => {
if (e.target.classList.contains("unit-type-button")) {
// 更新活跃按钮
unitTypes.querySelectorAll(".unit-type-button").forEach(btn => {
btn.classList.remove("active");
});
e.target.classList.add("active");
// 更新单位类型
currentType = e.target.dataset.type;
updateUnitSelectors(currentType);
}
});
// 交换单位
swapUnits.addEventListener("click", () => {
const tempUnit = fromUnit.value;
fromUnit.value = toUnit.value;
toUnit.value = tempUnit;
const tempValue = fromValue.value;
fromValue.value = toValue.value;
toValue.value = tempValue;
convert();
});
// 输入和单位变化时转换
fromValue.addEventListener("input", convert);
fromUnit.addEventListener("change", convert);
toUnit.addEventListener("change", convert);
// 初始化
updateUnitSelectors("length");
}
// 初始化倒计时/计时器
function initTimer() {
const timerTabs = document.querySelectorAll(".timer-tab");
const timerDisplay = document.getElementById("timer-display");
const timerInputs = document.getElementById("timer-inputs");
const hoursInput = document.getElementById("hours-input");
const minutesInput = document.getElementById("minutes-input");
const secondsInput = document.getElementById("seconds-input");
const startButton = document.getElementById("start-button");
const pauseButton = document.getElementById("pause-button");
const resetButton = document.getElementById("reset-button");
let mode = "timer"; // timer or stopwatch
let interval;
let running = false;
let totalSeconds = 0;
let startTime;
let elapsedPauseTime = 0;
let pauseStartTime;
// 切换模式
timerTabs.forEach(tab => {
tab.addEventListener("click", () => {
// 停止当前计时
clearInterval(interval);
running = false;
// 更新UI
timerTabs.forEach(t => t.classList.remove("active"));
tab.classList.add("active");
startButton.style.display = "block";
pauseButton.style.display = "none";
// 设置模式
mode = tab.dataset.tab;
// 重置显示
if (mode === "timer") {
timerInputs.style.display = "flex";
timerDisplay.textContent = "00:00:00";
hoursInput.value = 0;
minutesInput.value = 0;
secondsInput.value = 0;
} else {
timerInputs.style.display = "none";
timerDisplay.textContent = "00:00:00";
totalSeconds = 0;
}
});
});
// 格式化时间
function formatTime(seconds) {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
}
// 开始计时
startButton.addEventListener("click", () => {
if (running) return;
if (mode === "timer") {
// 倒计时模式
const hours = parseInt(hoursInput.value) || 0;
const minutes = parseInt(minutesInput.value) || 0;
const seconds = parseInt(secondsInput.value) || 0;
totalSeconds = hours * 3600 + minutes * 60 + seconds;
if (totalSeconds <= 0) {
timerDisplay.textContent = "请设置时间";
return;
}
timerDisplay.textContent = formatTime(totalSeconds);
timerInputs.style.display = "none";
interval = setInterval(() => {
totalSeconds--;
timerDisplay.textContent = formatTime(totalSeconds);
if (totalSeconds <= 0) {
clearInterval(interval);
running = false;
timerDisplay.textContent = "时间到!";
startButton.style.display = "block";
pauseButton.style.display = "none";
// 添加动画效果
timerDisplay.classList.add("shake");
setTimeout(() => {
timerDisplay.classList.remove("shake");
}, 500);
}
}, 1000);
} else {
// 秒表模式
startTime = Date.now() - elapsedPauseTime;
interval = setInterval(() => {
const elapsedTime = Math.floor((Date.now() - startTime) / 1000);
timerDisplay.textContent = formatTime(elapsedTime);
}, 100);
}
running = true;
startButton.style.display = "none";
pauseButton.style.display = "block";
});
// 暂停计时
pauseButton.addEventListener("click", () => {
if (!running) return;
clearInterval(interval);
running = false;
if (mode === "stopwatch") {
pauseStartTime = Date.now();
}
startButton.style.display = "block";
pauseButton.style.display = "none";
});
// 重置计时
resetButton.addEventListener("click", () => {
clearInterval(interval);
running = false;
if (mode === "timer") {
timerDisplay.textContent = "00:00:00";
timerInputs.style.display = "flex";
} else {
timerDisplay.textContent = "00:00:00";
elapsedPauseTime = 0;
}
startButton.style.display = "block";
pauseButton.style.display = "none";
});
// 限制输入范围
hoursInput.addEventListener("change", () => {
hoursInput.value = Math.max(0, Math.min(99, parseInt(hoursInput.value) || 0));
});
minutesInput.addEventListener("change", () => {
minutesInput.value = Math.max(0, Math.min(59, parseInt(minutesInput.value) || 0));
});
secondsInput.addEventListener("change", () => {
secondsInput.value = Math.max(0, Math.min(59, parseInt(secondsInput.value) || 0));
});
}
// 初始化页面
setActiveCategory("image");
setActiveTool("图片格式转换", "image");
});
</script>
</body>
</html>
index.html
style.css
index.js