Создание квадратизированной галереи проектов на JS v 2.0

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
Под квадратизированной галереей подразумевается галерея такого типа
Под квадратизированной галереей подразумевается галерея такого типа

Это моя вторая статья на тему создания адаптивной галереи проектов на JS. На основе замечаний и комментариев к первой я постарался исправить некоторые ошибки и поработал над логикой и структурой кода. Для удобства добавил возможность просмотра галереи и полного кода в песочнице.

Цели и задачи

  • Создать js-класс ImgComponent, описывающий компонент картинки галереи и принимающий следующие аргументы: тип картинки - широкая, высокая, стандартная, ссылка на изображение, ссылка на проект.

  • Генерировать галерею в html.

  • Реализовать механизм показа/скрытия частей галереи. После полного показа галереи - кнопка должна скрываться.

  • При наведении на картинку поверх изображения должна накладываться подсказка - заголовок проекта и прочая информация.

  • По class.elem должен быть доступен корневой DOM элемент картинки.

В статье пошагово описываю все свои действия по каждому пункту. Для создания данной галереи вам понадобится базовое знание ООП в JS и grid в CSS.

Приступим к созданию

1. Создадим HTML-контейнер для галереи:

<div class="gallery">
	<!--Сюда будут интерпретироваться картинки-->
</div>

2. Пропишем CSS для галереи и картинок:

Стили для высокой, широкой и стандартной картинки:

.short_box {
  width: 383px;
  height: 337px;
  background: #ffffff;
}
	
.high_box {
  width: 383px;
  height: 692px;
  background: #ffffff;
}
	
.long_box {
  width: 795px;
  height: 337px;
  background: #ffffff;
  grid-column: 1/2;
}

Стили для всех картинок:

.gallery > div {
  border: 4px solid #BB70B3;
  z-index: 10;
  position: relative;
  overflow: hidden;
  cursor: pointer;
}

.gallery > div img {
  z-index: -5;
  width: 100%;
  height: auto;
  position: absolute;
  left: 50%; 
  top: 50%;
  transform: translate(-50%, -50%);
}

Стили для контейнера галереи:

.gallery {
  width: 805px;
  display: grid;
  grid-template-columns: repeat(2, 390px);
  grid-gap: 20px;
  margin: 0 auto;
  justify-content: center;
  margin-top: 50px;
  margin-bottom: 50px;
}

Стили для кнопки прокрутки:

.scroll_button {
  width: 200%;
  cursor: pointer;
  grid-column: 1/2;
  padding: 0 10px 0 10px;
  position: relative;
}

Стили для анимации при наведении на кнопку:

@keyframes scroll_move {
  0% {
    top: 0;
  }
  100% {
    top: 12px;
  }
}  
.scroll_button:hover {
  animation-name: scroll_move;
  animation-duration: 0.5s;
  animation-iteration-count: infinite;
  animation-timing-function: ease-out;
  animation-direction: alternate;
}

Стили для анимации подсказки при наведении на картинку - маска, заголовок, текст

.mask {
  width: 100%;
  height: 100%;
  background: rgba(176, 155, 174, 0.92);
  position: absolute;
  display: none;
}

.descr {
  position: absolute;
  top: 35%;
  display: none;
  margin-left: 26px;
}

.gallery > div:hover .mask {
  display: block;
}
.gallery > div:hover .descr {
  display: block;
  top: 50%;
  transform: translate(0%, -50%);
}

.gallery h2 {
  font-family: Nunito;
  font-style: normal;
  font-weight: normal;
  font-size: 24px;
  line-height: 33px;
  letter-spacing: 0.2em;
  color: #FFFFFF;
}
.gallery p {
  font-family: Nunito;
  font-style: normal;
  font-weight: normal;
  font-size: 11px;
  line-height: 15px;
  letter-spacing: 0.2em;
  color: #F3F3F3;
  padding-top: 7px;
}

Дополнительные стили для адаптивности галереи:

@media screen and (max-width: 795px) {
  .gallery {
    width: 595px;
    grid-template-columns: 1fr;
  }
  .short_box {
    width: 283px;
    height: 237px;
    background: #ffffff;
  }
  .high_box {
    width: 283px;
    height: 490px;
    background: #ffffff;
  }
  .long_box {
    width: 595px;
    height: 237px;
    background: #ffffff;
  }
  .high_box, .long_box, .short_box {
    grid-column: 1!important;
    grid-row: auto!important;
    justify-self: center;
  }
  .scroll_button {
    grid-column: 1;
    width: 100%;
    padding: 0;
  }
}

