https://aidraw.foxhank.top
前后端完全基于(白嫖)cloudflare worker&cf worker ai,零服务器成本,但是由于我代码基本靠chatgpt,基本没有前端开发经验,有大佬可以提供一些改进意见嘛,万分感谢
搭建记录:http://www.foxhank.cn/archives/25/
48 个赞
可以很不错(*๓´╰╯`๓)
1 个赞
很赞,学习了
3 个赞
可以生成尺寸大一些的图吗?当做商城封面使用
感谢大佬的分享
生成质量还不错,就像前面佬讲的设置选个尺寸比较多用途,备用以后用得上,谢谢分享
前排围观,认真学习中,收藏了
這個用戶介面不錯
1 个赞
用Claude重新糊了个UI
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>AI绘画喵</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="description" content="使用先进的AI技术,轻松创作独特的数字艺术作品。探索无限可能,释放你的创意潜能。">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="AI绘画">
<link rel="icon" href="https://xx.com" type="image/png"> #修改为你的图床里的照片,作为网站小图标
<link rel="apple-touch-icon" href="https://xx.com"> #同上一行代码备注,作为苹果设备添加到设备桌面的Web APP的图标
<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%);
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, input[type="text"] {
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%;
}
}
.dark-theme {
--background-color: #2c3e50;
--text-color: #ecf0f1;
--card-background: rgba(44, 62, 80, 0.9);
}
</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.unshift({ prompt, imageUrl, timestamp: new Date().toISOString() });
if (history.length > 10) history.pop();
localStorage.setItem('aiDrawingHistory', JSON.stringify(history));
renderHistory();
}
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) {
submitButton.disabled = true;
submitButton.textContent = '正在创作...';
document.querySelector('.loading-overlay').style.display = 'flex';
const model = document.getElementById('model').value;
try {
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => {
controller.abort();
}, 30000);
const response = await fetch(`${window.location.origin}`, {
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}`);
}
const blob = await response.blob();
const Image = await blobToBase64(blob);
console.log('Base64 Image:', Image);
document.getElementById('aiImage').src = `${Image}`;
updateHistory(prompt, Image);
downloadBtn.style.display = 'block';
} catch (error) {
if (error.name === 'AbortError') {
alert('服务器连接超时,请稍后重试。');
} else {
console.error('Error:', error);
alert('生成过程中发生错误,请重试。');
}
} 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());
});
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 = '🌞';
}
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 id="aiImage"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="
alt="AI生成的图片">
<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" 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="请输入你想要创作的画面描述...">
<div class="button-group">
<button type="button" class="submit-btn" id="submitButton">开始创作</button>
<button type="button" class="download-btn" id="downloadBtn" style="display: none;">下载图片</button>
<button type="button" class="history-btn" id="historyBtn">历史记录</button>
</div>
</div>
</div>
<div id="historyModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<h2>历史记录</h2>
<div id="historyList"></div>
<button id="clearHistoryBtn" class="clear-history-btn">清空历史记录</button>
</div>
</div>
<button id="themeToggle" class="theme-toggle">🌙</button>
</body>
</html>
可能有点Bug,大佬勿喷
6 个赞
感谢佬,部署了!
1 个赞
十分感谢各位大佬的建议 L站各个都是人才,说话又好听,我超喜欢这里的
这是我开发的第一个前端项目,一路边学边写,确实收获良多
我靠大佬太厉害了 这可比我写的美观太多了,我可以把您这个当作前端吗,会在脚注上写上您的名字和网站地址的
1 个赞
没问题的哈,不过我没有网站
1 个赞
那B站主页之类的呢()
必须挂个名啊,要不我超链接到l站主页()
直接定位到L站吧
hhh好的,万分感谢!
感谢佬的分享
厉害呀,最近刚好有文生图的需求,这个的后端用的cd worker ai有限制次数的嘛
每天10万次的限制,昨天发出帖子之后用掉了一千多次,个人使用够够的
主要还是官方的这个模型效果只能说能用,不是特别理想,没有一些商用模型生成的好
商用模型mj还是好很多,之前体验了google的imagefx,但是限制太多了,感觉这个生成的也还行,提示词这些大佬是怎么解决的呢,我之前的提示词都是按照mj去gpt生成的