demo:
新页面更新于2024/12/01
0.前言
Cloudflare公司推出了workers-ai,可以免费在 Cloudflare 的全球网络上运行由无服务器 GPU 提供支持的机器学习模型。
Workers AI 可以 Cloudflare 网络上使用自己的代码运行机器学习模型,也就是说,只要写一个js代码,就可以免费调用cloudflare提供的大模型。这可以为我们节省大量的GPU支出成本。
当前worker ai提供的模型涵盖了文本分类、文本生成(GPT)、语音识别、图像分类、翻译、文生图等一系列领域,而且调用十分方便。
虽然免费用户每天有限额,但个人使用还是很够的~
1.前序准备
首先你需要有一个域名,并连接到cloudflare账户上。如果没有域名的话,阿里云之类的服务商都有新人1元域名活动,一块钱就能拥有一个属于自己的网址也是蛮爽的。
而且cloudflare还提供了一系列 白嫖免费计划,可以零成本(仅域名支出)就能搭建整套的个人网站。
在添加域名的时候,选择Free计划
即可。
2.创建项目
2.1 创建worker
在右侧的菜单里,选择 Workers 和 Pages
,随后点击创建
按钮。
在下一页中点击创建Worker
,随便起个名,点击部署即可
点击右上角的编辑代码
,把自带的演示代码全部删掉,将下方代码复制粘贴进去
worker.js:
import HTML from './index.html';
export default {
async fetch(request, env) {
const originalHost = request.headers.get("host");
// 设置CORS头部
const corsHeaders = {
'Access-Control-Allow-Origin': '*', // 允许任何源
'Access-Control-Allow-Methods': 'GET, POST', // 允许的请求方法
'Access-Control-Allow-Headers': 'Content-Type' // 允许的请求头
};
// 如果这是一个预检请求,则直接返回CORS头部
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: corsHeaders
});
}
// 检查请求方法
if (request.method === 'POST') {
// 处理 POST 请求,用于 AI 绘画功能
const data = await request.json();
const upload = data.upload ?? false; // 由后端直接上传到图床,解决奇奇怪怪的网络问题
let model = '@cf/black-forest-labs/flux-1-schnell'; // 默认模型
// 检查 prompt 是否存在
if (!('prompt' in data) || data.prompt.trim() === '') {
return new Response('Missing prompt', { status: 400, headers: corsHeaders });
}
// 检查 model 是否存在,如果没有则使用默认模型
if ('model' in data) {
switch(data.model) {
case 'dreamshaper-8-lcm':
model = '@cf/lykon/dreamshaper-8-lcm';
break;
case 'stable-diffusion-xl-base-1.0':
model = '@cf/stabilityai/stable-diffusion-xl-base-1.0';
break;
case 'stable-diffusion-xl-lightning':
model = '@cf/bytedance/stable-diffusion-xl-lightning';
break;
case 'flux-1-schnell':
model = '@cf/black-forest-labs/flux-1-schnell';
break;
default:
break;
}
}
let inputs = {
prompt: data.prompt.trim()
};
// 如果模型不是 flux-1-schnell, 则添加 width 和 height
if (model !== '@cf/black-forest-labs/flux-1-schnell') {
inputs.width = data.resolution?.width ?? 1024;
inputs.height = data.resolution?.height ?? 1024;
} else {
// 反之添加 num_steps
inputs.num_steps = 8; // 默认值
}
const response = await env.AI.run(model, inputs);
if (model === '@cf/black-forest-labs/flux-1-schnell') {
// flux-1-schnell模型返回的是base64编码,其他模型返回的都是二进制的png文件
// 如果模型是 flux-1-schnell,处理 base64 图片数据
if (response.image) {
const base64Image = response.image;
const blob = await base64ToBlob(base64Image);
if (upload) {
const uploadResponse = await uploadToImageHost(blob);
if (uploadResponse?.src) {
// 返回图床的图片地址
return new Response(JSON.stringify({
imageUrl: `https://pic.foxhank.top${uploadResponse.src}`
}), {
status: 200,
headers: {
...corsHeaders,
'content-type': 'application/json'
}
});
} else {
return new Response(JSON.stringify({
errors: ['Image upload failed'],
messages: []
}), {
status: 500,
headers: {
...corsHeaders,
'content-type': 'application/json'
}
});
}
} else {
// 不上传,直接返回原始图片
return new Response(blob, {
headers: {
...corsHeaders,
'content-type': 'image/png'
}
});
}
} else {
return new Response(JSON.stringify({
errors: ['No image found in the response'],
messages: []
}), {
status: 500,
headers: {
...corsHeaders,
'content-type': 'application/json'
}
});
}
} else {
//处理其他模型
if (upload) {
//把返回的stream转化为blob
async function convertReadableStreamToBlob(readableStream) {
try {
// 将 ReadableStream 转换为 Blob
const response = new Response(readableStream);
const blob = await response.blob();
return blob;
} catch (error) {
console.error('转换 ReadableStream 出错:', error);
return null;
}
}
// 转换 ReadableStream 为 Blob
const blob = await convertReadableStreamToBlob(response);
const uploadResponse = await uploadToImageHost(blob);
if (uploadResponse?.src) {
return new Response(JSON.stringify({
imageUrl: `https://pic.foxhank.top${uploadResponse.src}`
}), {
status: 200,
headers: {
...corsHeaders,
'content-type': 'application/json'
}
});
} else {
return new Response(JSON.stringify({
errors: ['Image upload failed'],
messages: [uploadResponse.text]
}), {
status: 500,
headers: {
...corsHeaders,
'content-type': 'application/json'
}
});
}
} else {
// 如果不上传,直接返回原始图片
return new Response(response, {
headers: {
...corsHeaders,
'content-type': 'image/png;base64',
},
});
}
}
} else {
// 处理 GET 请求,返回 index.html
return new Response(HTML.replace(/{{host}}/g, originalHost), {
status: 200,
headers: {
...corsHeaders, // 合并CORS头部
"content-type": "text/html"
}
});
}
}
};
// 将 Base64 图片数据转换为 Blob
async function base64ToBlob(base64) {
const base64Data = base64.replace(/^data:image\/\w+;base64,/, '');
const byteString = atob(base64Data);
const arrayBuffer = new ArrayBuffer(byteString.length);
const uint8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
uint8Array[i] = byteString.charCodeAt(i);
}
return new Blob([uint8Array], { type: 'image/png' });
}
// 上传图片到图床
async function uploadToImageHost(blob) {
const formData = new FormData();
formData.append('file', blob, 'image.png');
try {
const response = await fetch('https://pic.foxhank.top/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const errorMessage = await response.text(); // 等待并获取错误信息
throw new Error(`HTTP error! status: ${response.status}, message: ${errorMessage}`);
}
const jsonResponse = await response.json();
return jsonResponse[0]; // 图床返回地址里面是 src 字段
} catch (error) {
console.error(error); // 记录错误信息
return null;
}
}
旧代码
import HTML from './index.html';
export default {
async fetch(request, env) {
const originalHost = request.headers.get("host");
// 设置CORS头部
const corsHeaders = {
'Access-Control-Allow-Origin': '*', // 允许任何源
'Access-Control-Allow-Methods': 'GET, POST', // 允许的请求方法
'Access-Control-Allow-Headers': 'Content-Type' // 允许的请求头
};
// 预检请求
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: corsHeaders
});
}
// 检查请求方法
if (request.method === 'POST') {
// 处理 POST 请求,用于 AI 绘画功能
const data = await request.json();
let model = '@cf/lykon/dreamshaper-8-lcm';
if ('prompt' in data && 'model' in data) {
switch(data.model) {
case 'dreamshaper-8-lcm':
model = '@cf/lykon/dreamshaper-8-lcm';
break;
case 'stable-diffusion-xl-base-1.0':
model = '@cf/stabilityai/stable-diffusion-xl-base-1.0';
break;
case 'stable-diffusion-xl-lightning':
model = '@cf/bytedance/stable-diffusion-xl-lightning';
break;
default:
break;
}
const inputs = {
prompt: data.prompt,
};
const response = await env.AI.run(model, inputs);
return new Response(response, {
headers: {
...corsHeaders,
'content-type': 'image/png;base64',
},
});
} else {
return new Response('Missing prompt or model', { status: 400, headers: corsHeaders });
}
} else {
// 处理 GET 请求,返回html页面
return new Response(HTML.replace(/{{host}}/g, originalHost), {
status: 200,
headers: {
...corsHeaders, // 合并CORS头部
"content-type": "text/html"
}
});
}
}
};
然后点击左上角的Explorer,选择New File,新建一个名为index.html
的文件(如图所示)
切换到此index.html文件,将以下代码粘贴进去:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>AI绘画</title>
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<meta content="使用先进的AI技术,轻松创作独特的数字艺术作品。探索无限可能,释放你的创意潜能。" name="description">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="AI绘画" name="apple-mobile-web-app-title">
<!--页面logo-->
<!-- <link rel="icon" href="https://xx.com" type="image/png">-->
<!-- <link rel="apple-touch-icon" href="https://xx.com">-->
<style>
:root {
--primary-color: #4facfe;
--secondary-color: #00f2fe;
--background-color: #f5f7fa;
--text-color: #333;
--card-background: rgba(255, 255, 255, 0.9);
}
body, html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
/*background: linear-gradient(135deg, var(--background-color) 0%, #c3cfe2 100%);*/
background: linear-gradient(135deg, #e6f5ff 0%, #ffe6f5 100%);
color: var(--text-color);
}
.container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
}
.card {
background-color: var(--card-background);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2rem;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
width: 90%;
max-width: 600px;
display: flex;
flex-direction: column;
align-items: center;
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15);
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
text-align: center;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.image-container {
width: 100%;
max-width: 500px;
height: 350px;
border-radius: 10px;
margin-bottom: 1rem;
background-color: rgba(0, 0, 0, 0.05);
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
}
#aiImage {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
display: none;
}
.loading-spinner {
border: 5px solid #f3f3f3;
border-top: 5px solid var(--primary-color);
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
select {
width: 100%;
padding: 0.75rem;
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 1rem;
transition: all 0.3s ease;
}
select:focus, input[type="text"]:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(79, 172, 254, 0.2);
}
.button-group {
display: flex;
justify-content: space-between;
width: 100%;
}
button {
padding: 0.75rem 1.5rem;
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
border: none;
outline: none;
}
.submit-btn {
background: #cccccc;
color: white;
flex-grow: 1;
margin-right: 10px;
}
.submit-btn.active {
background: linear-gradient(to right, var(--primary-color) 0%, var(--secondary-color) 100%);
}
.submit-btn:hover {
opacity: 0.9;
transform: translateY(-2px);
}
.download-btn {
background: #2ecc71;
color: white;
}
.download-btn:hover {
background: #27ae60;
}
.history-btn {
background: #3498db;
color: white;
margin-left: 10px;
}
.history-btn:hover {
background: #2980b9;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px;
border-radius: 10px;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.history-item {
display: flex;
align-items: center;
margin-bottom: 10px;
padding: 10px;
background-color: #f9f9f9;
border-radius: 5px;
}
.history-item img {
width: 50px;
height: 50px;
object-fit: cover;
margin-right: 10px;
border-radius: 5px;
}
.history-item-buttons {
margin-left: auto;
}
.history-item-buttons button {
margin-left: 5px;
padding: 5px 10px;
font-size: 0.8rem;
}
.redraw-btn {
background-color: #3498db;
color: white;
}
.delete-btn {
background-color: #e74c3c;
color: white;
}
.clear-history-btn {
background-color: #e74c3c;
color: white;
margin-top: 10px;
}
.theme-toggle {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
@media (max-width: 768px) {
.card {
width: 95%;
padding: 1.5rem;
}
h1 {
font-size: 2rem;
}
select, input[type="text"], button {
font-size: 0.9rem;
}
.image-container {
height: 250px;
}
}
@media (max-width: 480px) {
.card {
width: 100%;
border-radius: 0;
}
h1 {
font-size: 1.75rem;
}
.button-group {
flex-direction: column;
}
.submit-btn, .download-btn, .history-btn {
margin: 5px 0;
width: 100%;
}
}
.input-group {
display: flex; /* 使用 Flex 布局 */
align-items: center; /* 垂直居中对齐 */
width: 100%; /* 占据父元素的全部宽度 */
margin-bottom: 1rem;
}
.input-group input[type="text"] {
width: 75%; /* 输入框宽度设为70% */
padding: 0.75rem;
margin-right: 1rem; /* 添加右边距以便与按钮隔开 */
border: 1px solid #ccc;
border-radius: 5px;
font-size: 1rem;
transition: all 0.3s ease;
}
.random-btn {
padding: 0.75rem 1rem;
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
border: none;
outline: none;
background-color: #3498db;
color: white;
white-space: nowrap; /* 防止文字换行 */
flex-shrink: 0; /* 防止按钮缩小 */
}
.random-btn:hover {
background-color: #2980b9;
}
/*黑暗模式*/
.dark-theme {
--background-color: #2c3e50;
--text-color: #ecf0f1;
--card-background: rgba(44, 62, 80, 0.9);
background: linear-gradient(135deg, #1d2731 0%, #292e3c 100%);
}
/*.dark-theme select, .dark-theme input[type="text"] {*/
.dark-theme select, .dark-theme input[type="text"], .dark-theme .custom-size-inputs input[type="number"] {
background-color: #333; /* 较深的背景色 */
color: #fff; /* 文本颜色 */
border-color: #555; /* 边框颜色 */
}
/* 添加尺寸选项 */
.size-options {
display: flex;
align-items: center; /* 保持垂直居中对齐 */
justify-content: space-between; /* 使选择框和输入框分别靠左右两端 */
width: 100%; /* 确保占据整行 */
/*margin-top: 0.5rem;*/
}
.size-options label {
white-space: nowrap; /* 防止文字换行 */
flex-shrink: 0; /* 防止按钮缩小 */
margin-right: 0.5rem;
}
.size-options select {
width: 100%; /* 选择框占据左侧空间 */
margin-right: 0.5rem;
}
.custom-size-inputs {
display: flex;
align-items: center;
}
.custom-size-inputs input[type="number"] {
width: 50px; /* 输入框宽度设为固定值 */
padding: 0.75rem;
margin-right: 0.5rem; /* 添加右边距以便与按钮隔开 */
border: 1px solid #ccc;
border-radius: 5px;
font-size: 1rem;
margin-bottom: 1rem;
/*transition: all 0.3s ease;*/
}
/*响应式样式 */
@media (max-width: 480px) {
.size-options {
flex-direction: column;
}
.size-options select {
margin-bottom: 1rem; /* 为选择框添加底部边距 */
}
.custom-size-inputs {
flex-direction: row; /* 输入框水平排列 */
justify-content: space-between; /* 输入框分别靠左右两端 */
width: 100%; /* 确保占据整行 */
margin-top: 0; /* 移除顶部边距 */
}
.custom-size-inputs input[type="number"] {
margin-bottom: 0; /* 移除底部边距 */
}
}
.footer {
display: flex;
justify-content: center;
padding: 5px;
color: #888; /* 灰色文字 */
font-size: 0.8em; /* 小号字体 */
border-top: 1px solid #ddd; /* 可选边框 */
}
.footer a {
color: #888; /* 灰色链接 */
text-decoration: none; /* 默认无下划线 */
transition: text-decoration 0.3s ease; /* 过渡效果 */
}
.footer a:hover {
text-decoration: underline; /* 鼠标悬停时显示下划线 */
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
const submitButton = document.getElementById('submitButton');
const promptInput = document.getElementById('prompt');
const downloadBtn = document.getElementById('downloadBtn');
const historyBtn = document.getElementById('historyBtn');
const modal = document.getElementById('historyModal');
const closeBtn = document.getElementsByClassName('close')[0];
const historyList = document.getElementById('historyList');
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
const themeToggle = document.getElementById('themeToggle');
let history = JSON.parse(localStorage.getItem('aiDrawingHistory')) || [];
function updateHistory(prompt, imageUrl) {
history.push({prompt: prompt, imageUrl: imageUrl, timestamp: new Date()});
localStorage.setItem('aiDrawingHistory', JSON.stringify(history));
renderHistory();
}
const prompts = ['1girl,solo,cute,in glass,atmosphere_X,best quality,beautiful,extremely detailed,masterpiece,very aesthetic',
'a girl,,nahida,light,full body,symbol eye, nahida,1girl,fair_skin,in summer,day,in a meadow,sky,cirrus_fibratus,intense shadows,blonde hair,pleated_dress,collared_shirt,blue eyes,long hair,fang,smile',
'((best quality)), ((masterpiece)),A Chinese couple in Hanfu embracing on an arch bridge, a sky full of rose petals, a romantic atmosphere, a huge moon, colorful clouds, clouds, ethereal, reflections of water, a mirage, a breeze,(Chinese ink style)',
'simple background, flower, signature, no humans, sparkle, leaf, plant, white flower, black background, still life, embroidery',
' 1 girl,(orange light effect),hair ornament,jewelry,looking at viewer,flower,floating hair,water,underwater,air bubble,submerged, 80sDBA style',
'masterpiece,best quality,high quality,loli,1girl, solo, long hair, looking at viewer, blush, bangs, thighhighs, dress, ribbon, brown eyes, very long hair, closed mouth, standing, full body, yellow eyes, white hair, short sleeves, outdoors, sky,no shoes, day, puffy sleeves, looking back, cloud, from behind, white dress, white thighhighs, red ribbon, tree, blue sky, puffy short sleeves, petals, cherry blossoms, skirt hold,',
' 1 girl,Clothes in the shape of snowflake,render,technology, (best quality) (masterpiece), (highly in detailed), 4K,Official art, unit 8 k wallpaper, ultra detailed, masterpiece, best quality, extremely detailed,CG,low saturation, as style, line art',
' best quality,masterpiece,sculpture,wonderland,,chinese fairy tales,an old man,boiling tea,drink tea,a painting of history floating and curved in the background,mountain,white cloud,chinese style courtyard,pavilion,chinese tea mountains,, Chinese architecture, trees,,white hair ,',
' 1girl, absurdres, arrow_(symbol), ata-zhubo, bicycle, billboard, black_eyes, black_footwear, black_hair, blue_sky, bridge, building, car, cardigan, city, cityscape, commentary_request, crosswalk, day, fire_hydrant, folding_bicycle, grey_cardigan, highres, lamppost, loafers, motor_vehicle, necktie, original, overpass, power_lines, railing, red_necktie, red_skirt, road, road_sign, scenery, school_uniform, shoes, short_hair, sign, skirt, sky, solo, stairs, standing, street, traffic_cone, traffic_light, truck, utility_pole, vending_machine',
'Steep stone walls towered into the sky, thunderous waves crashed against the river bank, and the waves stirred up like thousands of piles of white snow.',
'1girl, solo, elf, golden eyes, glowing eyes, slit_pupils, silver hair, green gradient hair, long hair, blunt bangs, brown capelet, frilled shirt, long sleeves, green brooch, pouch, belt, brown gloves, upper body, (chibi:0.4), (close-up), (broken:1.3), half-closed eye, expressionless, from side, depth of field, fallen leaves, side light, gingko, tree, masterpiece,bestquality, line art,',
'flower, outdoors, sky, tree, no humans, window, bird, building, scenery, house,oil painting style',
' (masterpiece,top quality,best quality,official art,beautiful and aesthetic:1.2),gf-hd,1girl,loli,solo,long hair,lovely smilie,(wink),(blazer,white shirt,white blouse:2),cozy,(lace details),v,robinSR,love heart',
' moon,outdoors,full moon,night,flower,cherry blossoms,sky,tree,pink flower flying around,night sky,no humans,masterpiece,illustration,extremely fine and beautiful,perfect details,stream,',
'comic,bestquality, masterpiece, super details, fine fabrics, highly detailed eyes and face, extremely fine and detailed, perfect details, 1 girl, solo, long hair, bangs, rosy cheeks, pearl hair clips, strawberry blonde tresses, strawberry-shaped stud earrings, sweet lolita-style dress with berry prints, holding a basket of fresh strawberries, whimsical garden setting, sunny and bright',
'(comic),a girl, soft colors, long curly hair, ocean blue hair, delicate flower crown, shimmering hazel eyes, gentle smile, bohemian style dress, sunny beach background, headshot',
'bestquality, masterpiece, super details, fine fabrics, high detailed eyes and detailed face, comic, extremely fine and detailed, perfect details, 1girl, solo, long hair, bangs, rose pink eyes, long sleeves, frilly pastel dress, lace accessory, sweet smile, holding a pink macaron, cotton candy pink hair, hair ribbons, soft pink and white dress, fairy tale garden, pink flowers, balloons',
];
// 随机选择一个提示词
function getRandomPrompt() {
const randomIndex = Math.floor(Math.random() * prompts.length);
return prompts[randomIndex];
}
// 随机提示词按钮
const randomButton = document.getElementById('randomButton');
randomButton.addEventListener('click', function () {
const randomPrompt = getRandomPrompt();
promptInput.value = randomPrompt;
promptInput.dispatchEvent(new Event('input')); // 触发 input 事件
});
function setupSizeOptions() {
const sizeSelect = document.getElementById('size-select');
const widthInput = document.getElementById('width-input');
const heightInput = document.getElementById('height-input');
// 监听输入框的变化,限制最大值为1024
widthInput.addEventListener('input', function () {
if (parseInt(this.value) > 1024) {
this.value = 1024;
}
});
heightInput.addEventListener('input', function () {
if (parseInt(this.value) > 1024) {
this.value = 1024;
}
});
sizeSelect.addEventListener('change', function () {
const selectedValue = this.value;
// 如果选择了自定义,则启用输入框
if (selectedValue === 'custom') {
widthInput.disabled = false;
heightInput.disabled = false;
return;
}
// 否则禁用输入框并根据选择的尺寸设置默认值
widthInput.disabled = true;
heightInput.disabled = true;
switch (selectedValue) {
case '16x9-horizontal':
widthInput.value = 1024;
heightInput.value = 576;
break;
case '16x9-vertical':
widthInput.value = 576;
heightInput.value = 1024;
break;
case '4x3-horizontal':
widthInput.value = 1024;
heightInput.value = 768;
break;
case '4x3-vertical':
widthInput.value = 768;
heightInput.value = 1024;
break;
case '1x1':
widthInput.value = 1024;
heightInput.value = 1024;
break;
default:
widthInput.value = 1024;
heightInput.value = 576;
break;
}
});
}
setupSizeOptions()
function renderHistory() {
historyList.innerHTML = '';
history.forEach((item, index) => {
const historyItem = document.createElement('div');
historyItem.className = 'history-item';
historyItem.innerHTML = `
<img src="${item.imageUrl}" alt="${item.prompt}">
<div>
<span>${item.prompt}</span>
<br>
<small>${new Date(item.timestamp).toLocaleString()}</small>
</div>
<div class="history-item-buttons">
<button class="redraw-btn tooltip" data-index="${index}">重绘<span class="tooltiptext">使用此提示词重新生成图片</span></button>
<button class="delete-btn tooltip" data-index="${index}">删除<span class="tooltiptext">从历史记录中删除此项</span></button>
</div>
`;
historyList.appendChild(historyItem);
});
}
function deleteHistoryItem(index) {
history.splice(index, 1);
localStorage.setItem('aiDrawingHistory', JSON.stringify(history));
renderHistory();
}
function clearHistory() {
if (confirm('确定要清空所有历史记录吗?此操作不可撤销。')) {
history = [];
localStorage.removeItem('aiDrawingHistory');
renderHistory();
}
}
async function generateImage(prompt, width, height) { // 修改函数签名以接收宽度和高度
submitButton.disabled = true;
submitButton.textContent = '正在创作...';
document.querySelector('.loading-overlay').style.display = 'flex';
const model = document.getElementById('model').value;
const resolution = {
width: parseInt(width), // 使用从参数接收的宽度
height: parseInt(height) // 使用从参数接收的高度
};
try {
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => {
controller.abort();
}, 300000);
// const response = await fetch(`${window.location.origin}`, {
const response = await fetch(`https://aidraw.foxhank.top`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'model': model,
'prompt': prompt,
'resolution': resolution,
'upload': true
}),
signal: signal
});
if (!response.ok) {
throw new Error(`请求失败:${response.status} ${response.statusText}`);
}
// 由于历史记录功能,本地保存历史生成的图片会超过浏览器限额,所以使用图床存储历史记录
// 后期可能设一个开关,如果关闭开关就不上传到图床
const responseData = await response.json();
// 上传改为服务器端上传,返回一个图床路径
// 提取 imageUrl
const imageUrl = responseData.imageUrl;
document.getElementById('aiImage').src = imageUrl;
// document.getElementById('aiImage').src = Image;
updateHistory(prompt, imageUrl);
downloadBtn.style.display = 'block';
} catch (error) {
if (error.name === 'AbortError') {
alert('服务器连接超时,请稍后重试。');
} else {
console.error('Error:', error);
alert('生成过程中发生错误,请重试。\n错误:' + error.message + '\n');
}
} finally {
submitButton.textContent = '开始创作';
submitButton.disabled = false;
document.querySelector('.loading-overlay').style.display = 'none';
}
}
promptInput.addEventListener('input', function () {
if (this.value.trim() !== '') {
submitButton.classList.add('active');
} else {
submitButton.classList.remove('active');
}
});
submitButton.addEventListener('click', function (event) {
event.preventDefault();
if (promptInput.value.trim() === '') {
alert('请输入描述词');
return;
}
generateImage(promptInput.value.trim(), document.getElementById('width-input').value, document.getElementById('height-input').value);
});
downloadBtn.addEventListener('click', function () {
const image = document.getElementById('aiImage');
const link = document.createElement('a');
link.href = image.src;
link.download = `ai-artwork-${new Date().toISOString()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
historyBtn.onclick = function () {
modal.style.display = 'block';
renderHistory();
}
closeBtn.onclick = function () {
modal.style.display = 'none';
}
window.onclick = function (event) {
if (event.target == modal) {
modal.style.display = 'none';
}
}
historyList.addEventListener('click', function (event) {
if (event.target.classList.contains('redraw-btn')) {
const index = event.target.getAttribute('data-index');
const prompt = history[index].prompt;
promptInput.value = prompt;
modal.style.display = 'none';
generateImage(prompt);
} else if (event.target.classList.contains('delete-btn')) {
const index = event.target.getAttribute('data-index');
deleteHistoryItem(index);
}
});
clearHistoryBtn.addEventListener('click', clearHistory);
themeToggle.addEventListener('click', function () {
document.body.classList.toggle('dark-theme');
themeToggle.textContent = document.body.classList.contains('dark-theme') ? '🌞' : '🌙';
localStorage.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light');
});
// 检查并应用保存的主题
if (localStorage.getItem('theme') === 'dark') {
document.body.classList.add('dark-theme');
themeToggle.textContent = '🌞';
}
// 添加键盘快捷键支持
document.addEventListener('keydown', function (event) {
if (event.ctrlKey && event.key === 'Enter') {
submitButton.click();
}
});
// 添加拖放支持
const dropZone = document.querySelector('.image-container');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.style.border = '2px dashed #4facfe';
});
dropZone.addEventListener('dragleave', () => {
dropZone.style.border = 'none';
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.style.border = 'none';
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = (e) => {
document.getElementById('aiImage').src = e.target.result;
};
reader.readAsDataURL(file);
}
});
});
</script>
</head>
<body>
<div class="container">
<div class="card">
<h1>AI绘画创作平台</h1>
<div class="image-container">
<img alt="AI生成的图片"
id="aiImage"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==">
<div class="loading-overlay">
<div class="loading-spinner"></div>
</div>
</div>
<select id="model">
<option value="dreamshaper-8-lcm">DreamShaper 8 LCM(容易出黑图)</option>
<option value="stable-diffusion-xl-base-1.0">Stable Diffusion XL Base 1.0(效果较好)</option>
<option value="stable-diffusion-xl-lightning">Stable Diffusion XL Lightning(效果一般 速度快)</option>
<option selected value="flux-1-schnell">flux-1-schnell(推荐用这个)</option>
>
</select>
<div class="input-group">
<input id="prompt" placeholder="请输入你想要创作的画面描述..." type="text">
<button class="button random-btn" id="randomButton">随机提示词</button>
</div>
<!-- 添加尺寸选择功能 -->
<div class="size-options">
<label for="size-select">尺寸:</label>
<select id="size-select">
<option value="16x9-horizontal">横屏 16:9</option>
<option value="16x9-vertical">竖屏 16:9</option>
<option value="4x3-horizontal">横屏 4:3</option>
<option value="4x3-vertical">竖屏 4:3</option>
<option value="1x1">正方形 1:1</option>
<option value="custom">自定义</option>
</select>
<!-- 自定义输入框 -->
<div class="custom-size-inputs">
<label for="width-input">宽度</label>
<input disabled id="width-input" max="1024" min="1" placeholder="宽度" type="number" value="1024">
<label for="height-input">高度</label>
<input disabled id="height-input" max="1024" min="1" placeholder="高度" type="number" value="576">
</div>
</div>
<div class="button-group">
<button class="submit-btn" id="submitButton" type="button">开始创作</button>
<button class="download-btn" id="downloadBtn" style="display: none;" type="button">下载图片</button>
<button class="history-btn" id="historyBtn" type="button">历史记录</button>
</div>
<footer class="footer">
</br></br>
<p>前端来自佬友小黑紫一枚:<a href="https://linux.do/u/jiu1/" target="_blank">https://linux.do/u/jiu1/</a>
</p>
<p>项目部署于:<a href="https://workers.cloudflare.com/" target="_blank">Cloudflare Workers</a></p>
</footer>
</div>
</div>
<div class="modal" id="historyModal">
<div class="modal-content">
<span class="close">×</span>
<h2>历史记录</h2>
<div id="historyList"></div>
<button class="clear-history-btn" id="clearHistoryBtn">清空历史记录</button>
</div>
</div>
<button class="theme-toggle" id="themeToggle">🌙</button>
</body>
</html>
index.html
旧代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>AI绘画</title>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
display: flex;
flex-direction: column; /* 内容垂直排列 */
}
.box {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to right, #e6f5ff, #ffe6f5);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.card {
position: absolute;
inset: 2% auto;
max-width: 80%;
width: 90%;
backdrop-filter: blur(21px) saturate(180%);
-webkit-backdrop-filter: blur(21px) saturate(180%);
background-color: rgba(255, 255, 255, 0.53);
border-radius: 10px;
border: 1px solid rgba(209, 213, 219, 0.3);
padding: 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 1rem;
left: 50%;
transform: translateX(-50%);
text-align: center;
overflow: hidden;
}
h1 {
font-size: 2.5em;
margin-bottom: 1rem;
}
img {
width: 100%;
max-width: 400px;
height: auto;
margin-bottom: 1rem;
}
select, input[type="text"] {
width: 100%;
padding: 10px;
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 5px;
line-height: 3.5;
background-color: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(30px) saturate(180%);
border-radius: 5px;
padding: 10px;
outline: none;
transition: background-color 0.3s ease;
}
select, input[type="text"]:focus {
background-color: rgba(255, 255, 255, 99.9);
}
button.submit-btn {
background: linear-gradient(to bottom, #ade8f4, #6cb3e3);
border-radius: 5px;
color: white;
padding: 10px 20px;
font-family: Arial, sans-serif;
cursor: pointer;
border: none;
transition: all 0.3s ease;
}
button.random-btn {
background: white; /* 白色背景 */
color: #007BFF; /* 蓝色文字 */
border-radius: 5px;
padding: 5px 40px;
font-family: Arial, sans-serif;
cursor: pointer;
border: 1px solid #007BFF; /* 添加蓝色边框 */
transition: all 0.3s ease;
}
button.submit-btn:hover {
opacity: 0.6;
}
@media screen and (max-width: 600px) {
.card {
inset: 10% auto;
max-width: 100%;
width: 90%;
left: 0%;
transform: none;
/* 保持原有的模糊效果和其他样式 */
backdrop-filter: blur(21px) saturate(180%);
-webkit-backdrop-filter: blur(21px) saturate(180%);
background-color: rgba(255, 255, 255, 0.53);
border-radius: 10px;
border: 1px solid rgba(209, 213, 219, 0.3);
padding: 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
}
select, input[type="text"] {
width: 90%;
padding: 10px;
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 5px;
line-height: 7;
background-color: rgba(255, 255, 255, 0.1); /* 更改背景颜色以匹配磨砂效果 */
backdrop-filter: blur(30px) saturate(180%); /* 增加模糊量以获得更重的磨砂效果 */
border-radius: 5px; /* 可选:增加边框圆角美化 */
padding: 10px; /* 输入框内边距,根据需要调整 */
outline: none; /* 移除焦点时的轮廓 */
transition: background-color 0.3s ease; /* 平滑的背景色过渡效果 */
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
const submitButton = document.getElementById('submitButton');
submitButton.addEventListener('click', async function (event) {
event.preventDefault();
submitButton.disabled = true;
submitButton.textContent = '正在生成...';
const model = document.getElementById('model').value;
const prompt = document.getElementById('prompt').value;
// 检查prompt是否为空
if (prompt === '') {
alert('请输入描述词');
submitButton.textContent = '提交';
submitButton.disabled = false;
return;
}
const blobToBase64 = (blob) => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = reject;
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(blob);
});
// 获取域名
const currentDomain = window.location.origin;
try {
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => {
controller.abort();
}, 30000); // 30秒超时
const response = await fetch(`${currentDomain}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'model': model,
'prompt': prompt
}),
signal: signal
});
if (!response.ok) {
throw new Error(`请求失败:${response.status} ${response.statusText}`);
}
// 读取响应的文本数据
// 将响应体转换为Blob
const blob = await response.blob();
// 将Blob转换为Base64编码的字符串
const Image = await blobToBase64(blob);
console.log('Base64 Image:', Image);
// const base64Image = await response.text();
document.getElementById('aiImage').src = `${Image}`;
} catch (error) {
if (error.name === 'AbortError') {
alert('服务器连接超时,请稍后重试。');
} else {
console.error('Error:', error);
alert('生成过程中发生错误,请重试。');
}
} finally {
submitButton.textContent = '提交';
submitButton.disabled = false;
}
});
document.getElementById('randomButton').addEventListener('click', function () {
// 提供的随机描述词
var prompts = ['1girl,solo,cute,in glass,atmosphere_X,best quality,beautiful,extremely detailed,masterpiece,very aesthetic',
'a girl,,nahida,light,full body,symbol eye, nahida,1girl,fair_skin,in summer,day,in a meadow,sky,cirrus_fibratus,intense shadows,blonde hair,pleated_dress,collared_shirt,blue eyes,long hair,fang,smile',
'((best quality)), ((masterpiece)),A Chinese couple in Hanfu embracing on an arch bridge, a sky full of rose petals, a romantic atmosphere, a huge moon, colorful clouds, clouds, ethereal, reflections of water, a mirage, a breeze,(Chinese ink style)',
'simple background, flower, signature, no humans, sparkle, leaf, plant, white flower, black background, still life, embroidery',
' 1 girl,(orange light effect),hair ornament,jewelry,looking at viewer,flower,floating hair,water,underwater,air bubble,submerged, 80sDBA style',
'masterpiece,best quality,high quality,loli,1girl, solo, long hair, looking at viewer, blush, bangs, thighhighs, dress, ribbon, brown eyes, very long hair, closed mouth, standing, full body, yellow eyes, white hair, short sleeves, outdoors, sky,no shoes, day, puffy sleeves, looking back, cloud, from behind, white dress, white thighhighs, red ribbon, tree, blue sky, puffy short sleeves, petals, cherry blossoms, skirt hold,',
' 1 girl,Clothes in the shape of snowflake,render,technology, (best quality) (masterpiece), (highly in detailed), 4K,Official art, unit 8 k wallpaper, ultra detailed, masterpiece, best quality, extremely detailed,CG,low saturation, as style, line art',
' best quality,masterpiece,sculpture,wonderland,,chinese fairy tales,an old man,boiling tea,drink tea,a painting of history floating and curved in the background,mountain,white cloud,chinese style courtyard,pavilion,chinese tea mountains,, Chinese architecture, trees,,white hair ,',
' 1girl, absurdres, arrow_(symbol), ata-zhubo, bicycle, billboard, black_eyes, black_footwear, black_hair, blue_sky, bridge, building, car, cardigan, city, cityscape, commentary_request, crosswalk, day, fire_hydrant, folding_bicycle, grey_cardigan, highres, lamppost, loafers, motor_vehicle, necktie, original, overpass, power_lines, railing, red_necktie, red_skirt, road, road_sign, scenery, school_uniform, shoes, short_hair, sign, skirt, sky, solo, stairs, standing, street, traffic_cone, traffic_light, truck, utility_pole, vending_machine',
'1girl, solo, elf, golden eyes, glowing eyes, slit_pupils, silver hair, green gradient hair, long hair, blunt bangs, brown capelet, frilled shirt, long sleeves, green brooch, pouch, belt, brown gloves, upper body, (chibi:0.4), (close-up), (broken:1.3), half-closed eye, expressionless, from side, depth of field, fallen leaves, side light, gingko, tree, masterpiece,bestquality, line art,',
'flower, outdoors, sky, tree, no humans, window, bird, building, scenery, house,oil painting style',
];
var randomIndex = Math.floor(Math.random() * prompts.length);
document.getElementById('prompt').value = prompts[randomIndex];
});
});
</script>
</head>
<body>
<div class="box">
<div class="card">
<h1>AI绘画</h1>
<img id="aiImage"
src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAADEUlEQVR4nO3BgQAAAADDoPlTX+EAVQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBvArQAAVkUTe8AAAAASUVORK5CYII="
alt="AI生成的图片"> <!-- 默认是黑色的图片占位符 -->
<select id="model">
<option value="dreamshaper-8-lcm">DreamShaper 8 LCM(容易出黑图)</option>
<option value="stable-diffusion-xl-base-1.0" selected>Stable Diffusion XL Base 1.0(效果好,速度较慢)</option>
<option value="stable-diffusion-xl-lightning">Stable Diffusion XL Lightning(效果一般,速度较快)</option>
</select>
<input type="text" id="prompt" placeholder="请输入描述词...">
<button type="button" class="random-btn" id="randomButton">随机提示词</button>
<button type="button" class="submit-btn" id="submitButton">提交</button>
</div>
</div>
</body>
</html>
这里给定了几个随机的提示词,点击“随机提示词”按钮即可填充,可以根据需要增添。每个提示词我都生成了几张示例图(见最后)。
之后点击右上角的部署
按钮,并点击“保存并部署”,即可保存此Worker。
2.2 绑定AI
返回到worker页面,选择设置--变量,向下滑找到"AI绑定"一项。选择“添加绑定”,
选择“添加绑定”,名称写AI
即可。随后点击部署按钮。
2.3 绑定自己的域名
默认的workers.dev域名由于网络原因在国内无法使用,所以需要绑定一个自己的域名。
返回worker页面,点击“设置”,选择“添加自定义域”,填入一个刚才绑定在cloudflare的域名,二级域名可以随便写,比如:xxx.domain.com,这里的domain.com就是刚才绑定在cloudflare的域名,xxx可以随意更改。
输入后即可点击“添加自定义域”。cloudflare会自动完成解析的工作。
随后点击下方的“添加路由”,路由输入框填写与刚才相同的内容(xxx.domain.com),区域选择此主域名
稍等片刻,等待证书初始化完成,点击此链接即可访问刚才部署的页面。
黑色图片是占位符;输入描述之后点击提交,稍后即可生成图片。
3.随机提示词
以下是部分我使用的提示词,可以修改html页面var prompts
部分,每一行是一个提示词,格式如下:
var prompts = [
'提示词1',
'提示词2',
'提示词3',
];
部分提示词及生成的图如下,均使用Stable Diffusion XL Base 1.0生成。
1girl,solo,cute,in glass,atmosphere_X,best quality,beautiful,extremely detailed,masterpiece,very aesthetic
a girl,,nahida,light,full body,symbol eye, nahida,1girl,fair_skin,in summer,day,in a meadow,sky,cirrus_fibratus,intense shadows,blonde hair,pleated_dress,collared_shirt,blue eyes,long hair,fang,smile
((best quality)), ((masterpiece)),A Chinese couple in Hanfu embracing on an arch bridge, a sky full of rose petals, a romantic atmosphere, a huge moon, colorful clouds, clouds, ethereal, reflections of water, a mirage, a breeze,(Chinese ink style)
simple background, flower, signature, no humans, sparkle, leaf, plant, white flower, black background, still life, embroidery
1 girl,(orange light effect),hair ornament,jewelry,looking at viewer,flower,floating hair,water,underwater,air bubble,submerged, 80sDBA style
masterpiece,best quality,high quality,loli,1girl, solo, long hair, looking at viewer, blush, bangs, thighhighs, dress, ribbon, brown eyes, very long hair, closed mouth, standing, full body, yellow eyes, white hair, short sleeves, outdoors, sky,no shoes, day, puffy sleeves, looking back, cloud, from behind, white dress, white thighhighs, red ribbon, tree, blue sky, puffy short sleeves, petals, cherry blossoms, skirt hold,
(ps:效果不太好,AI还是无法理解什么是手)
1 girl,Clothes in the shape of snowflake,render,technology, (best quality) (masterpiece), (highly in detailed), 4K,Official art, unit 8 k wallpaper, ultra detailed, masterpiece, best quality, extremely detailed,CG,low saturation, as style, line art
best quality,masterpiece,sculpture,wonderland,,chinese fairy tales,an old man,boiling tea,drink tea,a painting of history floating and curved in the background,mountain,white cloud,chinese style courtyard,pavilion,chinese tea mountains,, Chinese architecture, trees,,white hair ,
1girl, absurdres, arrow_(symbol), ata-zhubo, bicycle, billboard, black_eyes, black_footwear, black_hair, blue_sky, bridge, building, car, cardigan, city, cityscape, commentary_request, crosswalk, day, fire_hydrant, folding_bicycle, grey_cardigan, highres, lamppost, loafers, motor_vehicle, necktie, original, overpass, power_lines, railing, red_necktie, red_skirt, road, road_sign, scenery, school_uniform, shoes, short_hair, sign, skirt, sky, solo, stairs, standing, street, traffic_cone, traffic_light, truck, utility_pole, vending_machine
(ps:AI还是没法把控细节,或者是这个模型的原因...?)
1girl, solo, elf, golden eyes, glowing eyes, slit_pupils, silver hair, green gradient hair, long hair, blunt bangs, brown capelet, frilled shirt, long sleeves, green brooch, pouch, belt, brown gloves, upper body, (chibi:0.4), (close-up), (broken:1.3), half-closed eye, expressionless, from side, depth of field, fallen leaves, side light, gingko, tree, masterpiece,bestquality, line art,
flower, outdoors, sky, tree, no humans, window, bird, building, scenery, house,oil painting style
4.结论
选项全部默认,绘图大概在10s左右一张图,倒是可以接受。如果想一次生成多张图片,可以使用程序给api发请求,worker理论上并发没限制。
AI还是对手部优化不是很理想,涉及到手部的基本都是畸形种,可能是我的模型选取问题...?,而且AI绘画没有图层概念,主体和背景都是交融在一起的,只能期望后续发展了。
不错不错,我喜欢看
求更新教程,想用flux-1-schnell
更新啦,解决了一大堆奇奇怪怪的bug
感谢大佬分享,部署成功了,不过奇怪的是国内生成图片就会报错,科学出去生成图片就正常😂
还有大佬能不能更新下代码,一次生成4张图片哦,非常感谢。🙏