@media screen and (max-width: 595px) {
  .gallery {
    width: 100%;
  }
  .long_box {
    width: 100%;
    height: 237px;
  }
}

3. Пишем JS-класс для создания компонентов:

Создадим класс, в котором будем создавать DOM-элемент и повесим на него событие “onclick” для перехода по нужной ссылке. Для будущего механизма показа/скрытия частей галереи передадим аргумент groupImg, в который впоследствии будет передаваться группа с принадлежащим ей изображением. Благодаря этому мы сможем скрывать и показывать нужные нам группы изображений.

В аргумент descrip передаем объект с двумя свойствами: header - заголовок, text - текст подсказки.

class ImgComponent {
    constructor(groupImg, srcImg, srcToProject, row, column, size, descrip) { // передаем аргументы: группа изображений, ссылка на изображение, ссылка на проект, значение свойства row, значение свойства column, размер картинки по заранее созданным типам(short, long, high), объект подсказки

        this.render(); //создаем корневой DOM элемент

        this.elem.className = `img_gallery ${size}_box`; // присваиваем классы для картинок, и также класс описывающий его тип
        this.elem.dataset.group = `${groupImg}`; // Присваиваем data-атрибут 
        this.elem.href = `./articles/${srcToProject}`; // Присваиваем ссылку на проект, на который ведет картинка
        this.elem.style.cssText = `
            grid-row: ${size === 'high' ? row + '/' + ++row : row};
            grid-column: ${size === 'long' ? '1/2' : column + '/' + column};
        `; // Присваиваем все нужные свойства.
        this.elem.innerHTML = `
            <div class="mask"></div>
            <img src="img/${srcImg}" alt="gallery_img">
            <div class="descr">
                <h2>${descrip.header}</h2>
                <p>${descrip.text}</p>
            </div>
        `; // Вкладываем внутрь элемента картинку, маску и подсказку
        this.appendElem(); // Добавляем элемент на страницу
        this.onClick(); // Добавляем на элемент событие onclick
    } 
    render = () => { 
        this.elem = document.createElement('div'); //создаем метод, создающий корневой DOM элемент нашей картинки.
    }
    appendElem = () => { // Создаем метод, для добавления элемента на страницу
        document.querySelector('.gallery').append(this.elem);
    }
    onClick = () => { // Создаем метод для прослушки события onclick элемента, для перехода по ссылке.
        this.elem.addEventListener('click', () => {
            window.open(this.elem.href);
        })
    }
}

Класс для генерации галереи готов, теперь можем создавать на основе него нужные нам компоненты (картинки).

new ImgComponent(1, 'img_1.jpg', 'article1.html', 1, 1, 'short', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_1.jpg', 'article1.html', 1, 2, 'short', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_3.jpg', 'article1.html', 2, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_2.jpg', 'article1.html', 3, 1, 'high', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_2.jpg', 'article1.html', 3, 2, 'high', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(2, 'img_3.jpg', 'article1.html', 4, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(2, 'img_3.jpg', 'article1.html', 5, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(3, 'img_3.jpg', 'article1.html', 6, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(3, 'img_3.jpg', 'article1.html', 7, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});

Далее создадим объект, на основе которого будет создаваться элемент нашей кнопки, и все необходимые методы.

let scrollButton = {
    elem: document.createElement('img'), // Создаем корневой DOM элемент кнопки
    groupNum: 1, // Создаем свойство, которая будет считать прогресс показа картинок
    hiddenImg() {  // Создаем метод скрытия для первоначального всех изображений
        let array = document.querySelectorAll('.img_gallery'); // Выделяем все изображения внутри галлереи
        let arrayShow = document.querySelectorAll(`.img_gallery[data-group="${this.groupNum}"]`); // выделяем первую группу изображений по атрибута data-group для показа
        for(let i = 0; i < array.length; i++) { // Скрываем все изображения
            array[i].style.display = 'none';
        }
        for(let i = 0; i < arrayShow.length; i++) { // Показ первой группы изображений
            arrayShow[i].style.display = '';
            if(i === arrayShow.length - 1) { // Вставка элементов после первой группе изображений
                arrayShow[i].insertAdjacentElement('afterEnd', this.elem);
            }
        }
    },
    showImg() { // Создаем метод показа следующей группы изображений
        let array = document.querySelectorAll('.img_gallery'); // Выделяем все изображения внутри галлереи
        let arrayShow = document.querySelectorAll(`.img_gallery[data-group="${this.groupNum}"]`); // выделяем следующую группу изображений по атрибута data-group для показа
        for(let i = 0; i < arrayShow.length; i++) { // Перебор массива изображений которые нужно показать
            arrayShow[i].style.display = ''; // показываем все изображения из нашего массива 
            if(arrayShow[i] !== array[array.length - 1]) { // Проверка - на то является ли группа картинок последней
                arrayShow[i].insertAdjacentElement('afterEnd', this.elem); // Если нет, то вставляем кнопку после следующей группы картинок
            } else {
                this.elem.style.display = 'none'; // Если условие не проходит, скрываем кнопку со страницы
            }
        }
    },
    render() {
        this.elem.src = './img/scroll.svg'; // Прописываем путь до кнопки показа
        this.elem.className = 'scroll_button'; // Присваивает класс, для последующего приминения CSS стилей
        this.hiddenImg(); // Скрытие всех изображений на странице, кроме первой группы

        this.elem.addEventListener('click', () => { // Вешаем событие на кнопку - показ группы изображений и увелечение переменной хранящей в себе прогресс показа изображений
            this.groupNum++;
            this.showImg();
        })
    }
}

Завершающий штрих! Вызовем метод render() для активации механизма.

scrollButton.render();
Полный JS код
class ImgComponent {
    constructor(groupImg, srcImg, srcToProject, row, column, size, descrip) {

        this.render();

        this.elem.className = `img_gallery ${size}_box`;
        this.elem.dataset.group = `${groupImg}`;
        this.elem.href = `./articles/${srcToProject}`;
        this.elem.style.cssText = `
            grid-row: ${size === 'high' ? row + '/' + ++row : row};
            grid-column: ${size === 'long' ? '1/2' : column + '/' + column};
        `;
        this.elem.innerHTML = `
            <div class="mask"></div>
            <img src="img/${srcImg}" alt="gallery_img">
            <div class="descr">
                <h2>${descrip.header}</h2>
                <p>${descrip.text}</p>
            </div>
        `;
        this.appendElem();
        this.onClick();
    } 
    render = () => { 
        this.elem = document.createElement('div');
    }
    appendElem = () => { 
        document.querySelector('.gallery').append(this.elem);
    }
    onClick = () => { 
        this.elem.addEventListener('click', () => {
            window.open(this.elem.href);
        })
    }
}

new ImgComponent(1, 'img_1.jpg', 'article1.html', 1, 1, 'short', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_1.jpg', 'article1.html', 1, 2, 'short', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_3.jpg', 'article1.html', 2, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_2.jpg', 'article1.html', 3, 1, 'high', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(1, 'img_2.jpg', 'article1.html', 3, 2, 'high', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(2, 'img_3.jpg', 'article1.html', 4, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(2, 'img_3.jpg', 'article1.html', 5, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(3, 'img_3.jpg', 'article1.html', 6, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});
new ImgComponent(3, 'img_3.jpg', 'article1.html', 7, 1, 'long', {'header': 'Head', 'text': 'Test text test text test text'});


let scrollButton = {
    elem: document.createElement('img'),
    groupNum: 1,
    hiddenImg() {
        let array = document.querySelectorAll('.img_gallery');
        let arrayShow = document.querySelectorAll(`.img_gallery[data-group="${this.groupNum}"]`);
        for(let i = 0; i < array.length; i++) {
            array[i].style.display = 'none';
        }
        for(let i = 0; i < arrayShow.length; i++) {
            arrayShow[i].style.display = '';
            if(i === arrayShow.length - 1) {
                arrayShow[i].insertAdjacentElement('afterEnd', this.elem);
            }
        }
    },
    showImg() {
        let array = document.querySelectorAll('.img_gallery');
        let arrayShow = document.querySelectorAll(`.img_gallery[data-group="${this.groupNum}"]`);
        for(let i = 0; i < arrayShow.length; i++) {
            arrayShow[i].style.display = '';
            if(arrayShow[i] !== array[array.length - 1]) {
                arrayShow[i].insertAdjacentElement('afterEnd', this.elem);
            } else {
                this.elem.style.display = 'none';
            }
        }
    },
    render() {
        this.elem.src = './img/scroll.svg';
        this.elem.className = 'scroll_button';
        this.hiddenImg();

        this.elem.addEventListener('click', () => {
            this.groupNum++;
            this.showImg();
        })
    }
}

scrollButton.render();
Полный CSS код
@keyframes scroll_move {
  0% {
    top: 0;
  }
  100% {
    top: 12px;
  }
}  

.short_box {
  width: 383px;
  height: 337px;
  background: #ffffff;
}
	
.high_box {
  width: 383px;
  height: 692px;
  background: #ffffff;
}
	
.long_box {
  width: 795px;
  height: 337px;
  background: #ffffff;
  grid-column: 1/2;
}

.gallery > div {
  border: 4px solid #BB70B3;
  z-index: 10;
  position: relative;
  overflow: hidden;
  cursor: pointer;
}

.gallery > div img {
  z-index: -5;
  width: 100%;
  height: auto;
  position: absolute;
  left: 50%; 
  top: 50%;
  transform: translate(-50%, -50%);
}

.gallery {
  width: 805px;
  display: grid;
  grid-template-columns: repeat(2, 390px);
  grid-gap: 20px;
  margin: 0 auto;
  justify-content: center;
  margin-top: 50px;
  margin-bottom: 50px;
}

.scroll_button {
  width: 200%;
  cursor: pointer;
  grid-column: 1/2;
  padding: 0 10px 0 10px;
  position: relative;
}

.scroll_button:hover {
  animation-name: scroll_move;
  animation-duration: 0.5s;
  animation-iteration-count: infinite;
  animation-timing-function: ease-out;
  animation-direction: alternate;
}

.mask {
  width: 100%;
  height: 100%;
  background: rgba(176, 155, 174, 0.92);
  position: absolute;
  display: none;
}

.descr {
  position: absolute;
  top: 35%;
  display: none;
  margin-left: 26px;
}

.gallery > div:hover .mask {
  display: block;
}
.gallery > div:hover .descr {
  display: block;
  top: 50%;
  transform: translate(0%, -50%);
}

.gallery h2 {
  font-family: Nunito;
  font-style: normal;
  font-weight: normal;
  font-size: 24px;
  line-height: 33px;
  letter-spacing: 0.2em;
  color: #FFFFFF;
}
.gallery p {
  font-family: Nunito;
  font-style: normal;
  font-weight: normal;
  font-size: 11px;
  line-height: 15px;
  letter-spacing: 0.2em;
  color: #F3F3F3;
  padding-top: 7px;
}

@media screen and (max-width: 795px) {
  .gallery {
    width: 595px;
    grid-template-columns: 1fr;
  }
  .short_box {
    width: 283px;
    height: 237px;
    background: #ffffff;
  }
  .high_box {
    width: 283px;
    height: 490px;
    background: #ffffff;
  }
  .long_box {
    width: 595px;
    height: 237px;
    background: #ffffff;
  }
  .high_box, .long_box, .short_box {
    grid-column: 1!important;
    grid-row: auto!important;
    justify-self: center;
  }
  .scroll_button {
    grid-column: 1;
    width: 100%;
    padding: 0;
  }
}

@media screen and (max-width: 595px) {
  .gallery {
    width: 100%;
  }
  .long_box {
    width: 100%;
    height: 237px;
  }
}

Полный код галереи в песочнице - песочница

P.S. Если у вас есть идеи для дополнения или улучшения моей галереи, пишите в комментариях, я обязательно это реализую.

Источник: https://habr.com/ru/post/543660/


Интересные статьи

Интересные статьи

В прошлом году вышел API Figma, предназначенный для разработки плагинов. Команда дизайнеров Discord увидела в этом событии потрясающую возможность для улучшения своих техпроцессов. При...
Недавно я работал над анимацией респауна и спецэффектом главного героя моей игры “King, Witch and Dragon”. Для этого спецэффекта мне нужна была пара сотен анимированных крыс. Соз...
Асинхронная библиотека discord.py содержит все что нужно для бота, с помощью нее даже можно работать с голосовыми каналами сервера. В этой статье я расскажу как создать простенького бота для ваш...
Я написала мои первые сайты в конце 90-х. Тогда приводить их в рабочее состояние было очень просто. Был Apache-сервер на каком-нибудь общем хостинге, на этот сервер можно было войти по FTP, напис...
Доброго времени суток, сегодня я хочу рассказать об устройстве которое я разработал и собрал. Введение Столы с возможностью изменения высоты находятся на рынке уже давно и существует очен...