Простой веб-компонент для создания слайд-шоу

Простой веб-компонент для создания слайд-шоу

25 января 2023 г.

Поскольку я продолжаю экспериментировать и узнавать больше о веб-компонентах, я решил создать простой компонент, чтобы упростить добавление слайд-шоу. Под этим я подразумеваю что-то, что отображает одно изображение, но предоставляет элементы управления для перехода к большему количеству изображений. Я, вероятно, много раз создавал это в прошлом, как в JavaScript, так и в серверном коде, и я подумал, что это будет хорошим кандидатом на роль компонента. Как и в случае с большинством моих демонстраций, с ним можно сделать гораздо больше, но я решил поделиться тем, что у меня есть. Еще раз хочу поблагодарить Саймона Макдональда за помощь в работе над этим кодом. (В конце поста я поделюсь ошибкой, которую я совершил, так как я думаю, что с ней столкнутся другие, а также модифицированная версия, созданная Саймоном.)

Итак, я начал с «проектирования» того, как я хочу использовать компонент на обычной HTML-странице. Я хотел разрешить список изображений, передаваемых через атрибут:

<slide-show images="
    https://placekitten.com/500/500,
    https://picsum.photos/id/1/500/500,
    https://via.placeholder.com/500,
    https://placebear.com/500/500,
    https://baconmockup.com/500/500
    ">
</slide-show>

Обратите внимание, что я добавил пробелы вокруг URL-адресов. Я сделал это, чтобы сделать код более читабельным и простым для модификации. (Мне пришлось несколько раз изменить исходные URL-адреса.) Тег также поддерживает атрибут width и обычно должен использоваться всегда, но по умолчанию он равен 500.

<slide-show width="500" images="
    https://placekitten.com/500/500,
    https://picsum.photos/id/1/500/500,
    https://via.placeholder.com/500,
    https://placebear.com/500/500,
    https://baconmockup.com/500/500
    ">
</slide-show>

Теперь давайте посмотрим на код JavaScript. Это не очень долго, поэтому я поделюсь всей частью, а затем расскажу о том, что делает каждая часть:

class SlideShow extends HTMLElement {

    constructor() {

        super();

        const shadow = this.attachShadow({mode:'open'});

        if(!this.hasAttribute('images')) {
            console.warn('slide-show called with no images');
            return;
        }

        if(!this.hasAttribute('width')) {
            // default
            this.setAttribute('width', 500);
        }

        /*
        Convert attribute into an array and do some trimming so that the end user can have some spacing
        */
        this.images = this.getAttribute('images').split(',').map(i => i.trim());

        // preload for quicker response, we don't need to wait for this
        this.preload(this.images);

        this.totalImages = this.images.length;

        this.current = 0;

        const wrapper = document.createElement('div');

        wrapper.innerHTML = `
        <img id="currentImage" src="${this.images[this.current]}">
        <p>
        <button id="prevButton">Previous</button> 
        Picture <span id="currentPicture">1</span> of ${this.totalImages}
        <button id="nextButton">Next</button> 
        </p>
        `;

        this.$nextButton = wrapper.querySelector('#nextButton');
        this.$prevButton = wrapper.querySelector('#prevButton');
        this.$currentPicture = wrapper.querySelector('#currentPicture');
        this.$image = wrapper.querySelector('#currentImage');

        const style = document.createElement('style');
        style.innerHTML = `
div {
    width: ${this.getAttribute('width')}px
}
p {
text-align: center;
}
        `;
        shadow.appendChild(wrapper);
        shadow.appendChild(style);

    }

    connectedCallback() {
        this.$nextButton.addEventListener('click', this.nextImage.bind(this));
        this.$prevButton.addEventListener('click', this.prevImage.bind(this));
    }

    nextImage() {
        if(this.current+1 == this.totalImages) return; 
        this.current++;
        this.updateImage();
    }

    prevImage() {
        if(this.current == 0) return; 
        this.current--;
        this.updateImage();
    }

    updateImage() {
        this.$image.src = this.images[this.current];
        this.$currentPicture.innerText = this.current+1;
    }

    preload(i) {
        for(let x=0; x<i.length; x++) {
            let img = new Image();
            img.src = i[x];
        }
    }
}

customElements.define('slide-show', SlideShow);

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

this.images = this.getAttribute('images').split(',').map(i => i.trim());

Это бит, который позволяет мне добавлять разрывы строк и прочее вокруг URL-адресов. Мне это очень нравится, так как разработчику намного проще использовать тег.

Пользовательский опыт FTW!

Говоря об опыте, я добавил функцию preload, которая автоматически загружает все изображения. Теоретически это сделает слайд-шоу более быстрым, когда пользователь перемещается по изображениям. Я не жду, пока он завершится, что, по моему мнению, является хорошим компромиссом между попыткой загрузить что-то раньше и предоставлением пользователю возможности навигации, как только он захочет.

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

Это большая часть конструктора. В обработчике событий connectedBacllback я добавляю свои прослушиватели событий к кнопкам, стараясь правильно привязать область this, и я полностью не испортил это в первый раз. , честный. (Я сделал совершенно другую ошибку.) Обработчики событий выполняют базовые проверки «конца диапазона» и просто обновляют значение для текущего изображения, а затем связываются с updateImage для обновления DOM.

Вы можете увидеть все это в действии ниже:

https://codepen.io/cfjedimaster/pen/eYjygxN?embedable=true

Исходный код этой демонстрации находится в моем репозитории GitHub здесь

Итак, позвольте мне оставить вас с несколькими примечаниями.

* В моей первоначальной версии я использовал getAttribute и setAttribute с именем атрибута, написанным в верблюжьем регистре. Вы не можете этого сделать. У меня был currentImage, который не является допустимым атрибутом веб-компонента. Я помню это сейчас, но не знал, когда строил.

* Я упомянул, что Саймон помог мне, и он также создал свою собственную версию. (Его версия была собрана до того, как я добавил текст.) Вы можете найти ее здесь.

* В этом компоненте чего-то не хватает, что мне нужно добавить, и я сделаю это в следующем посте. Должна быть возможность изменять изображения с помощью JavaScript. Теоретически вы можете изменить их сейчас, но это ничего не изменит. Веб-компоненты определенно позволяют вам поддерживать это, и я собираюсь создать вторую версию, чтобы продемонстрировать это! (И дайте мне повод снова вести блог!)


Также опубликовано здесь


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE