| |
| class TextToImageAPI { |
| constructor() { |
| this.apiEndpoint = 'https://api.openai.com/v1/images/generations'; |
| this.apiKey = null; |
| this.cache = new Map(); |
| this.isEnabled = false; |
| |
| |
| this.fallbackImages = this.initFallbackImages(); |
| |
| this.init(); |
| } |
| |
| init() { |
| |
| this.apiKey = localStorage.getItem('textToImageApiKey'); |
| this.isEnabled = !!this.apiKey; |
| |
| if (!this.isEnabled) { |
| console.log('文生图API未配置,将使用备用图片'); |
| } |
| } |
| |
| async generateFood(foodName) { |
| try { |
| |
| const cacheKey = `food_${foodName}`; |
| if (this.cache.has(cacheKey)) { |
| return this.cache.get(cacheKey); |
| } |
| |
| let imageData; |
| |
| if (this.isEnabled) { |
| |
| imageData = await this.callAPI(`cartoon style ${foodName} food, cute, colorful, simple background`); |
| } else { |
| |
| imageData = this.getFallbackFoodImage(foodName); |
| } |
| |
| |
| this.cache.set(cacheKey, imageData); |
| return imageData; |
| |
| } catch (error) { |
| console.error('生成食物图片失败:', error); |
| return this.getFallbackFoodImage(foodName); |
| } |
| } |
| |
| async generateAnimal(foodName) { |
| try { |
| const cacheKey = `animal_${foodName}`; |
| if (this.cache.has(cacheKey)) { |
| return this.cache.get(cacheKey); |
| } |
| |
| const animalType = this.getAnimalForFood(foodName); |
| let imageData; |
| |
| if (this.isEnabled) { |
| imageData = await this.callAPI(`cartoon style cute ${animalType} animal, friendly, colorful, simple background`); |
| } else { |
| imageData = this.getFallbackAnimalImage(animalType); |
| } |
| |
| this.cache.set(cacheKey, imageData); |
| return imageData; |
| |
| } catch (error) { |
| console.error('生成动物图片失败:', error); |
| return this.getFallbackAnimalImage(this.getAnimalForFood(foodName)); |
| } |
| } |
| |
| async callAPI(prompt) { |
| if (!this.isEnabled) { |
| throw new Error('API未启用'); |
| } |
| |
| const response = await fetch(this.apiEndpoint, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Authorization': `Bearer ${this.apiKey}` |
| }, |
| body: JSON.stringify({ |
| prompt: prompt, |
| n: 1, |
| size: '256x256', |
| style: 'cartoon' |
| }) |
| }); |
| |
| if (!response.ok) { |
| throw new Error(`API请求失败: ${response.status}`); |
| } |
| |
| const data = await response.json(); |
| |
| if (data.data && data.data.length > 0) { |
| |
| return await this.downloadImage(data.data[0].url); |
| } else { |
| throw new Error('API返回数据格式错误'); |
| } |
| } |
| |
| async downloadImage(url) { |
| const response = await fetch(url); |
| const blob = await response.blob(); |
| |
| return new Promise((resolve, reject) => { |
| const reader = new FileReader(); |
| reader.onload = () => resolve(reader.result); |
| reader.onerror = reject; |
| reader.readAsDataURL(blob); |
| }); |
| } |
| |
| getFallbackFoodImage(foodName) { |
| |
| const fallbackFood = this.fallbackImages.food[foodName] || this.fallbackImages.food.default; |
| return this.createSimpleImage(fallbackFood.emoji, fallbackFood.color); |
| } |
| |
| getFallbackAnimalImage(animalType) { |
| |
| const fallbackAnimal = this.fallbackImages.animals[animalType] || this.fallbackImages.animals.default; |
| return this.createSimpleImage(fallbackAnimal.emoji, fallbackAnimal.color); |
| } |
| |
| createSimpleImage(emoji, backgroundColor = '#FFFFFF') { |
| |
| const canvas = document.createElement('canvas'); |
| const ctx = canvas.getContext('2d'); |
| |
| canvas.width = 64; |
| canvas.height = 64; |
| |
| |
| ctx.fillStyle = backgroundColor; |
| ctx.fillRect(0, 0, 64, 64); |
| |
| |
| ctx.font = '40px Arial'; |
| ctx.textAlign = 'center'; |
| ctx.textBaseline = 'middle'; |
| ctx.fillText(emoji, 32, 32); |
| |
| return canvas.toDataURL(); |
| } |
| |
| initFallbackImages() { |
| return { |
| food: { |
| 'apple': { emoji: '🍎', color: '#FFE4E1' }, |
| 'banana': { emoji: '🍌', color: '#FFFACD' }, |
| 'orange': { emoji: '🍊', color: '#FFE4B5' }, |
| 'strawberry': { emoji: '🍓', color: '#FFB6C1' }, |
| 'watermelon': { emoji: '🍉', color: '#F0FFF0' }, |
| 'grape': { emoji: '🍇', color: '#E6E6FA' }, |
| 'pizza': { emoji: '🍕', color: '#FFEFD5' }, |
| 'burger': { emoji: '🍔', color: '#F5DEB3' }, |
| 'cake': { emoji: '🍰', color: '#FFF8DC' }, |
| 'cookie': { emoji: '🍪', color: '#DEB887' }, |
| 'bread': { emoji: '🍞', color: '#F5DEB3' }, |
| 'cheese': { emoji: '🧀', color: '#FFFACD' }, |
| 'fish': { emoji: '🐟', color: '#E0FFFF' }, |
| 'chicken': { emoji: '🍗', color: '#FFEFD5' }, |
| 'rice': { emoji: '🍚', color: '#F8F8FF' }, |
| 'noodles': { emoji: '🍜', color: '#FFF8DC' }, |
| 'default': { emoji: '🍽️', color: '#F0F8FF' } |
| }, |
| animals: { |
| 'rabbit': { emoji: '🐰', color: '#F5F5DC' }, |
| 'monkey': { emoji: '🐵', color: '#DEB887' }, |
| 'bird': { emoji: '🐦', color: '#E0F6FF' }, |
| 'bear': { emoji: '🐻', color: '#D2B48C' }, |
| 'elephant': { emoji: '🐘', color: '#DCDCDC' }, |
| 'fox': { emoji: '🦊', color: '#FFE4B5' }, |
| 'cat': { emoji: '🐱', color: '#FFB6C1' }, |
| 'dog': { emoji: '🐶', color: '#DEB887' }, |
| 'mouse': { emoji: '🐭', color: '#D3D3D3' }, |
| 'squirrel': { emoji: '🐿️', color: '#D2691E' }, |
| 'duck': { emoji: '🦆', color: '#FFFACD' }, |
| 'rat': { emoji: '🐀', color: '#A9A9A9' }, |
| 'panda': { emoji: '🐼', color: '#F0F8FF' }, |
| 'chicken': { emoji: '🐔', color: '#FFFACD' }, |
| 'default': { emoji: '🐱', color: '#FFB6C1' } |
| } |
| }; |
| } |
| |
| getAnimalForFood(foodName) { |
| const animalMap = { |
| 'apple': 'rabbit', |
| 'banana': 'monkey', |
| 'orange': 'bird', |
| 'strawberry': 'bear', |
| 'watermelon': 'elephant', |
| 'grape': 'fox', |
| 'pizza': 'cat', |
| 'burger': 'dog', |
| 'cake': 'mouse', |
| 'cookie': 'squirrel', |
| 'bread': 'duck', |
| 'cheese': 'rat', |
| 'fish': 'cat', |
| 'chicken': 'dog', |
| 'rice': 'bird', |
| 'noodles': 'panda', |
| 'carrot': 'rabbit', |
| 'corn': 'chicken' |
| }; |
| |
| return animalMap[foodName] || 'cat'; |
| } |
| |
| |
| setApiKey(apiKey) { |
| this.apiKey = apiKey; |
| this.isEnabled = !!apiKey; |
| |
| if (apiKey) { |
| localStorage.setItem('textToImageApiKey', apiKey); |
| } else { |
| localStorage.removeItem('textToImageApiKey'); |
| } |
| } |
| |
| |
| clearCache() { |
| this.cache.clear(); |
| } |
| |
| |
| getCacheSize() { |
| return this.cache.size; |
| } |
| |
| |
| async preloadCommonImages() { |
| const commonFoods = ['apple', 'banana', 'orange', 'strawberry', 'watermelon']; |
| const promises = []; |
| |
| for (const food of commonFoods) { |
| promises.push(this.generateFood(food)); |
| promises.push(this.generateAnimal(food)); |
| } |
| |
| try { |
| await Promise.all(promises); |
| console.log('常用图片预加载完成'); |
| } catch (error) { |
| console.error('预加载图片失败:', error); |
| } |
| } |
| |
| |
| createConfigUI() { |
| const configDiv = document.createElement('div'); |
| configDiv.id = 'textToImageConfig'; |
| configDiv.style.cssText = ` |
| position: fixed; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| background: white; |
| padding: 20px; |
| border-radius: 10px; |
| box-shadow: 0 4px 20px rgba(0,0,0,0.3); |
| z-index: 10000; |
| display: none; |
| `; |
| |
| configDiv.innerHTML = ` |
| <h3>文生图API配置</h3> |
| <p>输入您的API密钥以启用AI图片生成功能:</p> |
| <input type="text" id="apiKeyInput" placeholder="输入API密钥" style="width: 300px; padding: 8px; margin: 10px 0;"> |
| <br> |
| <button id="saveApiKey" style="padding: 8px 16px; margin-right: 10px;">保存</button> |
| <button id="cancelConfig" style="padding: 8px 16px;">取消</button> |
| <p style="font-size: 12px; color: #666; margin-top: 10px;"> |
| 如果不配置API密钥,将使用备用的表情符号图片 |
| </p> |
| `; |
| |
| document.body.appendChild(configDiv); |
| |
| |
| document.getElementById('saveApiKey').onclick = () => { |
| const apiKey = document.getElementById('apiKeyInput').value.trim(); |
| this.setApiKey(apiKey); |
| configDiv.style.display = 'none'; |
| |
| if (apiKey) { |
| alert('API密钥已保存,现在将使用AI生成图片'); |
| } else { |
| alert('API密钥已清除,将使用备用图片'); |
| } |
| }; |
| |
| document.getElementById('cancelConfig').onclick = () => { |
| configDiv.style.display = 'none'; |
| }; |
| |
| return configDiv; |
| } |
| |
| |
| showConfig() { |
| let configDiv = document.getElementById('textToImageConfig'); |
| if (!configDiv) { |
| configDiv = this.createConfigUI(); |
| } |
| |
| document.getElementById('apiKeyInput').value = this.apiKey || ''; |
| configDiv.style.display = 'block'; |
| } |
| |
| |
| async testAPI() { |
| if (!this.isEnabled) { |
| return { success: false, message: 'API未配置' }; |
| } |
| |
| try { |
| await this.callAPI('test image'); |
| return { success: true, message: 'API连接正常' }; |
| } catch (error) { |
| return { success: false, message: error.message }; |
| } |
| } |
| } |
|
|
| |
| document.addEventListener('keydown', (e) => { |
| if (e.ctrlKey && e.shiftKey && e.key === 'I') { |
| if (window.game && window.game.textToImage) { |
| window.game.textToImage.showConfig(); |
| } |
| } |
| }); |
|
|