|
@@ -13,8 +13,15 @@ export interface UploadedFile {
|
|
|
preview?: string;
|
|
|
}
|
|
|
|
|
|
+export interface ColorInfo {
|
|
|
+ hex: string;
|
|
|
+ rgb: { r: number; g: number; b: number };
|
|
|
+ percentage: number;
|
|
|
+ name?: string; // 颜色名称,如"深蓝色"、"暖白色"等
|
|
|
+}
|
|
|
+
|
|
|
export interface ColorAnalysisResult {
|
|
|
- colors: Array<{ hex: string; rgb: { r: number; g: number; b: number }; percentage: number }>;
|
|
|
+ colors: ColorInfo[];
|
|
|
reportUrl?: string;
|
|
|
mosaicUrl?: string;
|
|
|
}
|
|
@@ -50,6 +57,7 @@ export class UploadSuccessModalComponent implements OnInit, OnDestroy {
|
|
|
// 动画状态
|
|
|
animationState = 'idle';
|
|
|
buttonHoverState = 'normal';
|
|
|
+ copySuccess = false; // 复制成功状态
|
|
|
|
|
|
private progressSubscription?: Subscription;
|
|
|
private resizeSubscription?: Subscription;
|
|
@@ -180,6 +188,280 @@ export class UploadSuccessModalComponent implements OnInit, OnDestroy {
|
|
|
return this.uploadedFiles.some(file => file.type?.startsWith('image/'));
|
|
|
}
|
|
|
|
|
|
+ // 生成颜色描述文字
|
|
|
+ generateColorDescription(): string {
|
|
|
+ if (!this.analysisResult || !this.analysisResult.colors.length) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ const colorDescriptions = this.analysisResult.colors.map(color => {
|
|
|
+ const colorName = color.name || this.getColorName(color.hex);
|
|
|
+ return `${colorName}(${color.hex}) ${color.percentage}%`;
|
|
|
+ });
|
|
|
+
|
|
|
+ return `主要色彩:${colorDescriptions.join('、')}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据色值获取颜色名称
|
|
|
+ private getColorName(hex: string): string {
|
|
|
+ const colorMap: { [key: string]: string } = {
|
|
|
+ '#FFFFFF': '纯白色',
|
|
|
+ '#F5F5F5': '白烟',
|
|
|
+ '#E5E5E5': '浅灰色',
|
|
|
+ '#CCCCCC': '中灰色',
|
|
|
+ '#999999': '深灰色',
|
|
|
+ '#666666': '暗灰色',
|
|
|
+ '#333333': '深暗灰',
|
|
|
+ '#000000': '纯黑色',
|
|
|
+ '#FF0000': '红色',
|
|
|
+ '#00FF00': '酸橙色',
|
|
|
+ '#0000FF': '蓝色',
|
|
|
+ '#FFFF00': '黄色',
|
|
|
+ '#FF00FF': '品红色',
|
|
|
+ '#00FFFF': '青色',
|
|
|
+ '#FFA500': '橙色',
|
|
|
+ '#800080': '紫色',
|
|
|
+ '#008000': '绿色',
|
|
|
+ '#000080': '海军蓝',
|
|
|
+ '#800000': '栗色',
|
|
|
+ '#808000': '橄榄色',
|
|
|
+ '#008080': '水鸭色',
|
|
|
+ '#C0C0C0': '银色',
|
|
|
+ '#808080': '灰色',
|
|
|
+ '#FFE4E1': '雾玫瑰',
|
|
|
+ '#F0F8FF': '爱丽丝蓝',
|
|
|
+ '#FAEBD7': '古董白',
|
|
|
+ '#F5F5DC': '米色',
|
|
|
+ '#DEB887': '硬木色',
|
|
|
+ '#A52A2A': '棕色',
|
|
|
+ '#D2691E': '巧克力色',
|
|
|
+ '#FF7F50': '珊瑚色',
|
|
|
+ '#6495ED': '矢车菊蓝',
|
|
|
+ '#DC143C': '深红色',
|
|
|
+ '#00008B': '深蓝色',
|
|
|
+ '#B8860B': '深金色',
|
|
|
+ '#A9A9A9': '深灰色',
|
|
|
+ '#006400': '深绿色',
|
|
|
+ '#BDB76B': '深卡其色',
|
|
|
+ '#8B008B': '深品红',
|
|
|
+ '#556B2F': '深橄榄绿',
|
|
|
+ '#FF8C00': '深橙色',
|
|
|
+ '#9932CC': '深兰花紫',
|
|
|
+ '#8B0000': '深红色2',
|
|
|
+ '#E9967A': '深鲑鱼色',
|
|
|
+ '#8FBC8F': '深海绿',
|
|
|
+ '#483D8B': '深石板蓝',
|
|
|
+ '#2F4F4F': '深石板灰',
|
|
|
+ '#00CED1': '深绿松石',
|
|
|
+ '#9400D3': '深紫罗兰',
|
|
|
+ '#FF1493': '深粉红',
|
|
|
+ '#00BFFF': '深天蓝',
|
|
|
+ '#696969': '暗灰色2',
|
|
|
+ '#1E90FF': '道奇蓝',
|
|
|
+ '#B22222': '火砖色',
|
|
|
+ '#FFFAF0': '花白色',
|
|
|
+ '#228B22': '森林绿',
|
|
|
+ '#DCDCDC': '淡灰色',
|
|
|
+ '#F8F8FF': '幽灵白',
|
|
|
+ '#FFD700': '金色',
|
|
|
+ '#DAA520': '金麒麟色',
|
|
|
+ '#ADFF2F': '绿黄色',
|
|
|
+ '#F0FFF0': '蜜瓜色',
|
|
|
+ '#FF69B4': '热粉红',
|
|
|
+ '#CD5C5C': '印度红',
|
|
|
+ '#4B0082': '靛青色',
|
|
|
+ '#FFFFF0': '象牙色',
|
|
|
+ '#F0E68C': '卡其色',
|
|
|
+ '#E6E6FA': '薰衣草色',
|
|
|
+ '#FFF0F5': '薰衣草红',
|
|
|
+ '#7CFC00': '草坪绿',
|
|
|
+ '#FFFACD': '柠檬绸',
|
|
|
+ '#ADD8E6': '浅蓝色',
|
|
|
+ '#F08080': '浅珊瑚色',
|
|
|
+ '#E0FFFF': '浅青色',
|
|
|
+ '#FAFAD2': '浅金菊黄',
|
|
|
+ '#D3D3D3': '浅灰色2',
|
|
|
+ '#90EE90': '浅绿色',
|
|
|
+ '#FFB6C1': '浅粉红',
|
|
|
+ '#FFA07A': '浅鲑鱼色',
|
|
|
+ '#20B2AA': '浅海绿',
|
|
|
+ '#87CEFA': '浅天蓝',
|
|
|
+ '#778899': '浅石板灰',
|
|
|
+ '#B0C4DE': '浅钢蓝',
|
|
|
+ '#FFFFE0': '浅黄色',
|
|
|
+ '#32CD32': '酸橙绿',
|
|
|
+ '#FAF0E6': '亚麻色',
|
|
|
+ '#66CDAA': '中海绿',
|
|
|
+ '#0000CD': '中蓝色',
|
|
|
+ '#BA55D3': '中兰花紫',
|
|
|
+ '#9370DB': '中紫色',
|
|
|
+ '#3CB371': '中海春绿',
|
|
|
+ '#7B68EE': '中石板蓝',
|
|
|
+ '#00FA9A': '中春绿',
|
|
|
+ '#48D1CC': '中绿松石',
|
|
|
+ '#C71585': '中紫罗兰红',
|
|
|
+ '#191970': '午夜蓝',
|
|
|
+ '#F5FFFA': '薄荷奶油',
|
|
|
+ '#FFDEAD': '那瓦霍白',
|
|
|
+ '#FDF5E6': '老花边',
|
|
|
+ '#6B8E23': '橄榄褐色',
|
|
|
+ '#FF4500': '橙红色',
|
|
|
+ '#DA70D6': '兰花紫',
|
|
|
+ '#EEE8AA': '灰秋麒麟',
|
|
|
+ '#98FB98': '灰绿色',
|
|
|
+ '#AFEEEE': '灰绿松石',
|
|
|
+ '#DB7093': '灰紫罗兰红',
|
|
|
+ '#FFEFD5': '番木瓜鞭',
|
|
|
+ '#FFDAB9': '桃扑',
|
|
|
+ '#CD853F': '秘鲁色',
|
|
|
+ '#FFC0CB': '粉红色',
|
|
|
+ '#DDA0DD': '洋李色',
|
|
|
+ '#B0E0E6': '粉蓝色',
|
|
|
+ '#BC8F8F': '玫瑰棕色',
|
|
|
+ '#4169E1': '皇家蓝',
|
|
|
+ '#8B4513': '马鞍棕色',
|
|
|
+ '#FA8072': '鲑鱼色',
|
|
|
+ '#F4A460': '沙棕色',
|
|
|
+ '#2E8B57': '海绿色',
|
|
|
+ '#FFF5EE': '海贝色',
|
|
|
+ '#A0522D': '赭色',
|
|
|
+ '#87CEEB': '天蓝色',
|
|
|
+ '#6A5ACD': '石板蓝',
|
|
|
+ '#708090': '石板灰',
|
|
|
+ '#FFFAFA': '雪色',
|
|
|
+ '#00FF7F': '春绿色',
|
|
|
+ '#4682B4': '钢蓝色',
|
|
|
+ '#D2B48C': '棕褐色',
|
|
|
+ '#D8BFD8': '蓟色',
|
|
|
+ '#FF6347': '番茄色',
|
|
|
+ '#40E0D0': '绿松石',
|
|
|
+ '#EE82EE': '紫罗兰',
|
|
|
+ '#F5DEB3': '小麦色',
|
|
|
+ '#9ACD32': '黄绿色'
|
|
|
+ };
|
|
|
+
|
|
|
+ // 如果找到精确匹配,返回对应名称
|
|
|
+ if (colorMap[hex.toUpperCase()]) {
|
|
|
+ return colorMap[hex.toUpperCase()];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 否则根据RGB值判断颜色类型
|
|
|
+ const rgb = this.hexToRgb(hex);
|
|
|
+ if (!rgb) return '未知颜色';
|
|
|
+
|
|
|
+ const { r, g, b } = rgb;
|
|
|
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
|
+
|
|
|
+ // 判断是否为灰色系
|
|
|
+ const isGray = Math.abs(r - g) < 30 && Math.abs(g - b) < 30 && Math.abs(r - b) < 30;
|
|
|
+ if (isGray) {
|
|
|
+ if (brightness > 240) return '浅灰白';
|
|
|
+ if (brightness > 200) return '浅灰色';
|
|
|
+ if (brightness > 160) return '中灰色';
|
|
|
+ if (brightness > 120) return '深灰色';
|
|
|
+ if (brightness > 80) return '暗灰色';
|
|
|
+ return '深暗灰';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断主要颜色倾向
|
|
|
+ const max = Math.max(r, g, b);
|
|
|
+ const min = Math.min(r, g, b);
|
|
|
+ const saturation = max === 0 ? 0 : (max - min) / max;
|
|
|
+
|
|
|
+ if (saturation < 0.2) {
|
|
|
+ // 低饱和度,偏向灰色
|
|
|
+ if (brightness > 200) return '浅灰色';
|
|
|
+ if (brightness > 100) return '中灰色';
|
|
|
+ return '深灰色';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 高饱和度,判断色相
|
|
|
+ let colorName = '';
|
|
|
+ if (r >= g && r >= b) {
|
|
|
+ if (g > b) {
|
|
|
+ colorName = brightness > 150 ? '浅橙色' : '橙色';
|
|
|
+ } else {
|
|
|
+ colorName = brightness > 150 ? '浅红色' : '红色';
|
|
|
+ }
|
|
|
+ } else if (g >= r && g >= b) {
|
|
|
+ if (r > b) {
|
|
|
+ colorName = brightness > 150 ? '浅黄绿' : '黄绿色';
|
|
|
+ } else {
|
|
|
+ colorName = brightness > 150 ? '浅绿色' : '绿色';
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (r > g) {
|
|
|
+ colorName = brightness > 150 ? '浅紫色' : '紫色';
|
|
|
+ } else {
|
|
|
+ colorName = brightness > 150 ? '浅蓝色' : '蓝色';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return colorName;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将十六进制颜色转换为RGB
|
|
|
+ private hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
|
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
|
+ return result ? {
|
|
|
+ r: parseInt(result[1], 16),
|
|
|
+ g: parseInt(result[2], 16),
|
|
|
+ b: parseInt(result[3], 16)
|
|
|
+ } : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 复制颜色描述到剪贴板
|
|
|
+ async copyColorDescription(): Promise<void> {
|
|
|
+ const description = this.generateColorDescription();
|
|
|
+ if (!description) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await navigator.clipboard.writeText(description);
|
|
|
+ this.copySuccess = true;
|
|
|
+ console.log('颜色描述已复制到剪贴板');
|
|
|
+
|
|
|
+ // 2秒后重置复制状态
|
|
|
+ setTimeout(() => {
|
|
|
+ this.copySuccess = false;
|
|
|
+ }, 2000);
|
|
|
+ } catch (err) {
|
|
|
+ console.error('复制失败:', err);
|
|
|
+ // 降级方案:使用传统方法
|
|
|
+ this.fallbackCopyTextToClipboard(description);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 降级复制方案
|
|
|
+ private fallbackCopyTextToClipboard(text: string): void {
|
|
|
+ const textArea = document.createElement('textarea');
|
|
|
+ textArea.value = text;
|
|
|
+ textArea.style.top = '0';
|
|
|
+ textArea.style.left = '0';
|
|
|
+ textArea.style.position = 'fixed';
|
|
|
+ textArea.style.opacity = '0';
|
|
|
+
|
|
|
+ document.body.appendChild(textArea);
|
|
|
+ textArea.focus();
|
|
|
+ textArea.select();
|
|
|
+
|
|
|
+ try {
|
|
|
+ const successful = document.execCommand('copy');
|
|
|
+ if (successful) {
|
|
|
+ this.copySuccess = true;
|
|
|
+ console.log('颜色描述已复制到剪贴板(降级方案)');
|
|
|
+
|
|
|
+ // 2秒后重置复制状态
|
|
|
+ setTimeout(() => {
|
|
|
+ this.copySuccess = false;
|
|
|
+ }, 2000);
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error('降级复制方案也失败了:', err);
|
|
|
+ }
|
|
|
+
|
|
|
+ document.body.removeChild(textArea);
|
|
|
+ }
|
|
|
+
|
|
|
private checkScreenSize() {
|
|
|
const width = window.innerWidth;
|
|
|
this.isMobile = width < 768;
|