摩搭api 实现

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>