| 
					
				 | 
			
			
				@@ -0,0 +1,335 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Component, OnInit, ViewChild, ElementRef, Renderer2 } from '@angular/core'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Router } from '@angular/router'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { IonModal } from '@ionic/angular'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Storage } from '@ionic/storage-angular'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+interface Bubble { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  x: number; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  y: number; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  text: string; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  color: string; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  scale: number; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  burst: boolean; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  speedX: number; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  speedY: number; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  type: 'normal' | 'hard' | 'fragile'; // 气泡类型 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  health: number; // 生命值 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  hits: number; // 已击中次数 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Component({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  selector: 'app-emotion-vent', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  templateUrl: './emotion-vent.page.html', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  styleUrls: ['./emotion-vent.page.scss'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  standalone:false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+export class EmotionVentPage implements OnInit { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  @ViewChild('particlesContainer', { static: false }) particlesContainer!: ElementRef; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  @ViewChild('keywordModal') keywordModal!: IonModal; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  newKeyword = ''; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  customKeywords: string[] = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bubbles: Bubble[] = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  burstCount = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  hammerX = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  hammerY = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  hammerVisible = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  negativeWords = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    '加班', '分手', '压力', '焦虑', '失眠', '孤独', '失败',  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    '被拒', '拖延', '迷茫', '自卑', '愤怒', '委屈', '失望', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    '疲惫', '无助', '内卷', 'PUA', '崩溃', 'emo' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  colors = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    '#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    '#F06292', '#7986CB', '#9575CD', '#64B5F6', '#4DB6AC' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  animationFrameId: number=0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  constructor( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private router: Router, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private renderer: Renderer2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private storage: Storage 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  async ngOnInit() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    await this.storage.create(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.loadCustomKeywords(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.generateBubbles(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.startBubbleMovement(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 打开关键词模态框 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async openKeywordModal() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  await this.loadCustomKeywords(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.keywordModal.present(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 关闭模态框 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+dismissModal() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.keywordModal.dismiss(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 添加关键词 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async addKeyword() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (this.newKeyword.trim() && !this.customKeywords.includes(this.newKeyword.trim())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.customKeywords.push(this.newKeyword.trim()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    await this.storage.set('customKeywords', this.customKeywords); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.newKeyword = ''; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.resetBubbles(); // 重新生成气泡以包含新关键词 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 移除关键词 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async removeKeyword(keyword: string) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.customKeywords = this.customKeywords.filter(k => k !== keyword); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  await this.storage.set('customKeywords', this.customKeywords); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.resetBubbles(); // 重新生成气泡 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 加载保存的关键词 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async loadCustomKeywords() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const savedKeywords = await this.storage.get('customKeywords'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.customKeywords = savedKeywords || []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 修改generateBubbles方法 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+generateBubbles() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const bubbleCount = 15; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.bubbles = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.burstCount = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 合并默认关键词和自定义关键词 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const allKeywords = [...this.negativeWords, ...this.customKeywords]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (let i = 0; i < bubbleCount; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const typeRoll = Math.random(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const randomWord = allKeywords[Math.floor(Math.random() * allKeywords.length)]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let type: 'normal' | 'hard' | 'fragile', health: number; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (typeRoll < 0.2) { // 20%几率生成坚硬气泡 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      type = 'hard'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      health = 2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else if (typeRoll < 0.4) { // 20%几率生成脆弱气泡 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      type = 'fragile'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      health = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { // 60%普通气泡 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      type = 'normal'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      health = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //const randomWord = this.negativeWords[Math.floor(Math.random() * this.negativeWords.length)]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const randomColor = this.getColorForType(type); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.bubbles.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      x: Math.random() * (window.innerWidth - 100), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      y: Math.random() * (window.innerHeight - 100), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      text: randomWord, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      color: randomColor, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      scale: this.getScaleForType(type), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      burst: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      speedX: (Math.random() - 0.5) * 2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      speedY: (Math.random() - 0.5) * 2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      type: type, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      health: health, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      hits: 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 根据类型获取颜色 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+getColorForType(type: string): string { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  switch(type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case 'hard': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return '#FF6B6B'; // 红色系 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case 'fragile': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return '#4ECDC4'; // 青色系 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return this.colors[Math.floor(Math.random() * this.colors.length)]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 根据类型获取大小 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+getScaleForType(type: string): number { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  switch(type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case 'hard': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return 0.9 + Math.random() * 0.3; // 稍大 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case 'fragile': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return 0.7 + Math.random() * 0.2; // 稍小 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return 0.8 + Math.random() * 0.4; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  startBubbleMovement() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const moveBubbles = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.bubbles.forEach(bubble => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!bubble.burst) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          bubble.x += bubble.speedX; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          bubble.y += bubble.speedY; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          // 边界检测 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          if (bubble.x <= 0 || bubble.x >= window.innerWidth - 80) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bubble.speedX *= -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          if (bubble.y <= 0 || bubble.y >= window.innerHeight - 80) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bubble.speedY *= -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.animationFrameId = requestAnimationFrame(moveBubbles); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    moveBubbles(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  onTouchMove(event: TouchEvent) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (event.touches.length > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.hammerX = event.touches[0].clientX; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.hammerY = event.touches[0].clientY; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      // 检测碰撞 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.checkCollision(this.hammerX, this.hammerY); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  onTouchEnd() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 触摸结束时可以添加一些效果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  checkCollision(x: number, y: number) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.bubbles.forEach(bubble => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (!bubble.burst) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const distance = Math.sqrt( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          Math.pow(x - (bubble.x + 40), 2) +  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          Math.pow(y - (bubble.y + 40), 2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (distance < 40) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          this.burstBubble(bubble); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ burstBubble(bubble: Bubble) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 增加击中次数 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bubble.hits++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 如果未达到爆裂条件,只显示击中效果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bubble.hits < bubble.health) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.showHitEffect(bubble); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!bubble.burst) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bubble.burst = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.burstCount++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 根据不同类型播放不同音效 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.playBurstSound(bubble.type); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 所有气泡都爆裂后显示完成消息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (this.burstCount === this.bubbles.length) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      setTimeout(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        this.showCompletionMessage(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      }, 1000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 显示击中效果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+showHitEffect(bubble: Bubble) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 震动效果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const element = document.querySelector(`.negative-bubble[style*="left: ${bubble.x}px"][style*="top: ${bubble.y}px"]`); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (element) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    element.classList.add('shake'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    setTimeout(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      element.classList.remove('shake'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }, 500); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 显示裂纹效果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.showCrackEffect(bubble); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 播放击中音效 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.playHitSound(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 显示裂纹效果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+showCrackEffect(bubble: Bubble) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const crack = document.createElement('div'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crack.className = 'crack-effect'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crack.innerHTML = '💢'; // 可以使用Unicode字符或SVG 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const element = document.querySelector(`.negative-bubble[style*="left: ${bubble.x}px"][style*="top: ${bubble.y}px"]`); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (element) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    crack.style.position = 'absolute'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    crack.style.fontSize = '30px'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    crack.style.animation = 'fadeInOut 1s'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    element.appendChild(crack); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    setTimeout(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      crack.remove(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }, 1000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  playBurstSound(type: string) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 这里可以添加爆裂音效 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const sounds = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      'assets/sounds/pop1.mp3', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const sound = new Audio(sounds[Math.floor(Math.random() * sounds.length)]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sound.volume = 0.3; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sound.play().catch(e => console.log('Audio play error:', e)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  resetBubbles() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cancelAnimationFrame(this.animationFrameId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.generateBubbles(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.startBubbleMovement(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 添加点击处理方法 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+onBubbleClick(bubble: Bubble, event: MouseEvent) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  //this.createRipple(event, bubble); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  this.burstBubble(bubble); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// // 播放击中音效 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+playHitSound() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const sounds = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'assets/sounds/pop2.mp3' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const sound = new Audio(sounds[Math.floor(Math.random() * sounds.length)]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  sound.volume = 0.2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  sound.play().catch(e => console.log('Audio play error:', e)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+showCompletionMessage() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 创建更漂亮的完成消息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const message = document.createElement('div'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    message.innerHTML = ` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <div style=" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        position: fixed; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        top: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        left: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        transform: translate(-50%, -50%); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        background: rgba(0,0,0,0.8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        color: white; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        padding: 20px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        border-radius: 10px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        text-align: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        z-index: 100; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        animation: zoomIn 0.5s; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      "> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <h2>🎉 干得漂亮! 🎉</h2> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <p>你已经粉碎了所有负面情绪!</p> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <ion-button onclick="this.parentElement.remove()" fill="clear" style="color: white;"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          继续发泄 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        </ion-button> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    `; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    document.body.appendChild(message); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |