import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output, QueryList, ViewChild, ViewChildren, } from "@angular/core"; import { Subject, Subscription, debounceTime, fromEvent, map, takeUntil } from "rxjs"; @Component({ selector: "app-scale-image", templateUrl: "./scale-image.component.html", styleUrls: ["./scale-image.component.less"], standalone: true, }) export class ScaleImageComponent implements AfterViewInit, OnDestroy { @Input({ required: true }) imageSrc: string = ""; @ViewChild("container", { static: true }) containerRef: ElementRef | null = null; @ViewChild("image", { static: true }) imageRef: ElementRef | null = null; @ViewChildren("image") imageList: QueryList> = new QueryList< ElementRef >(); @Output() view = new EventEmitter(); @Output() clickLeft = new EventEmitter(); @Output() clickRight = new EventEmitter(); @Input() host: ElementRef | null = null; @Input() hostScrollable: boolean = false; private destroy$ = new Subject(); private resizeSubscription: Subscription = new Subscription(); private observer!: IntersectionObserver; ngAfterViewInit(): void { this.setupResizeListener(); if (this.host) { this.observer = new IntersectionObserver( ([entry]) => entry.isIntersecting && this.view.emit(), { root: this.hostScrollable ? this.host.nativeElement : null, rootMargin: "100px", }, ); if (this.containerRef) this.observer.observe(this.containerRef.nativeElement); } if (!this.host) { this.imageList.changes .pipe>>(takeUntil(this.destroy$)) .subscribe((imageList) => { fromEvent(imageList.first.nativeElement, "click") .pipe( takeUntil(this.destroy$), map((e) => e as MouseEvent), ) .subscribe((e) => { const target = e.target as HTMLImageElement; const imageWidth = target.clientWidth; const clickX = e.offsetX; if (clickX < imageWidth / 2) this.clickLeft.emit(); else this.clickRight.emit(); }); }); } // fromEvent(this.imageRef.first.nativeElement, "click") // .pipe(takeUntil(this.destroy$)) // .subscribe((e) => { // console.log(e); // }); } ngOnDestroy(): void { if (this.resizeSubscription) this.resizeSubscription.unsubscribe(); if (this.observer) this.observer.disconnect(); this.destroy$.next(); this.destroy$.complete(); } onImageLoad() { this.scaleImage(); } private setupResizeListener() { this.resizeSubscription = fromEvent(window, "resize") .pipe(debounceTime(200)) .subscribe(() => this.scaleImage()); } private scaleImage() { if (this.containerRef && this.imageRef) { const container = this.containerRef.nativeElement; const img = this.imageRef.nativeElement; const containerWidth = container.clientWidth; const containerHeight = container.clientHeight; const imgRatio = img.naturalWidth / img.naturalHeight; const containerRatio = containerWidth / containerHeight; let newWidth, newHeight; if (imgRatio > containerRatio) { newWidth = containerWidth; newHeight = containerWidth / imgRatio; } else { newHeight = containerHeight; newWidth = containerHeight * imgRatio; } newWidth = Math.min(newWidth, img.naturalWidth); newHeight = Math.min(newHeight, img.naturalHeight); if (img.naturalHeight / img.naturalWidth >= 5) { img.style.width = "100%"; img.style.height = "auto"; } else { img.style.width = `${newWidth}px`; img.style.height = `${newHeight}px`; } } } }