+
+
![]()
+
+ @if(isInfoPanelHidden) {
+
+ } @else {
+
+ }
diff --git a/front/src/app/mid-res-image/mid-res-image.scss b/front/src/app/mid-res-image/mid-res-image.scss
index 32dc007..01cc393 100644
--- a/front/src/app/mid-res-image/mid-res-image.scss
+++ b/front/src/app/mid-res-image/mid-res-image.scss
@@ -4,27 +4,329 @@
position: fixed;
top: 0;
left: 0;
- width: 100vw;
- height: 100vh;
+ width: 100dvw;
+ height: 100dvh;
background-color: rgba(42, 41, 38, 0.95);
display: flex;
z-index: 1000;
+ flex-grow: 1;
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
+ // Entrada animada del contenedor
+ animation: fadeInContainer 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+
+ // Animación coordinada desde la posición de la imagen clickeada
+ &.coordinated-animation {
+ animation: fadeInContainerCoordinated 0.5s cubic-bezier(0.4, 0, 0.2, 1)
+ forwards;
+
+ .image-container {
+ animation: coordinatedImageTransition 0.7s
+ cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
+ }
+
+ .panel {
+ animation: slideInPanelDelayed 0.5s cubic-bezier(0.4, 0, 0.2, 1)
+ 0.4s both;
+ }
+
+ .show-button {
+ animation: fadeInButtonDelayed 0.4s cubic-bezier(0.4, 0, 0.2, 1)
+ 0.5s both;
+ }
+ }
+
+ // Animaciones de salida coordinada
+ &.coordinated-exit {
+ animation: fadeOutContainerCoordinated 0.6s cubic-bezier(0.4, 0, 0.2, 1)
+ forwards;
+
+ .image-container {
+ animation: coordinatedImageExitTransition 0.6s
+ cubic-bezier(0.7, 0, 0.84, 0) forwards;
+ }
+
+ .panel {
+ animation: slideOutPanel 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+ }
+
+ .show-button {
+ animation: fadeOutButton 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+ }
+ }
+
+ // Animación de salida simple (sin coordinación)
+ &.simple-exit {
+ animation: fadeOutContainerSimple 0.4s cubic-bezier(0.4, 0, 0.2, 1)
+ forwards;
+
+ .image-container {
+ animation: zoomOutImage 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+ }
+
+ .panel {
+ animation: slideOutPanel 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+ }
+
+ .show-button {
+ animation: fadeOutButton 0.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+ }
+ }
+
+ @keyframes fadeInContainer {
+ from {
+ opacity: 0;
+ -webkit-backdrop-filter: blur(0px);
+ backdrop-filter: blur(0px);
+ }
+ to {
+ opacity: 1;
+ -webkit-backdrop-filter: blur(5px);
+ backdrop-filter: blur(5px);
+ }
+ }
+
// Image section
- .img {
+ .image-container {
width: 70%;
- height: 100vh;
- object-fit: contain;
- cursor: zoom-in;
- transition: transform 0.3s ease;
+ height: 100dvh;
+ transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ // Animación de entrada de la imagen
+ animation: slideInImage 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+
+ img {
+ width: fit-content;
+ height: fit-content;
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: scale-down;
+
+ // Animación de zoom elegante
+ animation: zoomInImage 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)
+ forwards;
+ }
+ }
+
+ @keyframes slideInImage {
+ from {
+ transform: translateX(-30px);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+ }
+
+ @keyframes zoomInImage {
+ from {
+ transform: scale(0.8);
+ opacity: 0;
+ }
+ to {
+ transform: scale(1);
+ opacity: 1;
+ }
+ }
+
+ // When panel is hidden, image takes full width
+ &.panel-hidden {
+ .image-container {
+ width: 100%;
+ }
+
+ .panel {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ }
+
+ &.panel-shown {
+ .image-container {
+ width: 75%;
+ }
+ .panel {
+ width: 25%;
+ transform: translateX(0);
+ opacity: 1;
+ }
+ }
+
+ // Show panel button - positioned to always be visible
+ .show-button {
+ position: fixed;
+ top: 4rem;
+ right: 4rem;
+ z-index: 1001;
+ padding: 0;
+ margin: 0;
+
+ // Animación de entrada del botón con retraso
+ animation: fadeInButton 0.4s cubic-bezier(0.4, 0, 0.2, 1) 0.3s both;
+ }
+
+ @keyframes fadeInButton {
+ from {
+ transform: translateY(-20px) scale(0.8);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0) scale(1);
+ opacity: 1;
+ }
+ }
+
+ // Animaciones coordinadas para transición suave desde galería
+ @keyframes fadeInContainerCoordinated {
+ from {
+ opacity: 0;
+ -webkit-backdrop-filter: blur(0px);
+ backdrop-filter: blur(0px);
+ }
+ to {
+ opacity: 1;
+ -webkit-backdrop-filter: blur(5px);
+ backdrop-filter: blur(5px);
+ }
+ }
+
+ @keyframes coordinatedImageTransition {
+ from {
+ transform: translate(
+ calc(var(--start-x, 50vw) - 50vw),
+ calc(var(--start-y, 50vh) - 50vh)
+ )
+ scale(calc(var(--start-width, 200px) / 400));
+ opacity: 0.8;
+ }
+ 50% {
+ transform: translate(
+ calc((var(--start-x, 50vw) - 50vw) * 0.3),
+ calc((var(--start-y, 50vh) - 50vh) * 0.3)
+ )
+ scale(0.9);
+ opacity: 0.9;
+ }
+ to {
+ transform: translate(0, 0) scale(1);
+ opacity: 1;
+ }
+ }
+
+ @keyframes slideInPanelDelayed {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+ }
+
+ @keyframes fadeInButtonDelayed {
+ from {
+ transform: translateY(-20px) scale(0.8);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0) scale(1);
+ opacity: 1;
+ }
+ }
+
+ // Animaciones de salida coordinada
+ @keyframes fadeOutContainerCoordinated {
+ from {
+ opacity: 1;
+ -webkit-backdrop-filter: blur(5px);
+ backdrop-filter: blur(5px);
+ }
+ to {
+ opacity: 0;
+ -webkit-backdrop-filter: blur(0px);
+ backdrop-filter: blur(0px);
+ }
+ }
+
+ @keyframes coordinatedImageExitTransition {
+ from {
+ transform: translate(0, 0) scale(1);
+ opacity: 1;
+ }
+ 50% {
+ transform: translate(
+ calc((var(--end-x, 50vw) - 50vw) * 0.3),
+ calc((var(--end-y, 50vh) - 50vh) * 0.3)
+ )
+ scale(0.7);
+ opacity: 0.8;
+ }
+ to {
+ transform: translate(
+ calc(var(--end-x, 50vw) - 50vw),
+ calc(var(--end-y, 50vh) - 50vh)
+ )
+ scale(calc(var(--end-width, 200px) / 400));
+ opacity: 0;
+ }
+ }
+
+ @keyframes slideOutPanel {
+ from {
+ transform: translateX(0);
+ opacity: 1;
+ }
+ to {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ }
+
+ @keyframes fadeOutButton {
+ from {
+ transform: translateY(0) scale(1);
+ opacity: 1;
+ }
+ to {
+ transform: translateY(-20px) scale(0.8);
+ opacity: 0;
+ }
+ }
+
+ // Animaciones de salida simple
+ @keyframes fadeOutContainerSimple {
+ from {
+ opacity: 1;
+ -webkit-backdrop-filter: blur(5px);
+ backdrop-filter: blur(5px);
+ }
+ to {
+ opacity: 0;
+ -webkit-backdrop-filter: blur(0px);
+ backdrop-filter: blur(0px);
+ }
+ }
+
+ @keyframes zoomOutImage {
+ from {
+ transform: scale(1);
+ opacity: 1;
+ }
+ to {
+ transform: scale(0.8);
+ opacity: 0;
+ }
}
// Panel section
.panel {
- width: 30%;
- height: 100vh;
+ height: 100dvh;
background: variables.$primary-white;
overflow-y: auto;
display: flex;
@@ -32,6 +334,12 @@
box-shadow: -4px 0 20px rgba(42, 41, 38, 0.2);
border-left: 1px solid variables.$border-grey;
padding: 2%;
+ transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1),
+ opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
+ width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+
+ // Animación de entrada del panel
+ animation: slideInPanel 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.2s both;
.infoPanelMenu {
display: flex;
@@ -178,11 +486,22 @@
}
}
+ @keyframes slideInPanel {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+ }
+
// Responsive design
@media (max-width: 768px) {
flex-direction: column;
- .img {
+ .image-container {
width: 100%;
height: 60vh;
}
@@ -195,10 +514,17 @@
min-width: 100%;
}
}
+
+ // Adjust show button position for mobile
+ .show-button {
+ top: 1rem;
+ right: 1rem;
+ padding: 0.5rem;
+ }
}
@media (max-width: 480px) {
- .img {
+ .image-container {
height: 50vh;
}
diff --git a/front/src/app/mid-res-image/mid-res-image.ts b/front/src/app/mid-res-image/mid-res-image.ts
index d746601..4a51f98 100644
--- a/front/src/app/mid-res-image/mid-res-image.ts
+++ b/front/src/app/mid-res-image/mid-res-image.ts
@@ -1,14 +1,23 @@
-import { Component, EventEmitter, Input, Output } from '@angular/core';
-import { SvgLoader } from '../svg/svg';
+import {
+ Component,
+ EventEmitter,
+ Input,
+ Output,
+ OnInit,
+ OnDestroy,
+ HostListener,
+} from '@angular/core';
import { Button } from '../button/button';
+// view: src/app/mid-res-image/mid-res-image.html
+
@Component({
selector: 'mid-res-image',
imports: [Button],
templateUrl: './mid-res-image.html',
styleUrl: './mid-res-image.scss',
})
-export class MidResImage {
+export class MidResImage implements OnInit, OnDestroy {
@Input({ required: true }) src: string = '';
@Input({ required: true }) alt: string = '';
@Input() title: string = 'Title';
@@ -17,25 +26,126 @@ export class MidResImage {
@Output() closed = new EventEmitter
();
isInfoPanelHidden: boolean = false;
+ hasCoordinatedAnimation: boolean = false;
+ private originalPosition: any = null;
comments: string[] = [];
+ @HostListener('document:keydown', ['$event'])
+ handleKeyDown(event: KeyboardEvent): void {
+ if (event.key === 'Escape') {
+ this.closeFullscreenAnimated();
+ }
+ }
+
+ ngOnInit(): void {
+ // Bloquear el scroll del body cuando se abre la imagen en pantalla completa
+ document.body.style.overflow = 'hidden';
+
+ // Verificar si hay información de transición disponible
+ const transitionData = (document as any).__imageTransition;
+ if (transitionData && Date.now() - transitionData.timestamp < 200) {
+ this.hasCoordinatedAnimation = true;
+ this.setupCoordinatedAnimation(transitionData);
+ // Limpiar la información de transición
+ delete (document as any).__imageTransition;
+ }
+ }
+
+ private setupCoordinatedAnimation(transitionData: any): void {
+ // Guardar la posición original para la animación de salida
+ this.originalPosition = { ...transitionData };
+
+ // Aplicar CSS variables para la animación coordinada
+ const container = document.querySelector(
+ '.mid-res-image-container'
+ ) as HTMLElement;
+ if (container) {
+ container.style.setProperty('--start-x', `${transitionData.x}px`);
+ container.style.setProperty('--start-y', `${transitionData.y}px`);
+ container.style.setProperty(
+ '--start-width',
+ `${transitionData.width}px`
+ );
+ container.style.setProperty(
+ '--start-height',
+ `${transitionData.height}px`
+ );
+ container.classList.add('coordinated-animation');
+ }
+ }
+
+ ngOnDestroy(): void {
+ // Restaurar el scroll del body cuando se destruye el componente
+ document.body.style.overflow = 'auto';
+ }
+
hideInfoPanel(event: MouseEvent): void {
event.stopPropagation();
- this.isInfoPanelHidden = !this.isInfoPanelHidden;
+ this.isInfoPanelHidden = true;
+ // tienes que coger el div 'panel' y cambiar su display a none
+ // Esto es para que no se vea el panel de información cuando se cierra
+ // Ademas, tienes que mostrar un botón para mostrar de nuevo el panel
+ document
+ .querySelector('.panel')
+ ?.setAttribute('style', 'display: none;');
}
showInfoPanel(event: MouseEvent): void {
event.stopPropagation();
- this.isInfoPanelHidden = !this.isInfoPanelHidden;
+ this.isInfoPanelHidden = false;
}
closeFullscreen(event: MouseEvent): void {
event.stopPropagation();
+ this.closeFullscreenAnimated();
}
- openFullscreen(event: MouseEvent): void {
- event.stopPropagation();
+ private closeFullscreenAnimated(): void {
+ // Iniciar animación de salida
+ this.startExitAnimation();
+
+ // Emitir el evento de cerrado después de la animación
+ setTimeout(() => {
+ // Restaurar el scroll del body
+ document.body.style.overflow = 'auto';
+ this.closed.emit();
+ }, 600); // Duración de la animación de salida
+ }
+
+ private startExitAnimation(): void {
+ const container = document.querySelector(
+ '.mid-res-image-container'
+ ) as HTMLElement;
+ if (container) {
+ // Remover animaciones de entrada
+ container.classList.remove('coordinated-animation');
+
+ // Si tenemos información de la posición original, configurar para retorno coordinado
+ if (this.hasCoordinatedAnimation && this.originalPosition) {
+ // Volver a establecer las variables CSS para la animación de retorno
+ container.style.setProperty(
+ '--end-x',
+ `${this.originalPosition.x}px`
+ );
+ container.style.setProperty(
+ '--end-y',
+ `${this.originalPosition.y}px`
+ );
+ container.style.setProperty(
+ '--end-width',
+ `${this.originalPosition.width}px`
+ );
+ container.style.setProperty(
+ '--end-height',
+ `${this.originalPosition.height}px`
+ );
+ container.classList.add('coordinated-exit');
+ } else {
+ // Animación de salida simple si no hay coordinación
+ container.classList.add('simple-exit');
+ }
+ }
}
editImage(event: MouseEvent): void {
@@ -48,21 +158,16 @@ export class MidResImage {
downloadImage(event: MouseEvent): void {
event.stopPropagation();
+ // debe descargar la imagen en resolución media (720p)
}
shareImage(event: MouseEvent): void {
event.stopPropagation();
+ // tiene que abrir un modal para compartir la imagen en redes sociales
}
buyImage(event: MouseEvent): void {
event.stopPropagation();
- }
-
- toggleCommentForm(event: MouseEvent): void {
- event.stopPropagation();
- }
-
- close() {
- this.closed.emit();
+ // tiene que abrir un modal para comprar la imagen
}
}