前言
Cloudflare新出了workers-ai,和GPT类似可以回答问题和绘画,刷到了一篇文章使用Cloudflare Worker搭建自己的AI绘画工具,前来折腾一个AI绘画网站,不过效果一般,可以看看demo测试一下。DEMO
:https://ai.kafuchino.top
正式开始
注意,这些代码都是狐狸汉克的小站的,你可以直接跳转到原作者的部署教程
创建worker
在右侧的菜单里,选择 Workers 和 Pages
,随后点击创建
按钮。

在下一页中点击创建Worker
,随便起个名,点击部署即可

点击右上角的编辑代码
,把自带的演示代码全部删掉,将下方代码复制粘贴进去
worker.js
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' // 允许的请求头
};
// 预检请求
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文件,将以下代码粘贴进去:
index.html
html
<!DOCTYPE html>
<!-- saved from url=(0027)https://aidraw.foxhank.top/ -->
<html lang="zh"><head><meta http-equiv="Content-Type" content="text/html; 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">
<!-- <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(localr2.getItem('aiDrawingHistory')) || [];
function updateHistory(prompt, imageUrl) {
history.push({prompt: prompt, imageUrl: imageUrl, timestamp: new Date()});
localr2.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);
localr2.setItem('aiDrawingHistory', JSON.stringify(history));
renderHistory();
}
function clearHistory() {
if (confirm('确定要清空所有历史记录吗?此操作不可撤销。')) {
history = [];
localr2.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
}),
signal: signal
});
if (!response.ok) {
throw new Error(`请求失败:${response.status} ${response.statusText}`);
}
const blob = await response.blob();
console.log(blob)
const imageUrl = await uploadToImageHost(blob);
const Image = await blobToBase64(blob);
// console.log('Base64 Image:', Image);
console.log('Imageurl:', 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';
}
}
async function uploadToImageHost(blob) {
const formData = new FormData();
formData.append("file", blob, 'generated-image.png'); // 使用文件名
try {
const response = await fetch('https://pic.foxhank.top/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
document.getElementById('aiImage').src = Image;
throw new Error(`上传失败:${response.status} ${response.statusText}`);
}
// 解析返回
const result = await response.json();
return `https://pic.foxhank.top${result[0].src}`;
} catch (error) {
console.error('Upload Error:', error);
throw error;
}
}
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') ? '🌞' : '🌙';
localr2.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light');
});
// 检查并应用保存的主题
if (localr2.getItem('theme') === 'dark') {
document.body.classList.add('dark-theme');
themeToggle.textContent = '🌞';
}
const blobToBase64 = (blob) => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = reject;
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(blob);
});
// 添加键盘快捷键支持
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="./AI绘画_files/generated-image.png">
<div class="loading-overlay" style="display: none;">
<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 active" id="submitButton" type="button">开始创作</button>
<button class="download-btn" id="downloadBtn" style="display: block;" 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" style="display: none;">
<div class="modal-content">
<span class="close">×</span>
<h2>历史记录</h2>
<div id="historyList"><div class="history-item">
<img src="./AI绘画_files/generated-image(1).png" alt="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.">
<div>
<span>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.</span>
<br>
<small>2024/10/11 15:11:04</small>
</div>
<div class="history-item-buttons">
<button class="redraw-btn tooltip" data-index="0">重绘<span class="tooltiptext">使用此提示词重新生成图片</span></button>
<button class="delete-btn tooltip" data-index="0">删除<span class="tooltiptext">从历史记录中删除此项</span></button>
</div>
</div><div class="history-item">
<img src="./AI绘画_files/generated-image(2).png" alt="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">
<div>
<span>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</span>
<br>
<small>2024/10/11 16:04:44</small>
</div>
<div class="history-item-buttons">
<button class="redraw-btn tooltip" data-index="1">重绘<span class="tooltiptext">使用此提示词重新生成图片</span></button>
<button class="delete-btn tooltip" data-index="1">删除<span class="tooltiptext">从历史记录中删除此项</span></button>
</div>
</div><div class="history-item">
<img src="./AI绘画_files/generated-image(3).png" alt="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">
<div>
<span>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</span>
<br>
<small>2024/10/11 16:04:59</small>
</div>
<div class="history-item-buttons">
<button class="redraw-btn tooltip" data-index="2">重绘<span class="tooltiptext">使用此提示词重新生成图片</span></button>
<button class="delete-btn tooltip" data-index="2">删除<span class="tooltiptext">从历史记录中删除此项</span></button>
</div>
</div><div class="history-item">
<img src="./AI绘画_files/generated-image.png" alt="(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">
<div>
<span>(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</span>
<br>
<small>2024/10/11 16:05:09</small>
</div>
<div class="history-item-buttons">
<button class="redraw-btn tooltip" data-index="3">重绘<span class="tooltiptext">使用此提示词重新生成图片</span></button>
<button class="delete-btn tooltip" data-index="3">删除<span class="tooltiptext">从历史记录中删除此项</span></button>
</div>
</div></div>
<button class="clear-history-btn" id="clearHistoryBtn">清空历史记录</button>
</div>
</div>
<button class="theme-toggle" id="themeToggle">🌙</button>
<script defer="" src="./AI绘画_files/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon="{"rayId":"8d0d53c80d4780f5","version":"2024.10.1","r":1,"token":"28706964845141f98aca7a1268fb8e99","serverTiming":{"name":{"cfExtPri":true,"cfL4":true,"cfSpeedBrain":true,"cfCacheStatus":true}}}" crossorigin="anonymous"></script>
</body></html>
这里给定了几个随机的提示词,点击“随机提示词”按钮即可填充,可以根据需要增添。
之后点击右上角的部署
按钮,并点击“保存并部署”,即可保存此Worker。
返回到worker页面,选择设置–变量,向下滑找到"AI绑定"一项。选择“添加绑定”,
选择“添加绑定”,名称写AI
即可。随后点击部署按钮。
绑定自己的域名
默认的workers.dev域名在国内无法使用,所以建议绑定一个自己的域名。 到这里,点击你的域名,就可以生成图片了,原作者提供了一些提示词的生成效果,你可以去原作者的博客查看狐狸汉克的小站
测试效果
flux-1-schnell模型
绘图大概在10s左右一张图,速度尚可。但效果较为一般,还是得大佬们自炼的模型。
版权声明
该文章中代码都来自于作者: 狐狸汉克文章链接: 使用Cloudflare Worker搭建自己的AI绘画工具
- 本文链接:https://blog.kafuchino.top/posts/2024-09-16
- 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 许可协议。