演示站(这个UI超好看):https://websim.ai/c/xQmkwDSfnOrHj2aiD
如图:
已经不知道在论坛看过多少个薅flux的代码了
这里再重写一个
也是不同的UI
代码逻辑是一模一样的
效果:
代码来源:https://linux.do/t/topic/182826?u=stellafortuna
老毛病了,我又把代码没备份就删掉了
简单重写了一下代码,UI变得简陋了一些,我哭 没有什么事是比UI难看更难受的
后端不支持生成更多图片 每次只能生成一张
代码
<!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>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;600&display=swap" rel="stylesheet">
<style>
:root {
--bg-gradient-light: linear-gradient(135deg, #f6d365 0%, #fda085 100%);
--bg-gradient-dark: linear-gradient(135deg, #30cfd0 0%, #330867 100%);
--text-color-light: #333;
--text-color-dark: #e0e0e0;
--button-bg-light: #ff6b6b;
--button-bg-dark: #4e54c8;
--container-bg-light: rgba(255, 255, 255, 0.7);
--container-bg-dark: rgba(16, 18, 27, 0.7);
--input-border-light: #ff9a9e;
--input-border-dark: #6a11cb;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Noto Sans SC', sans-serif;
background: var(--bg-gradient-light);
color: var(--text-color-light);
transition: all 0.5s ease;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
overflow-x: hidden;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(white, rgba(255,255,255,.2) 2px, transparent 40px),
radial-gradient(white, rgba(255,255,255,.15) 1px, transparent 30px),
radial-gradient(white, rgba(255,255,255,.1) 2px, transparent 40px);
background-size: 550px 550px, 350px 350px, 250px 250px;
background-position: 0 0, 40px 60px, 130px 270px;
animation: starMove 200s linear infinite;
z-index: -1;
}
@keyframes starMove {
from { transform: translateY(0); }
to { transform: translateY(-2000px); }
}
body.dark-theme {
background: var(--bg-gradient-dark);
color: var(--text-color-dark);
}
.container {
background: var(--container-bg-light);
padding: 2rem;
border-radius: 20px;
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
backdrop-filter: blur(4px);
border: 1px solid rgba(255, 255, 255, 0.18);
max-width: 600px;
width: 100%;
transition: all 0.5s ease;
position: relative;
overflow: hidden;
}
body.dark-theme .container {
background: var(--container-bg-dark);
}
h1 {
text-align: center;
margin-bottom: 1rem;
font-weight: 600;
color: #ff6b6b;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
body.dark-theme h1 {
color: #4e54c8;
}
textarea, input, button, select {
width: 100%;
padding: 0.75rem;
margin-bottom: 1rem;
box-sizing: border-box;
border-radius: 10px;
border: 2px solid var(--input-border-light);
background: rgba(255, 255, 255, 0.8);
transition: all 0.3s ease;
font-family: 'Noto Sans SC', sans-serif;
}
body.dark-theme textarea,
body.dark-theme input,
body.dark-theme select {
border-color: var(--input-border-dark);
background: rgba(255, 255, 255, 0.1);
color: var(--text-color-dark);
}
button {
background-color: var(--button-bg-light);
color: white;
border: none;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
}
body.dark-theme button {
background-color: var(--button-bg-dark);
}
button:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
#result {
margin-top: 1rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
#result img {
width: 100%;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
transition: transform 0.3s ease;
}
#result img:hover {
transform: scale(1.05);
}
.theme-toggle {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 1000;
}
.theme-toggle button {
background: none;
border: none;
font-size: 2rem;
cursor: pointer;
transition: transform 0.3s ease;
}
.theme-toggle button:hover {
transform: rotate(360deg);
}
.loading {
display: none;
text-align: center;
margin-top: 1rem;
}
.loading::after {
content: '';
display: inline-block;
width: 30px;
height: 30px;
border: 3px solid #ff6b6b;
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error {
color: #ff6b6b;
text-align: center;
margin-top: 1rem;
}
#history {
margin-top: 2rem;
border-top: 1px solid var(--input-border-light);
padding-top: 1rem;
}
#history h2 {
margin-bottom: 1rem;
}
#historyList {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
#historyList img {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 5px;
cursor: pointer;
transition: transform 0.3s ease;
}
#historyList img:hover {
transform: scale(1.1);
}
@media (max-width: 600px) {
.container {
padding: 1rem;
}
h1 {
font-size: 1.5rem;
}
textarea, input, button, select {
font-size: 0.9rem;
}
#result {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="theme-toggle">
<button id="themeToggle">🌓</button>
</div>
<div class="container">
<h1>天琴AI绘画师</h1>
<textarea id="prompt" rows="4" placeholder="请描述你想要生成的图像..."></textarea>
<select id="aspectRatio">
<option value="1:1">1:1 (正方形)</option>
<option value="4:3">4:3 (标准)</option>
<option value="16:9">16:9 (宽屏)</option>
</select>
<select id="outputFormat">
<option value="webp">WebP</option>
<option value="png">PNG</option>
<option value="jpg">JPG</option>
</select>
<input type="number" id="numOutputs" min="1" max="4" value="1" placeholder="生成数量 (1-4)">
<input type="number" id="outputQuality" min="1" max="100" value="90" placeholder="输出质量 (1-100)">
<button id="generate">生成图像</button>
<div class="loading" id="loading"></div>
<div id="result"></div>
<div class="error" id="error"></div>
<div id="history">
<h2>历史记录</h2>
<div id="historyList"></div>
</div>
</div>
<script>
const themeToggle = document.getElementById('themeToggle');
const body = document.body;
const generateBtn = document.getElementById('generate');
const promptInput = document.getElementById('prompt');
const aspectRatioSelect = document.getElementById('aspectRatio');
const outputFormatSelect = document.getElementById('outputFormat');
const numOutputsInput = document.getElementById('numOutputs');
const outputQualityInput = document.getElementById('outputQuality');
const resultDiv = document.getElementById('result');
const loadingDiv = document.getElementById('loading');
const errorDiv = document.getElementById('error');
const historyList = document.getElementById('historyList');
let history = JSON.parse(localStorage.getItem('imageHistory')) || [];
themeToggle.addEventListener('click', () => {
body.classList.toggle('dark-theme');
themeToggle.textContent = body.classList.contains('dark-theme') ? '🌞' : '🌓';
});
generateBtn.addEventListener('click', async () => {
const prompt = promptInput.value;
const aspectRatio = aspectRatioSelect.value;
const outputFormat = outputFormatSelect.value;
const numOutputs = numOutputsInput.value;
const outputQuality = outputQualityInput.value;
if (!prompt) {
showError('请输入图像描述');
return;
}
showLoading();
try {
const token = await getToken();
const imageUrls = await generateImage(token, prompt, aspectRatio, outputFormat, numOutputs, outputQuality);
showResult(imageUrls);
addToHistory(imageUrls);
} catch (error) {
showError('生成图像时出错: ' + error.message);
} finally {
hideLoading();
}
});
async function getToken() {
try {
const response = await fetch('https://fluxaiweb.com/flux/getToken');
if (!response.ok) {
throw new Error('获取令牌失败');
}
const data = await response.json();
return data.data.token;
} catch (error) {
throw new Error('获取令牌时出错: ' + error.message);
}
}
async function generateImage(token, prompt, aspectRatio, outputFormat, numOutputs, outputQuality) {
try {
const response = await fetch('https://fluxaiweb.com/flux/generateImage', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'token': token
},
body: JSON.stringify({
prompt,
aspectRatio,
outputFormat,
numOutputs: parseInt(numOutputs),
outputQuality: parseInt(outputQuality)
})
});
if (!response.ok) {
throw new Error('生成图像失败');
}
const data = await response.json();
return Array.isArray(data.data.images) ? data.data.images : [data.data.image];
} catch (error) {
throw new Error('生成图像时出错: ' + error.message);
}
}
function showResult(imageUrls) {
resultDiv.innerHTML = imageUrls.map(url => `
<div>
<img src="${url}" alt="生成的图像" onclick="openImageInNewTab('${url}')">
<button onclick="downloadImage('${url}')">下载</button>
</div>
`).join('');
}
function showLoading() {
loadingDiv.style.display = 'block';
errorDiv.textContent = '';
}
function hideLoading() {
loadingDiv.style.display = 'none';
}
function showError(message) {
errorDiv.textContent = message;
}
function addToHistory(imageUrls) {
history = [...imageUrls, ...history].slice(0, 20);
localStorage.setItem('imageHistory', JSON.stringify(history));
updateHistoryDisplay();
}
function updateHistoryDisplay() {
historyList.innerHTML = history.map(url => `
<img src="${url}" alt="历史图像" onclick="openImageInNewTab('${url}')">
`).join('');
}
function openImageInNewTab(url) {
window.open(url, '_blank');
}
function downloadImage(url) {
const a = document.createElement('a');
a.href = url;
a.download = 'generated_image.' + url.split('.').pop();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
// 初始化历史记录显示
updateHistoryDisplay();
</script>
</body>
</html>