I am looking to achieve a specific behavior with an image, where it fits the viewport while maintaining its aspect ratio and allowing for zooming similar to how Chrome or Firefox handle local images. Here are the details of my project:
- The image I have is a large Unsplash image with a ratio of 4896x3264.
- My goal is to create a "Where Is Waldo" type game where users can zoom in to examine every detail of the image. Initially, I want the image to fit the screen (with the height at 100% maximum). Users should be able to zoom in and out using ctrl + scroll or other shortcuts.
- This image is displayed in an Angular 9 application within a component's HTML like this:
And I access this HTML through<img src="../../assets/img/Background.webp"/>
<router-outlet>
.
Currently, when navigating to this page, the image displays in its original size of 4896x3264, which is too tall for a regular screen. Using CSS, I can set the height and width to fit the viewport and center it as follows:
img {
max-height: 100%;
display: block;
}
However, with this approach, I lose the ability to zoom in on the image to see the details. Even when zoomed to 500%, the image remains fitted on the screen at 1536 x 722.
Opening a local image in browsers like Chrome or Firefox results in the exact behavior I desire: By default (100% zoom), the image fits at 1017x678. Users can zoom to 4896x3264 by clicking on it and gradually zoom in/out using ctrl + scroll. Inspecting the console, I notice that the width and height automatically adjust when zooming in and out:
<img style="-webkit-user-select: none;margin: auto;cursor: zoom-in;"src="file:///D:/Videotec/Background.webp" width="1083" height="722">
Is there a way to replicate this behavior in my application? While I found several topics on fitting images with CSS to the viewport, none seem to address my specific project requirements. If something similar has been discussed before, please let me know as English is not my native language and I may have missed it during my searches.
Screenshot of current behavior: https://ibb.co/DLChrtp
Desired default behavior screenshot: https://ibb.co/p0hLDxn
Thank you in advance for your assistance.
EDIT: I realize my description of the desired behavior may not be comprehensive due to limitations in my vocabulary. Refer to @NVRM's answer for more detailed insights.
EDIT Workaround: Since I couldn't find a pre-built solution, I implemented the desired behavior using Typescript and CSS. My approach involved toggling between classes (one with max-height: 100% and another without any width or height restrictions) based on user actions. Additionally, I had to retrieve click coordinates for centering the view. To ensure the scrolling functionality worked correctly, I leveraged the AfterViewChecked lifecycle hook. I also used a boolean flag for tracking scroll events to prevent unintentional triggers. While I acknowledge this may not be the most elegant solution, it perfectly aligns with the desired behavior for my project.
In .html
<div #videotecContainer class="videotec-container">
<img
#videotecImage
src="../../assets/img/Videotec.webp"
[ngClass]="zoomed ? 'zoomedIn' : 'zoomedOut'"
(click)="onClick($event)"
/>
</div>
In .ts:
zoomed: boolean = false;
scrolled: boolean = false;
xPosition: number;
yPosition: number;
@ViewChild("videotecImage", { static: true }) videotecImage: ElementRef;
@ViewChild("videotecContainer", { static: true }) videotecContainer: ElementRef;
onClick(event: any) {
const coordinates = this.getPixelPositions(event);
this.xPosition = coordinates.px - coordinates.clientWidth / 2;
this.yPosition = coordinates.py - coordinates.clientHeight / 2;
this.zoomImage();
}
zoomImage() {
if (this.zoomed && this.scrolled) this.scrolled = false;
this.zoomed = !this.zoomed;
}
ngAfterViewChecked() {
if (this.zoomed && !this.scrolled) {
this.videotecContainer.nativeElement.scrollTo(
this.xPosition,
this.yPosition
);
this.scrolled = true;
}
getPixelPositions(event) {
// Calculate position based on Wolfgang Fahl's answer here:
//questions/34867066
}
In .css:
.zoomedOut {
max-height: 100%;
max-width: 100%;
display: block;
margin: auto;
}
.zoomedIn {
display: block;
}
#videotecImage {
overflow: scroll;
}
.videotec-container {
height: 100vh;
overflow: scroll;
background-color: black;
}