DISEPICGALL

by Manuel Castro

HTML

<div class="galeria"></div>

<div id="modal" class="modal">
    <span class="cerrar">&times;</span>
    <img id="modalImg">
</div>

CSS

.galeria {
      display: grid;
    grid-template-columns: repeat(4, 0.5fr);
    gap: 20px;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 20px;
    justify-content: center;
    }

    .galeria .item {
      aspect-ratio: 1 / 1;
      overflow: hidden;
      border-radius: 12px;
      /*background: #e0e0e0;*/
      margin: 10px 15px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.08);
    }

    .galeria .item a {
      display: block;
      width: 100%;
      height: 100%;
    }

    .galeria .item img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }

/* Modal */

.modal{
    position:fixed;
    inset:0;
    background:rgba(0,0,0,.82);

    display:flex;
    justify-content:center;
    align-items:center;

    opacity:0;
    visibility:hidden;

    transition:.28s ease;

    z-index:9999;
}

.modal.activo{
    opacity:1;
    visibility:visible;
}

.modal img{

    max-width:90vw;
    max-height:90vh;

    border-radius:12px;

    transform:scale(.92);

    transition:.28s ease;

    box-shadow:0 25px 70px rgba(0,0,0,.45);

}

.modal.activo img{
    transform:scale(1);
}

.cerrar{

    position:absolute;

    top:25px;
    right:35px;

    font-size:42px;
    color:white;

    cursor:pointer;

    user-select:none;

    font-family:Arial;
}

JavaScript

const API = "https://script.google.com/macros/s/AKfycbyBjZL328x9u-1kVmaaWqftxhwTRTa9HSlwqF4SGDUwkWdEF5KdZU-j1TLq_5pWg3XDXg/exec";

const galeria = document.querySelector(".galeria");
const modal = document.getElementById("modal");
const modalImg = document.getElementById("modalImg");
const cerrar = document.querySelector(".cerrar");

let archivos = [];
let indice = 0;
const LOTE = 20;

const observer = new IntersectionObserver((entries) => {

    if (entries[0].isIntersecting) {

        observer.unobserve(entries[0].target);

        if (indice < archivos.length) {

            cargarLote();

        }

    }

}, {
    rootMargin: "300px"
});

fetch(API)
.then(res => res.json())
.then(data => {

    archivos = data;

    cargarLote();

});

function cargarLote() {

    const fragment = document.createDocumentFragment();

    const fin = Math.min(indice + LOTE, archivos.length);

    let ultimoItem = null;

    for (let i = indice; i < fin; i++) {

        const file = archivos[i];

        const item = document.createElement("div");
        item.className = "item";

        const img = document.createElement("img");

        img.loading = "lazy";
        img.src = file.thumb;
        img.alt = file.name;
        img.dataset.full = file.thumb;

        img.onclick = () => {

            modalImg.src = img.dataset.full;

            modal.classList.add("activo");

            document.body.style.overflow = "hidden";

        };

        item.appendChild(img);

        fragment.appendChild(item);

        ultimoItem = item;

    }

    galeria.appendChild(fragment);

    indice = fin;

    if (ultimoItem && indice < archivos.length) {

        observer.observe(ultimoItem);

    }

}

cerrar.onclick = cerrarModal;

modal.onclick = (e)=>{

    if(e.target===modal){

        cerrarModal();

    }

};

document.addEventListener("keydown",(e)=>{

   ...