1import { 2 AfterViewInit, 3 Component, 4 ElementRef, 5 Input, 6 OnChanges, 7 SimpleChanges, 8 ViewChild, 9} from '@angular/core'; 10import { MotionGolden } from '../model/golden'; 11import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; 12import { NgIf } from '@angular/common'; 13import { PreviewService } from '../service/preview.service'; 14import { FormsModule } from '@angular/forms'; 15import { MatIconModule } from '@angular/material/icon'; 16import { MatButtonModule } from '@angular/material/button'; 17 18@Component({ 19 selector: 'app-preview', 20 imports: [ 21 NgIf, 22 FormsModule, 23 MatIconModule, 24 MatButtonModule 25 ], 26 templateUrl: './preview.component.html', 27 styleUrl: './preview.component.css', 28}) 29export class PreviewComponent implements OnChanges, AfterViewInit { 30 constructor( 31 private sanitizer: DomSanitizer, 32 private previewService: PreviewService 33 ) { 34 this.previewService.frameCount$.subscribe( 35 (frames) => (this.frames = frames) 36 ); 37 } 38 39 @Input() selectedGolden: MotionGolden | null = null; 40 videoUrl: SafeResourceUrl | null = null; 41 frames: Array<string | number> | null = null; 42 currentFrame: number | null = null; 43 animationFrameId: number | null = null; 44 playbackSpeed = 0.25; 45 isPlaying: boolean = false 46 47 @ViewChild('videoPlayer') 48 videoPlayer!: ElementRef<HTMLVideoElement>; 49 50 ngAfterViewInit(): void { 51 if (this.videoPlayer && this.videoPlayer.nativeElement) { 52 this.videoPlayer.nativeElement.addEventListener('play', () => { 53 this.updateFrame(); 54 this.isPlaying = true; 55 }); 56 57 this.videoPlayer.nativeElement.addEventListener('pause', () => { 58 if (this.animationFrameId) { 59 cancelAnimationFrame(this.animationFrameId); 60 this.animationFrameId = null; 61 this.isPlaying = false; 62 } 63 }); 64 } 65 } 66 67 ngOnChanges(changes: SimpleChanges): void { 68 if (changes['selectedGolden']) { 69 const currentGolden: MotionGolden | null = 70 changes['selectedGolden'].currentValue; 71 72 if (currentGolden?.videoUrl) { 73 this.videoUrl = this.sanitizer.bypassSecurityTrustResourceUrl( 74 currentGolden.videoUrl 75 ); 76 this.ngAfterViewInit() 77 } else { 78 this.videoUrl = null; 79 } 80 } 81 } 82 83 updateFrame() { 84 this.calculateCurrentFrame(); 85 this.animationFrameId = requestAnimationFrame(() => this.updateFrame()); 86 } 87 88 calculateCurrentFrame() { 89 if (this.videoPlayer && this.videoPlayer.nativeElement && this.frames) { 90 const currentTime = this.videoPlayer.nativeElement.currentTime; 91 const totalDuration = this.videoPlayer.nativeElement.duration; 92 const frameCount = this.frames.length - 2; 93 94 if (currentTime && totalDuration) { 95 this.currentFrame = Math.round( 96 (currentTime / totalDuration) * frameCount 97 ); 98 const nextFrame = this.frames[this.currentFrame]; 99 if ( 100 this.currentFrame < this.frames.length - 1 && 101 typeof nextFrame === 'number' 102 ) { 103 this.previewService.setCurrentFrameFromView(nextFrame); 104 } else { 105 this.previewService.setCurrentFrameFromView(0); 106 } 107 } else { 108 this.currentFrame = null; 109 this.previewService.setCurrentFrameFromView(0); 110 } 111 } else { 112 this.currentFrame = null; 113 this.previewService.setCurrentFrameFromView(0); 114 } 115 } 116 117 goToStart() { 118 if (this.videoPlayer && this.videoPlayer.nativeElement) { 119 this.videoPlayer.nativeElement.currentTime = 0; 120 this.previewService.setCurrentFrameFromView(0) 121 } 122 } 123 124 togglePlayPause() { 125 if (this.videoPlayer && this.videoPlayer.nativeElement) { 126 if (this.videoPlayer.nativeElement.paused) { 127 this.isPlaying = true 128 this.videoPlayer.nativeElement.playbackRate = this.playbackSpeed; 129 this.videoPlayer.nativeElement.play(); 130 this.updateFrame(); 131 } else { 132 this.videoPlayer.nativeElement.pause(); 133 if (this.animationFrameId) { 134 this.isPlaying = false 135 cancelAnimationFrame(this.animationFrameId); 136 this.animationFrameId = null; 137 } 138 } 139 } 140 } 141 142 stepBackward() { 143 if (this.videoPlayer && this.videoPlayer.nativeElement && this.frames && this.currentFrame !== null) { 144 if(this.currentFrame > 0) { 145 const frameCount = this.frames.length - 2; 146 const totalDuration = this.videoPlayer.nativeElement.duration; 147 const targetTime = (this.currentFrame - 1) / frameCount * totalDuration 148 this.videoPlayer.nativeElement.currentTime = targetTime; 149 } 150 } 151 } 152 153 setPlaybackSpeed() { 154 if (this.videoPlayer && this.videoPlayer.nativeElement) { 155 this.videoPlayer.nativeElement.playbackRate = this.playbackSpeed; 156 } 157 } 158 159 stepForward() { 160 if (this.videoPlayer && this.videoPlayer.nativeElement && this.frames && this.currentFrame !== null) { 161 const frameCount = this.frames.length - 2; 162 const totalDuration = this.videoPlayer.nativeElement.duration; 163 if(this.currentFrame < frameCount) { 164 const targetTime = (this.currentFrame + 1) / frameCount * totalDuration 165 this.videoPlayer.nativeElement.currentTime = targetTime; 166 } 167 168 } 169 } 170 171 goToEnd() { 172 if (this.videoPlayer && this.videoPlayer.nativeElement) { 173 this.videoPlayer.nativeElement.currentTime = this.videoPlayer.nativeElement.duration; 174 this.updateFrame() 175 } 176 } 177} 178