const {
fromEvent
} = rxjs;
const images = document.querySelectorAll('article img');
const detailModal = document.querySelector('#detail-modal');
const detailBgModal = document.querySelector('.bg');
let canShowModal = true;
detailBgModal.addEventListener("transitionend", () => {
if (detailBgModal.style.opacity === '0') {
const showImage = document.querySelector('[fullscreen=true]')
showImage.style.zIndex = 0;
detailBgModal.style.bottom = 'auto';
showImage.removeAttribute('fullscreen')
canShowModal = true;
}
});
const checkIsImagePortrait = (src) => {
return new Promise((resolve) => {
const img = new Image();
img.src = src;
img.onload = () => {
let isImagePortrait;
const ratio = img.naturalWidth / img.naturalHeight;
const pratio = window.innerWidth / window.innerHeight;
console.log('pratio', pratio)
if (ratio < pratio) {
isImagePortrait = true;
} else {
isImagePortrait = false
}
resolve(isImagePortrait);
};
});
};
const showModal = (imageElement) => {
const src = imageElement.getAttribute('src');
const modalImage = document.querySelector('#detail-modal img');
return checkIsImagePortrait(src).then(isPortrait => {
const src = imageElement.getAttribute('src');
if (isPortrait) {
modalImage.style.height = '100%';
modalImage.style.width = 'auto';
} else {
modalImage.style.height = 'auto';
modalImage.style.width = '100%';
}
detailModal.style.top = `${window.scrollY}px`;
detailModal.style.height = `${window.innerHeight}px`;
detailModal.style.display = 'flex';
detailBgModal.style.bottom = '0';
detailBgModal.style.opacity = 1;
document.querySelector('#detail-modal img').setAttribute('src', src);
});
};
const hideModal = () => {
detailBgModal.style.opacity = 0;
detailModal.style.display = 'none';
canShowModal = false;
};
let modalDetailPos;
const handleBodyScroll = () => {
const {
scrollY
} = window;
if (Math.abs(scrollY - modalDetailPos) > 50) {
const event = new Event('click');
detailModal.dispatchEvent(event);
window.removeEventListener('scroll', handleBodyScroll);
}
};
images.forEach((image) => {
fromEvent(image, 'click').subscribe(() => {
if (!canShowModal) {
return
}
image.setAttribute('fullscreen', true)
console.log('show image')
showModal(image).then(() => {
const modalImage = document.querySelector('#detail-modal img');
const firstSnap = image.getBoundingClientRect();
const lastSnap = modalImage.getBoundingClientRect();
const {
deltaX,
deltaY,
deltaWidth,
deltaHeight
} = getDelta(firstSnap, lastSnap);
modalImage.animate([{
transformOrigin: 'top left',
transform: `
translate(${deltaX}px, ${deltaY}px)
scale(${deltaWidth}, ${deltaHeight})
`
},
{
transformOrigin: 'top left',
transform: 'none'
}
], {
duration: 300,
easing: 'ease-in-out',
fill: 'both'
}).onfinish = () => {
modalDetailPos = window.scrollY;
window.addEventListener('scroll', handleBodyScroll)
};
});
});
})
const moveElementToFullscreen = (element) => {
element.style.position = 'fixed';
element.style.left = 0;
element.style.top = 0;
element.style.right = 0;
element.style.bottom = 0;
};
const moveElementToNormalState = (element) => {
element.style.position = null;
element.style.left = null;
element.style.top = null;
element.style.right = null;
element.style.bottom = null;
};
const getDelta = (firstSnap, lastSnap) => {
const deltaX = firstSnap.left - lastSnap.left;
const deltaY = firstSnap.top - lastSnap.top;
const deltaWidth = firstSnap.width / lastSnap.width;
const deltaHeight = firstSnap.height / lastSnap.height;
return {
deltaX: deltaX,
deltaY: deltaY,
deltaWidth: deltaWidth,
deltaHeight: deltaHeight
};
}
fromEvent(detailModal, 'click').subscribe(() => {
const showImage = document.querySelector('[fullscreen=true]');
if (!showImage) {
return;
}
const modalImage = document.querySelector('#detail-modal img');
console.log('showImage', showImage)
const firstSnap = modalImage.getBoundingClientRect();
const lastSnap = showImage.getBoundingClientRect();
hideModal();
const {
deltaX,
deltaY,
deltaWidth,
deltaHeight
} = getDelta(firstSnap, lastSnap);
showImage.style.zIndex = 100;
showImage.animate([{
transformOrigin: 'top left',
transform: `
translate(${deltaX}px, ${deltaY}px)
scale(${deltaWidth}, ${deltaHeight})
`
},
{
transformOrigin: 'top left',
transform: 'none'
}
], {
duration: 400,
easing: 'ease',
fill: 'both'
});
});
article {
max-width: 700px;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
}
p {
font-family: 'Nunito';
font-size: 18px;
color: rgba(0, 0, 0, .84);
line-height: 1.60;
margin: 30px auto;
}
article img {
max-width: 100%;
display: block;
position: relative;
cursor: zoom-in;
}
#detail-modal {
justify-content: center;
align-items: center;
display: none;
position: absolute;
left: 0;
right: 0;
top: 0;
}
#detail-modal img {
display: block;
position: relative;
z-index: 100;
cursor: zoom-out;
}
.bg {
position: fixed;
left: 0;
top: 0;
right: 0;
background-color: rgba(0,0,0,.3);
opacity: 0;
display: block;
transition: opacity .3s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>
<article>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut rutrum mauris id nibh ultrices, vitae hendrerit nibh venenatis. Phasellus volutpat mauris in diam lacinia, sit amet blandit ante scelerisque. Mauris porttitor risus sit amet urna vestibulum
porta. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer id diam sem. Nunc commodo, est sed efficitur condimentum, massa purus facilisis tellus, at commodo ex est a tellus. Morbi quis iaculis mi. Nam et
iaculis sapien, at mattis ipsum.</p>
<div>
<img src="https://images.unsplash.com/photo-1507358522600-9f71e620c44e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2850&q=80" />
</div>
<p>Nullam non porttitor nibh. Etiam mollis libero turpis, vitae sagittis ipsum gravida nec. Vivamus diam sapien, laoreet vel mi ultrices, efficitur tristique nunc. Nam tempus pharetra felis, nec condimentum leo vehicula a. Duis rutrum orci a tellus tristique
scelerisque. Suspendisse potenti. Proin mollis turpis feugiat, pulvinar risus ac, scelerisque diam. Aenean sodales venenatis tellus, in lacinia sapien. Nam tempus efficitur ligula id feugiat. Donec pretium, nunc sit amet dignissim rutrum, urna est
tristique ante, id convallis arcu urna vel dui. Cras a metus id orci aliquet tincidunt eget ac mi. Pellentesque elementum lorem in elementum vehicula. Nunc et dolor orci. Nulla varius lorem metus, vel cursus leo ultricies non.</p>
<div>
<img src="https://images.unsplash.com/photo-1548636200-691c76f69390?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=668&q=80" />
</div>
<p>
Aliquam at arcu mauris. Curabitur tincidunt massa ut sem porttitor ornare. Duis dapibus dignissim lectus. Cras sodales urna vitae libero lobortis, in consequat dolor efficitur. Sed eleifend nibh mi, sit amet euismod sem faucibus sed. Aenean ac accumsan
libero, ut dictum ex. Aenean tincidunt gravida enim, in luctus ante volutpat eu. Curabitur sed orci nec nisi cursus blandit.
</p>
<div>
<img src="https://images.unsplash.com/reserve/fPuLkQNXRUKI6HQ2cMPf_IMG_4761.jpg?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80" />
</div>
<p>
Morbi ac quam luctus, aliquam odio in, consectetur orci. Etiam et dui sollicitudin, congue odio sit amet, commodo metus. Nunc ac facilisis dolor, sit amet dignissim dui. Praesent vehicula ut dui hendrerit commodo. Vivamus ac elementum turpis. Proin non
erat semper, dignissim risus vel, ornare libero. Ut volutpat libero non lacus eleifend ultrices. Morbi augue massa, placerat eget eros vel, consequat tincidunt sapien. Vestibulum placerat diam placerat tincidunt lacinia. Proin lorem justo, viverra
pretium laoreet eu, condimentum et odio. Proin vitae nibh felis.
</p>
</article>
<div class="bg"></div>
<div id="detail-modal">
<img />
</div>