Escrevendo um gerador de galeria de imagens com um controle deslizante embutido



Bom dia amigos

Em vez de introdução (declaração do problema)


Tudo começou com o estudo dos controles deslizantes de outras pessoas (soluções prontas na rede, como bxslider , owlcarousel e slick ). Algum dia vou escrever tutoriais detalhados sobre como trabalhar com essas ferramentas (bons sonhos). Havia um desejo de escrever seu controle deslizante. No entanto, logo (inclusive depois de ler vários artigos sobre Habré), chegou à conclusão de que apenas um controle deslizante é para os fracos. Algo mais radical é necessário.

Como resultado, criei a seguinte tarefa: escrever um gerador de galeria adaptável com um controle deslizante embutido.

Condições:

  • Capacidade de fazer upload de qualquer número de imagens (de qualquer lugar no seu disco rígido).
  • A galeria consiste em imagens carregadas, a marcação é formada "on the fly", em conformidade com a semântica HTML5.
  • A galeria parece igualmente boa em telas com diferentes resoluções.
  • Quando você clica em qualquer imagem, um controle deslizante é gerado.
  • Ao gerar um controle deslizante, o fundo é escurecido.
  • A imagem clicada é o primeiro slide.
  • A troca de slides é implementada através do DOM.
  • Slides alternam sem problemas.
  • Capacidade de controlar a troca de slides usando os botões e o teclado.
  • A capacidade de retornar à galeria clicando no slide e no botão atuais e usando o teclado.
  • JavaScript puro (todas as marcações via JS).
  • Código mínimo.

Então, vamos lá (como disse Gagarin, indo para o espaço).

A marcação é assim:
<div class="wrap">
    <input type="file" multiple accept="image/*">
    <button>generate gallery</button>
</div>

Das coisas interessantes, talvez, os múltiplos e os atributos de aceitação do tag de entrada. O primeiro atributo permite fazer upload de vários arquivos, o segundo - define um filtro para os tipos de arquivos que podem ser baixados. Nesse caso, accept tem o valor "image / *", o que significa que apenas imagens (qualquer) podem ser carregadas.

Traga imediatamente a beleza (adicione estilos):
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    min-height: 100vh;
    background: radial-gradient(circle, skyblue, steelblue);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

button {
    padding: 0.25em;
    font-family: monospace;
    text-transform: uppercase;
    cursor: pointer;
}

.darken {
    position: absolute;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.4);
    z-index: -1;
}

.slider {
    width: 100%;
    display: inherit;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
}

figure {
    margin: 0.5em;
    width: 300px;
    display: inherit;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    transition: 0.2s;
}

figcaption {
    font-size: 1.2em;
    text-transform: capitalize;
    text-align: center;
    margin-bottom: 0.25em;
    color: #ddd;
    text-shadow: 1px 1px rgba(0, 0, 0, 0.4);
}

img {
    max-width: 80%;
    max-height: 80vh;
    cursor: pointer;
}

.button {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 30px;
    background: none;
    border: none;
    outline: none;
    filter: invert();
}

.left {
    left: 2em;
}

.right {
    right: 2em;
}

.close {
    top: 2em;
    right: 1em;
}


Não há nada para falar (.darken - escurecimento).

Passando ... para JS.

Nós encontramos o botão e penduramos o ouvinte nele:
let button = document.querySelector("button");
button.addEventListener("click", generateGallery);

Todo o código adicional estará na função generateGallery para evitar "não definido" sem retorno:
function generateGallery() {
    //    
}

Encontramos entrada, verifique se não está vazia, obtenha uma coleção de arquivos baixados, exclua .wrap e crie um contêiner para a galeria:
let input = document.querySelector("input");
// ,  input  
if(input.files.length == 0) return;
let files = input.files;
//  
let i;

//  .wrap,     
let wrap = document.querySelector(".wrap");
document.body.removeChild(wrap);

//    , ,    gallery
let slider = document.createElement("div");
slider.className = "slider";
document.body.appendChild(slider);

Classificamos a coleção de arquivos, obtemos o nome e o endereço de cada arquivo, criamos marcações, criamos legendas de imagens e as próprias imagens:
for (i = 0; i < files.length; i++) {
    let file = files[i];
    // URL.createObjectURL         ,         
    // ,   ,       
    //             ,      
    /*let src = URL.createObjectURL(file);*/

    //   
    let name = file.name;

    //   ,      img     
    let src = `img/${name}`;

    //  : figure, figcaption, img
    let figure = document.createElement("figure");
    slider.appendChild(figure);

    let figcaption = document.createElement("figcaption");

    //  ,             ,   
    // (?=\.) -  :     ,    
    //       \w,         
    let regexp = /.+(?=\.)/;
    name = name.match(regexp);
    //   ["", index: 0, input: ".jpg", groups: undefined]
    //    
    figcaption.innerText = name[0];
    figure.appendChild(figcaption);

    //  
    let img = document.createElement("img");
    img.src = src;
    figure.appendChild(img);
}

