AI图片生成器前端实现详解
本文详细解析一个功能完整的AI图片生成器前端实现,包含主题切换、参数配置、图片生成与预览等核心功能。
项目概述
这是一个基于ModelScope平台的AI图片生成器前端实现,用户可以通过输入提示词、选择模型和调整参数来生成高质量图片。该应用具有响应式设计、暗黑/明亮模式切换、图片预览和下载等完整功能。
下面是完整的实现代码:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI 图片生成器</title>
<style>
:root {
--bg-color: #f5f7fa;
--text-color: #333;
--panel-bg: #ffffff;
--border-color: #e1e4e8;
--primary-color: #4a6bdf;
--secondary-color: #f0f2f5;
--button-hover: #3a5bd9;
--input-bg: #ffffff;
--progress-bg: #e0e0e0;
--image-bg: #f0f0f0;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
.dark-mode {
--bg-color: #1a1a1a;
--text-color: #e0e0e0;
--panel-bg: #2d2d2d;
--border-color: #444;
--primary-color: #5d7eff;
--secondary-color: #3a3a3a;
--button-hover: #4a6bdf;
--input-bg: #3a3a3a;
--progress-bg: #444;
--image-bg: #2a2a2a;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: var(--transition);
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border-color);
}
h1 {
font-size: 28px;
color: var(--primary-color);
font-weight: 700;
letter-spacing: 0.5px;
}
.theme-toggle {
background: var(--secondary-color);
border: none;
padding: 8px 15px;
border-radius: 30px;
cursor: pointer;
color: var(--text-color);
display: flex;
align-items: center;
gap: 8px;
transition: var(--transition);
font-weight: 500;
box-shadow: var(--box-shadow);
}
.theme-toggle:hover {
background: var(--border-color);
transform: translateY(-2px);
}
.main-content {
display: flex;
gap: 25px;
flex: 1;
}
.panel {
background: var(--panel-bg);
border-radius: 12px;
padding: 25px;
box-shadow: var(--box-shadow);
border: 1px solid var(--border-color);
transition: var(--transition);
}
.params-panel {
flex: 0 0 400px;
display: flex;
flex-direction: column;
}
.preview-panel {
flex: 1;
display: flex;
flex-direction: column;
}
.panel-title {
font-size: 20px;
margin-bottom: 20px;
color: var(--primary-color);
font-weight: 600;
position: relative;
padding-bottom: 10px;
}
.panel-title::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 50px;
height: 3px;
background: var(--primary-color);
border-radius: 3px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-color);
}
input, select, textarea {
width: 100%;
padding: 12px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--input-bg);
color: var(--text-color);
font-size: 14px;
transition: var(--transition);
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(74, 107, 223, 0.2);
}
textarea {
min-height: 120px;
resize: vertical;
}
.advanced-params {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
}
.param-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.param-item {
flex: 1;
}
button {
background: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
transition: var(--transition);
box-shadow: var(--box-shadow);
}
button:hover {
background: var(--button-hover);
transform: translateY(-2px);
}
button:active {
transform: translateY(0);
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
transform: none;
}
.status-area {
margin-top: 20px;
padding: 15px;
background: var(--secondary-color);
border-radius: 8px;
min-height: 20px;
font-size: 14px;
border-left: 4px solid var(--primary-color);
}
.progress-container {
margin-top: 20px;
}
progress {
width: 100%;
height: 10px;
border-radius: 5px;
background: var(--progress-bg);
border: none;
}
progress::-webkit-progress-bar {
background: var(--progress-bg);
border-radius: 5px;
}
progress::-webkit-progress-value {
background: var(--primary-color);
border-radius: 5px;
transition: width 0.3s ease;
}
progress::-moz-progress-bar {
background: var(--primary-color);
border-radius: 5px;
}
.preview-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
background: var(--image-bg);
border-radius: 12px;
overflow: hidden;
position: relative;
transition: var(--transition);
cursor: pointer;
}
.preview-container:hover {
box-shadow: 0 0 0 2px var(--primary-color);
}
.preview-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
display: none;
transition: var(--transition);
}
.preview-placeholder {
text-align: center;
padding: 30px;
color: var(--text-color);
opacity: 0.7;
font-size: 16px;
}
.time-info {
margin-top: 15px;
font-size: 14px;
color: var(--primary-color);
font-weight: 500;
text-align: center;
}
.action-buttons {
display: flex;
gap: 15px;
margin-top: 20px;
}
.secondary-button {
background: var(--secondary-color);
color: var(--text-color);
}
.secondary-button:hover {
background: var(--border-color);
}
/* 图片放大模态框样式 */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
overflow: auto;
opacity: 0;
transition: opacity 0.3s ease;
}
.modal.show {
display: flex;
justify-content: center;
align-items: center;
opacity: 1;
}
.modal-content {
max-width: 90%;
max-height: 90%;
object-fit: contain;
animation: zoomIn 0.3s ease;
}
.close-modal {
position: absolute;
top: 20px;
right: 30px;
color: white;
font-size: 35px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s ease;
}
.close-modal:hover {
transform: rotate(90deg);
}
@keyframes zoomIn {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* 加载动画 */
.loader {
display: none;
width: 48px;
height: 48px;
margin: 20px auto;
border: 5px solid rgba(74, 107, 223, 0.2);
border-radius: 50%;
border-top-color: var(--primary-color);
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 768px) {
.main-content {
flex-direction: column;
}
.params-panel {
flex: 1;
}
header {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.action-buttons {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>AI 图片生成器</h1>
<button class="theme-toggle" id="themeToggle">
<span id="themeIcon">🌙</span> <span id="themeText">暗夜模式</span>
</button>
</header>
<div class="main-content">
<div class="panel params-panel">
<h2 class="panel-title">生成参数</h2>
<div class="action-buttons">
<button id="generateBtn">生成图片</button>
<button id="downloadBtn" class="secondary-button" disabled>下载图片</button>
</div>
<div class="status-area" id="statusArea">就绪</div>
<div class="time-info" id="timeInfo"></div>
<div class="progress-container">
<progress id="progressBar" value="0" max="100" style="display: none;"></progress>
<div class="loader" id="loader"></div>
</div>
<div class="form-group">
<label for="model">模型选择</label>
<select id="model">
<option value="Qwen/Qwen-Image">Qwen/Qwen-Image</option>
<option value="Liudef/XB_F.1_MIX">Liudef/XB_F.1_MIX</option>
<option value="Liudef/XB_PONY">Liudef/XB_PONY</option>
<option value="Liudef/XB_Illustrious">Liudef/XB_Illustrious</option>
<option value="Liudef/XB_QWEN_ANIYA_FACE6_C">Liudef/XB_QWEN_ANIYA_FACE6_C</option>
<option value="Liudef/XB_F.1_ANIYA_FACE6_B">Liudef/XB_F.1_ANIYA_FACE6_B</option>
<option value="Liudef/XB_F.1_FACE_3">Liudef/XB_F.1_FACE_3</option>
<option value="Liudef/XB_F.1_MD">Liudef/XB_F.1_MD</option>
</select>
</div>
<div class="form-group">
<label for="prompt">正向提示词</label>
<textarea id="prompt" placeholder="请输入详细的描述词以获得更好的效果...">一只棕色的猫</textarea>
</div>
<div class="form-group">
<label for="negativePrompt">负向提示词 (可选)</label>
<textarea id="negativePrompt" placeholder="请输入不希望出现在图片中的内容...">lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry</textarea>
</div>
<div class="advanced-params">
<h3 class="panel-title">高级参数</h3>
<div class="param-row">
<div class="param-item">
<label for="width">宽度</label>
<input type="number" id="width" min="64" max="2048" step="64" value="1024">
</div>
<div class="param-item">
<label for="height">高度</label>
<input type="number" id="height" min="64" max="2048" step="64" value="1024">
</div>
</div>
<div class="param-row">
<div class="param-item">
<label for="steps">采样步数</label>
<input type="number" id="steps" min="1" max="100" value="30">
</div>
<div class="param-item">
<label for="guidance">引导系数</label>
<input type="number" id="guidance" min="1.5" max="20" step="0.1" value="3.5">
</div>
</div>
</div>