Queremos gerar um controle deslizante ao clicar na imagem. Para fazer isso, encontramos a figura inteira e a penduramos em cada ouvinte:
let figures = document.querySelectorAll("figure");
for (i = 0; i < figures.length; i++) {
    let figure = figures[i];
    figure.addEventListener("click", () => {
        //  ,       figure,   
        generateSlider(figure);
    });
}

Em seguida, trabalhamos dentro da função generateSlider:
function generateSlider(figure) {
    //  
}

Escureça o fundo:
darkenBack();
function darkenBack() {
    // ,   
    //  , ,   , 
    if (document.querySelector(".darken") == null) {
        let div = document.createElement("div");
        div.className = "darken";
        document.body.appendChild(div);
    } else {
        let div = document.querySelector(".darken");
        document.body.removeChild(div);
    }
}

Vamos exibir um slide de cada vez. Não esqueça que o interruptor deslizante deve ser suave. Isso é fácil de conseguir com transparência e uma pequena transição. Portanto, sobrepomos as imagens umas sobre as outras, as colocamos no centro e tornamos todas as imagens, exceto a clicada, transparentes:
for (i = 0; i < figures.length; i++) {
    if (figures[i].hasAttribute("style")) {
        figures[i].removeAttribute("style");
    } else {
        figures[i].setAttribute("style", "margin: 0; width: auto; position: absolute; opacity: 0;");
    }
}

//      / 
if (figure.hasAttribute("style")) {
    figure.style.opacity = 1;
    generateButtons();
} else generateButtons();

Em seguida, crie botões para alternar slides e fechar a galeria. O código acabou sendo longo e chato (talvez gerar botões toda vez que você inicia o controle deslizante não fosse uma boa ideia):
Código de criação do botão:
function generateButtons() {
    if (document.querySelector(".buttons") == null) {
        //    
        let buttons = document.createElement("div");
        buttons.className = "buttons";
        slider.appendChild(buttons);

        //   
        let leftButton = document.createElement("button");
        leftButton.className = "button left";
        let leftImg = document.createElement("img");
        leftImg.src = "https://thebestcode.ru/media/sliderGenerator/buttons/left.png";
        leftButton.appendChild(leftImg);
        buttons.appendChild(leftButton);
        leftButton.addEventListener("click", () => changeSlide("-"));

        //   
        let rightButton = document.createElement("button");
        rightButton.className = "button right";
        let rightImg = document.createElement("img");
        rightImg.src = "https://thebestcode.ru/media/sliderGenerator/buttons/right.png";
        rightButton.appendChild(rightImg);
        buttons.appendChild(rightButton);
        rightButton.addEventListener("click", () => changeSlide("+"));

        //    
        let closeButton = document.createElement("button");
        closeButton.className = "button close";
        let closeImg = document.createElement("img");
        closeImg.src = "https://thebestcode.ru/media/sliderGenerator/buttons/close.png";
        closeButton.appendChild(closeImg);
        buttons.appendChild(closeButton);
        closeButton.addEventListener("click", () => generateSlider(figure));
    } else {
        //   ,  
        let buttons = document.querySelector(".buttons");
        slider.removeChild(buttons);
    }
}


A troca de slides é implementada usando a função changeSlide, que é passada como parâmetro, respectivamente, "+" ou "-":
function changeSlide(e) {
    //    
    for (i = 0; i < figures.length; i++) {
        figures[i].style.opacity = 0;
    }
    if (e == "-") {
        //      ,    
        if (figure == figures[0]) {
            figure = figures[figures.length - 1];
        } else {
            figure = figure.previousElementSibling;
        }
    } else if (e == "+") {
        //      ,    
        if (figure == figures[figures.length - 1]) {
            figure = figures[0];
        } else {
            figure = figure.nextElementSibling;
        }
    }
    //    
    figure.style.opacity = 1;
}

Adicione a capacidade de alternar slides e fechar a galeria usando o teclado:
document.addEventListener("keydown", e => {
    //  
    if (e.keyCode == 37 || e.keyCode == 189) {
        changeSlide("-");
    //  
    } else if (e.keyCode == 39 || e.keyCode == 187) {
        changeSlide("+");
    // esc
    } else if(e.keyCode == 27) {
        generateSlider(figure);
    }
});

Isso é tudo, o gerador de galeria adaptável com o controle deslizante embutido está pronto. Missão cumprida. As condições são atendidas. No final, percebi que “código mínimo” e “toda marcação é formada em tempo real usando JS” se contradizem, mas era tarde demais (é tarde demais para pedir desculpas ou o que dizer sobre One Republic?).

O resultado pode ser visto aqui .

Observe que no Codepen usamos URL.createObjectURL para vincular às imagens, porque o Codepen não vê a pasta img.

Obrigado pela atenção.

Source: https://habr.com/ru/post/undefined/


All Articles