Пакет подключаемых модулей|Инкапсулируйте собственный подключаемый модуль карусели - переключение левой и правой версии

JavaScript
Пакет подключаемых модулей|Инкапсулируйте собственный подключаемый модуль карусели - переключение левой и правой версии

предыдущий постКейс | Родной почерк карусель - блеклая и блеклая версияЕсть еще много недостатков, я хотел бы поблагодарить вас большое здесьcsdokerЦенные мнения и рекомендации даны 🙏, поэтому автор решил повторно улучшить случай карты карусели и планирует сделать простую версию пакета плагина левой и правой карты карусели;

1. Предисловие к упаковке подключаемых модулей (основы упаковки)

карта разума

(1) Идея сменной упаковки

1. Идея построения гибкой платформы

Чтобы инкапсулировать плагин, в первую очередь необходимо проанализировать, проанализировать конечный эффект и элементы конфигурации, которые могут поддерживаться.

2. Основание:

  • => накопление опыта
  • => Исследуйте отличные плагины других людей 😄

3. Цель:

Цель написания плагинов — облегчить дальнейшую разработку, и они должны использоваться большим количеством людей (с открытым исходным кодом).

4. Требования:

  • -> Professional, производительность плагинов, которые мы пишем, лучше (код лучше)
  • -> Простота в использовании, другие очень просты в использовании
  • -> Мощный, насколько это возможно в случае лаконичного кода, поддержка более мощных эффектов
  • -> Гибкая, пишите как можно больше на основе родных JS, не полагайтесь на другие библиотеки или контент (то есть приносят прагматизм)
  • -> Расширяемый, обновляемый, учитывайте обратную совместимость более низкой версии при обновлении и не слишком далеко от исходной версии
  • ......так далее

(2) Идея инкапсуляции левого и правого плагинов карусели

Инкапсуляция плагинов, компонентов, библиотек классов и т. д. в основном основана на объектно-ориентированном мышлении.

  • Объектно-ориентированное мышление может гарантировать отсутствие связи между каждым экземпляром.
  • Основная цель объектно-ориентированного подхода: инкапсуляция подключаемых компонентов.
  • Плагин, который мы инкапсулируем, представляет собой класс
    • Изображение карусели, реализованное на основе каждого вызова плагина, является экземпляром созданного класса.
  • Для каруселей:
    • 1. Основная основная информация должна быть конфиденциальной
    • 2. Некоторые методы работы должны быть общедоступными

(3) Формальная обработка параметров в пакете подключаемых модулей

Способ 1: установите его как формальный параметр и передавайте по одному

  • Функции:
    • -> Порядок, в котором передаются фактические параметры, должен соответствовать порядку, в котором устанавливаются формальные параметры.
    • -> Нехорошо устанавливать значения по умолчанию для формальных параметров
    • -> Если некоторые предметы в середине не будут переданы, предметы, переданные позже, будут перемещены
  • используемые сцены:
    • Как правило, эта ситуация используется только в том случае, если параметров очень мало (обычно не более двух).

Способ 2: обработка на основе пар ключ-значение объекта

  • Функции:
    • -> может быть пройдено или не пройдено
    • -> Порядок доставки также произвольный, потому что все они отмечены на основе имени атрибута, пока атрибут и значение соответствуют, порядок не имеет значения
    • -> легко расширяется
    • -> Для информации, которая не передается, мы устанавливаем для нее значение по умолчанию
  • используемые сцены:
    • В случае передачи нескольких параметров мы обычно имеем дело с парами ключ-значение объекта.

Во-вторых, пакет карты карусели - HTML

В этой части мы можем написать небольшую полку, и пользователи могут напрямую CV, когда они ее используют, заменять свои собственные изображения и настраивать некоторые свои детали;Адрес склада

<!-- 轮播图整体容器 -->
<div class="xiaozhima-container">
    <!-- 所有图片容器 -->
    <div class="xiaozhima-wrapper">
        <!-- 每个图片容器 -->
        <div class="xiaozhima-slide">
            <!-- 插入您的图片 -->
            <img src=" " alt="">
        </div>
        <div class="xiaozhima-slide">
            <img src=" " alt="">
        </div>
        <div class="xiaozhima-slide">
            <img src=" " alt="">
        </div>
        <div class="xiaozhima-slide">
            <img src=" " alt="">
        </div>
    </div>
    <!-- 分页器容器 -->
    <div class="xiaozhima-pagination"></div>
    <!-- 左右按钮容器 -->
    <a href="javascript:;" class="xiaozhima-arrow-prev"></a>
    <a href="javascript:;" class="xiaozhima-arrow-next"></a>
</div>

3. Инкапсуляция карусели — CSS

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

1. Как использовать:

  • импортировать по имениbanner-plugin.cssТаблица стилей может быть;Адрес склада(Вы также можете сами создать файл CSS и вставить код прямо в него);

2. Что нужно настроить пользователю:

  • Общий контейнер карусели:
    • width
    • height
  • Вставить в свою картинку: (можно просто загрузить картинку не меняя ее)
    • Картины и стили картинок

3. Стили, которые нужно динамически вычислять в JS

  • Для всех контейнеров изображений:

    • ширина : По количеству СЛАЙДОВ (включая клоны) * ширина контейнера КОНТЕЙНЕРА
    • переход: устанавливается в JS
    • слева: какой СЛАЙД отображается изначально, ЛЕВОЕ значение должно быть рассчитано
  • Для каждого контейнера изображения:

    • ширина: должна соответствовать ширине КОНТЕЙНЕРА, переданного пользователем (обрабатывается в JS).
  • Левая и правая кнопки: (изображения Пекина конвертируются в кодировку BASE64)

    • Лучше не использовать картинки для стилевых ресурсов в плагине (т.к. это требует дополнительного импорта пользователем, что не способствует продвижению и использованию плагина) => Если вам нужно использовать картинки, лучше установить картинки в кодировку BASE64

    • Интернет-сайт, который конвертирует изображения в кодировку BASE64:tool.css-js.com/base64.html

4. Специальный код CSS

.xiaozhima-container {
	/* 
	 * 需要用户后期自己配置的样式
	 *   width
	 *   height
	 */
	position: relative;
	box-sizing: border-box;
	width: 100%;
	overflow: hidden;
}

.xiaozhima-container .xiaozhima-wrapper {
	/* 
	 * 需要在JS中动态计算的样式
	 *   width 根据SLIDE的个数(含克隆的)* CONTAINER容器的宽度
	 *   transition 是在JS中设置的
	 *   left 初始展示哪一个SLIDE,LEFT值就应该是计算好的
	 */
	display: flex;
	box-sizing: border-box;
	position: absolute;
	top: 0;
	height: 100%;
}

.xiaozhima-container .xiaozhima-wrapper .xiaozhima-slide {
	/*
	 * 需要用户后期自己配置的样式
	 *   width 和CONTAINER的宽度保持一致(JS中处理)
	 */
	box-sizing: border-box;
	height: 100%;
	overflow: hidden;
}

.xiaozhima-container .xiaozhima-wrapper .xiaozhima-slide img {
	display: block;
	width: 100%;
	height: 100%;
}

.xiaozhima-container .xiaozhima-pagination {
	position: absolute;
	bottom: 10px;
	left: 50%;
	z-index: 999;
	transform: translateX(-50%);
	padding: 3px 6px;
	background: rgba(255, 255, 255, .3);
	font-size: 0;
	border-radius: 10px;
}

.xiaozhima-container .xiaozhima-pagination span {
	display: inline-block;
	margin: 0 6px;
	width: 10px;
	height: 10px;
	border-radius: 50%;
	background: lightblue;
	cursor: pointer;
}

.xiaozhima-container .xiaozhima-pagination span.active {
	background: lightcoral;
}

.xiaozhima-arrow-prev,
.xiaozhima-arrow-next {
	display: none;
	position: absolute;
	z-index: 999;
	top: 50%;
	margin-top: -22.5px;
	width: 30px;
	height: 45px;
	/* 插件中的样式资源最好不要使用图片(因为这样需要用户额外的导入,不利于插件的推广和使用) => 需要使用图片,我们最好把图片设置为BASE64编码 */
	background: url("data:image/png;base64,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") no-repeat 0 0;
}

.xiaozhima-arrow-prev {
	left: 0;
}

.xiaozhima-arrow-next {
	right: 0;
	background-position: -50px 0;
}

.xiaozhima-container:hover .xiaozhima-arrow-prev,
.xiaozhima-container:hover .xiaozhima-arrow-next {
	display: block;
}

В-четвертых, пакет карты карусели — JS (ключ)

(1), первый шаг - анализ функций и элементов конфигурации

1. Цель:

  • пакет одинbannerPluginспособ, который позволяет указанному контейнеру добиться эффекта левого и правого варианта карусели;

2. Используйте синтаксис:

  • bannerPligin([container],[options]) ;

3. Описание параметра:

  • контейнер: этикетка контейнера
  • options: требуются элементы конфигурации (много параметров настраивается пользователем, поэтому мы используемТипы данных объекта)

Подробное объяснение элемента конфигурации параметров параметров

(2), второй шаг - инициализация параметра

Выше мы упоминали, что есть два способа обработки параметров.Здесь мы выбираем обработку их на основе пар ключ-значение объекта;

Здесь мы используем метод глубокого сравнения и итерации цикла, который мы создали вчера (Глубокое клонирование VS поверхностное клонирование | Глубокое сравнение VS поверхностное сравнение | Функции обратного вызова) и методы определения типа данных, статьяПреимущества и недостатки четырех методов определения типа данных в JSупомянуто в конце, я положил его здесь первым в виде комплекта

1. Инструментарий

Адрес складаВы также можете напрямую резюме

/* 工具包 */
(function () {
    // 数据类型检测
    let class2type = {};
    ["Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Object", "Error", "Symbol"].forEach(item => {
        class2type["[object " + item + "]"] = item.toLowerCase();
    });

    function toType(obj) {
        if (obj == null) return obj + "";
        return typeof obj === "object" || typeof obj === "function" ?
            class2type[class2type.toString.call(obj)] || "object" :
            typeof obj;
    }

    // 检测是否为函数
    function isFunction(obj) {
        return typeof obj === "function" && typeof obj.nodeType !== "number";
    }

    // 检测window
    function isWindow(obj) {
        return obj != null && obj === obj.window;
    }

    // 数组/类数组
    function isArrayLike(obj) {
        var length = !!obj && "length" in obj && obj.length,
            type = toType(obj);
        if (isFunction(obj) || isWindow(obj)) return false;
        return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj;
    }

    // 循环迭代
    function _each(obj, callback, context = window) {
        if (/^(ARRAY|OBJECT)$/i.test(obj.constructor)) {
            obj = _cloneDeep(obj);
        }
        if (isArrayLike(obj)) {
            for (let i = 0; i < obj.length; i++) {
                let res = callback && callback.call(context, obj[i], i);
                if (res === false) break;
                if (res !== undefined) obj[i] = res;
            }
        } else {
            for (let key in obj) {
                if (!obj.hasOwnProperty(key)) break;
                let res = callback && callback.call(context, obj[key], key);
                if (res === false) break;
                if (res !== undefined) obj[key] = res;
            }
        }
        return obj;
    }

    // 深克隆
    function _cloneDeep(obj) {
        if (obj === null) return null;
        if (typeof obj !== "object") return obj;
        if (obj instanceof RegExp) return new RegExp(obj);
        if (obj instanceof Date) return new Date(obj);
        let cloneObj = new obj.constructor;
        for (let key in obj) {
            if (!obj.hasOwnProperty(key)) break;
            cloneObj[key] = _cloneDeep(obj[key]);
        }
        return cloneObj;
    }

    // 深比较
    function _assignDeep(obj1, obj2) {
        let obj = _cloneDeep(obj1);
        for (let key in obj2) {
            if (!obj2.hasOwnProperty(key)) break;
            let v2 = obj2[key],
                v1 = obj[key];
            if ((v1 !== null && typeof v1 === "object") && (v2 !== null && typeof v2 === "object")) {
                obj[key] = _assignDeep(v1, v2);
                continue;
            }
            obj[key] = v2;
        }
        return obj;
    }

    ['_each', '_cloneDeep', '_assignDeep', 'toType', 'isFunction', 'isWindow', 'isArrayLike'].forEach(item => {
        window[item] = eval(item);
    });
})();

2. Цель инициализации параметра

  • Настройте информацию о параметре по умолчанию и перейдите к информации по умолчанию, если пользователь не передает соответствующий параметр.
function bannerPlugin(container, options = {}) {
    // 参数初始化:默认配置信息
    let defaultParams = {
        initialSlide: 0,
        autoplay: 3000,
        speed: 300,
        pagination: {
            el: '.xiaozhima-pagination',
            clickable: true
        },
        navigation: {
            nextEl: '.xiaozhima-arrow-next',
            prevEl: '.xiaozhima-arrow-prev'
        },
        on: {
            init() {},
            transitionStart() {},
            transitionEnd() {}
        }
    };
    // 利用深比较,把用户传递进来的参数和默认配置合并(达到替换默认配置的效果)
    options = _assignDeep(defaultParams, options);

    // 判断用户传递进来的第一个参数是否为元素容器,支持直接传递标签
    typeof container === "string" ? container = document.querySelector(container) : null;
    if (!container || container.nodeType !== 1) {
        throw new TypeError('container must be an element!');
    }  
    return new Banner(container, options);
}

(3), третий шаг - настроить приватные свойства каждой карусели

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

  • Когда пользователь использует его, каждая информация о конфигурации должна быть частной для текущей карусели, поэтому настроенные нами параметры по умолчанию должны быть частными свойствами каждого экземпляра.
class Banner {
    constructor(container, options) {
        // 把传递进来的信息都挂载到当前类的实例上
        // 1.信息都作为他的私有属性(这样每一个实例之间互不影响)
        // 2.挂载到实例上,以后在当前类的其它方法中,只要保证THIS是实例,都可以基于THIS.XXX获取和操作
        _each(options, (item, key) => {
            this[key] = item;
        });
        this.container = container;
        this.activeIndex = this.initialSlide;
    }
}

(4), четвертый шаг - настроить публичный метод (ключ)

  • Выше мы добавили приватные свойства к каждому экземпляру карусели, а следующие общедоступные методы являются ключевым содержимым.

1. Вычислительная структура и функции стиля

  • После завершения каждой карусели необходимой операцией является получение элемента
computed() {
    let {
        container,
        pagination,
        navigation
    } = this;

    // 轮播图
    this.wrapper = container.querySelector('.xiaozhima-wrapper');
    this.slidesTrue = container.querySelectorAll('.xiaozhima-slide');
    // 克隆第一张到容器的末尾
    this.wrapper.appendChild(this.slidesTrue[0].cloneNode(true));
    this.slides = container.querySelectorAll('.xiaozhima-slide');

    // 分页器
    this.paginationBox = null;
    this.paginationList = null;
    if (toType(pagination) === "object") {
        let el = pagination.el;
        if (el) {
            this.paginationBox = container.querySelector(el);
            // 创建SPAN
            let str = ``;
            _each(this.slidesTrue, item => {
                str += `<span></span>`;
            });
            this.paginationBox.innerHTML = str;
            this.paginationList = this.paginationBox.querySelectorAll('span');
        }
    }

    // 左右切换
    this.arrowPrev = null;
    this.arrowNext = null;
    if (toType(navigation) === "object") {
        navigation.prevEl ? this.arrowPrev = container.querySelector(navigation.prevEl) : null;
        navigation.nextEl ? this.arrowNext = container.querySelector(navigation.nextEl) : null;
    }

    // 控制元素的样式(包含初始展示哪一个)
    this.changeWidth = parseFloat(getComputedStyle(container).width);
    this.activeIndex = this.activeIndex < 0 ? 0 : (this.activeIndex > this.slides.length - 1 ? this.slides.length - 1 : this.activeIndex);
    this.wrapper.style.width = `${this.changeWidth*this.slides.length}px`;
    this.wrapper.style.transition = `left ${this.speed}ms`;
    this.wrapper.style.left = `${-this.activeIndex*this.changeWidth}px`;
    _each(this.slides, item => {
        item.style.width = `${this.changeWidth}px`;
    });
}

2, функция переключения, реализованная на фиг.8

// 实现轮播图切换
change(now = false) {
    //now 传值是立即切换,不传是默认的有动画切换
    let {
        wrapper,
        speed,
        activeIndex,
        changeWidth,
        on
    } = this;
    let isO = toType(on) === "object" ? true : false,
        transitionStart = isO ? on.transitionStart : null,
        transitionEnd = isO ? on.transitionEnd : null;
    
    // 切换之前触发的钩子函数
    !now && transitionStart ? transitionStart.call(this, this) : null;

    // 如果传了now 立即切换,则不需要有动画
    wrapper.style.transitionDuration = `${now?0:speed}ms`;
    wrapper.style.left = `${-activeIndex*changeWidth}px`;
    if (now) {
        // 如果立即切换我需要让上面先渲染一次,利用读写分离;
        wrapper.offsetWidth;
    } 

    // 切换之后触发的钩子函数
    let fn = () => {
        !now && transitionEnd ? transitionEnd.call(this, this) : null;
        // 每一次都会重新监听,所以监听完需要把上一次监听的移除掉
        wrapper.removeEventListener('transitionend', fn);
    };
    wrapper.addEventListener('transitionend', fn);
}

3. Функция автоматического вращения

// 自动轮播
autoMove() {
    if (this.activeIndex === this.slides.length - 1) {
        this.activeIndex = 0;
        this.change(true);
    }
    this.activeIndex++;
    this.change();
}

4, функция выравнивания фокуса

// 实现焦点对齐
autoFocus() {
    let {
        paginationList,
        activeIndex,
        slides
    } = this;
    if (!paginationList) return;
    activeIndex === slides.length - 1 ? activeIndex = 0 : null;
    _each(paginationList, (item, index) => {
        if (index === activeIndex) {
            item.className = 'active';
            return;
        }
        item.className = '';
    });
}

(5) Шаг 5 — Последовательность выполнения и условия общедоступных методов (выделено)

  • Теперь мы инкапсулировали метод для реализации функции карусели, но этот метод мертв, и ценно только его выполнение.
  • В настоящее время мы должны учитывать, что, хотя мы создали эти методы, параметры, переданные пользователем, могут не нуждаться в их использовании, поэтому нам также необходимо судить, нужны ли они пользователю, и если пользователю это нужно, то что наш исполнительный лист? ;
init() {
    // THIS:当前类的实例
    // 入口,在这里控制代码执行的逻辑顺序
    
    // 先基于它获取元素后,才可以获取实例上的信息
    this.computed();

    let {
        autoplay,
        autoMove,
        container,
        pagination,
        paginationList,
        arrowNext,
        arrowPrev
    } = this;

    // 控制是否自动切换
    if (autoplay) {
        this.autoTimer = setInterval(autoMove.bind(this), autoplay);
        // 箭头函数中的THIS是上下文中的,也就是实例
        container.onmouseenter = () => clearInterval(this.autoTimer);
        container.onmouseleave = () => this.autoTimer = setInterval(autoMove.bind(this), autoplay);
    }

    // 控制焦点切换
    if (toType(pagination) === "object" && pagination.clickable === true && paginationList) {
        _each(paginationList, (item, index) => {
            item.onclick = () => {
                let {
                    activeIndex,
                    slides
                } = this;
                if ((index === activeIndex) || (index === 0 && activeIndex === slides.length - 1)) return;
                this.activeIndex = index;
                this.change();
            };
        });
    }

    // 控制左右按钮切换
    arrowNext ? arrowNext.onclick = autoMove.bind(this) : null;
    arrowPrev ? arrowPrev.onclick = () => {
        if (this.activeIndex === 0) {
            this.activeIndex = this.slides.length - 1;
            this.change(true);
        }
        this.activeIndex--;
        this.change();
    } : null;

    // 初始化完成,触发INIT回调函数
    if (toType(this.on) === "object" && isFunction(this.on.init)) {
        // 把回调函数执行,让方法中的THIS是实例,并且传递的第一个参数也是实例
        this.on.init.call(this, this);
    }
}

(6), шестой шаг - оптимизация интеграции (ключ)

Резюме приведенного выше кода JS

  • Вы можете использовать JS для заливки;Адрес склада
    • Автор не указал здесь адрес, и вам также необходимо создатьbanner-plugin.jsJS файл, вставьте код прямо в него;
(function () {
    class Banner {
        constructor(container, options) {
            // 把传递进来的信息都挂载到当前类的实例上
            // 1.信息都作为他的私有属性(这样每一个实例之间互不影响)
            // 2.挂载到实例上,以后在当前类的其它方法中,只要保证THIS是实例,都可以基于THIS.XXX获取和操作
            _each(options, (item, key) => {
                this[key] = item;
            });
            this.container = container;
            this.activeIndex = this.initialSlide;
            this.init();
        }
        init() {
            // THIS:当前类的实例
            // 入口,在这里控制代码执行的逻辑顺序

            // 先基于它获取元素后,才可以获取实例上的信息
            this.computed();

            let {
                autoplay,
                autoMove,
                container,
                pagination,
                paginationList,
                arrowNext,
                arrowPrev
            } = this;

            // 控制是否自动切换
            if (autoplay) {
                this.autoTimer = setInterval(autoMove.bind(this), autoplay);
                // 箭头函数中的THIS是上下文中的,也就是实例
                container.onmouseenter = () => clearInterval(this.autoTimer);
                container.onmouseleave = () => this.autoTimer = setInterval(autoMove.bind(this), autoplay);
            }

            // 控制焦点切换
            if (toType(pagination) === "object" && pagination.clickable === true && paginationList) {
                _each(paginationList, (item, index) => {
                    item.onclick = () => {
                        let {
                            activeIndex,
                            slides
                        } = this;
                        if ((index === activeIndex) || (index === 0 && activeIndex === slides.length - 1)) return;
                        this.activeIndex = index;
                        this.change();
                    };
                });
            }

            // 控制左右按钮切换
            arrowNext ? arrowNext.onclick = autoMove.bind(this) : null;
            arrowPrev ? arrowPrev.onclick = () => {
                if (this.activeIndex === 0) {
                    this.activeIndex = this.slides.length - 1;
                    this.change(true);
                }
                this.activeIndex--;
                this.change();
            } : null;

            // 初始化完成,触发INIT回调函数
            if (toType(this.on) === "object" && isFunction(this.on.init)) {
                // 把回调函数执行,让方法中的THIS是实例,并且传递的第一个参数也是实例
                this.on.init.call(this, this);
            }
        }


        // 计算结构和样式
        computed() {
            let {
                container,
                pagination,
                navigation
            } = this;

            // 轮播图
            this.wrapper = container.querySelector('.xiaozhima-wrapper');
            this.slidesTrue = container.querySelectorAll('.xiaozhima-slide');
            // 克隆第一张到容器的末尾
            this.wrapper.appendChild(this.slidesTrue[0].cloneNode(true));
            this.slides = container.querySelectorAll('.xiaozhima-slide');

            // 分页器
            this.paginationBox = null;
            this.paginationList = null;
            if (toType(pagination) === "object") {
                let el = pagination.el;
                if (el) {
                    this.paginationBox = container.querySelector(el);
                    // 创建SPAN
                    let str = ``;
                    _each(this.slidesTrue, item => {
                        str += `<span></span>`;
                    });
                    this.paginationBox.innerHTML = str;
                    this.paginationList = this.paginationBox.querySelectorAll('span');
                }
            }

            // 左右切换
            this.arrowPrev = null;
            this.arrowNext = null;
            if (toType(navigation) === "object") {
                navigation.prevEl ? this.arrowPrev = container.querySelector(navigation.prevEl) : null;
                navigation.nextEl ? this.arrowNext = container.querySelector(navigation.nextEl) : null;
            }

            // 控制元素的样式(包含初始展示哪一个)
            this.changeWidth = parseFloat(getComputedStyle(container).width);
            this.activeIndex = this.activeIndex < 0 ? 0 : (this.activeIndex > this.slides.length - 1 ? this.slides.length - 1 : this.activeIndex);
            this.wrapper.style.width = `${this.changeWidth * this.slides.length}px`;
            this.wrapper.style.transition = `left ${this.speed}ms`;
            this.wrapper.style.left = `${-this.activeIndex * this.changeWidth}px`;
            _each(this.slides, item => {
                item.style.width = `${this.changeWidth}px`;
            });
            this.autoFocus();
        }


        // 自动轮播
        autoMove() {
            if (this.activeIndex === this.slides.length - 1) {
                this.activeIndex = 0;
                this.change(true);
            }
            this.activeIndex++;
            this.change();
        }

        // 实现轮播图切换
        change(now = false) {
            //now 传值是立即切换,不传是有动画切换
            let {
                wrapper,
                speed,
                activeIndex,
                changeWidth,
                on
            } = this;
            let isO = toType(on) === "object" ? true : false,
                transitionStart = isO ? on.transitionStart : null,
                transitionEnd = isO ? on.transitionEnd : null;
            // 切换之前触发的钩子函数
            !now && transitionStart ? transitionStart.call(this, this) : null;

            // 如果传了now 立即切换,则不需要有动画
            wrapper.style.transitionDuration = `${now ? 0 : speed}ms`;
            wrapper.style.left = `${-activeIndex * changeWidth}px`;
            if (now) {
                // 如果立即切换我需要让上面先渲染一次,利用读写分离;
                wrapper.offsetWidth;
            } else {
                this.autoFocus();
            }

            // 切换之后触发的钩子函数
            let fn = () => {
                !now && transitionEnd ? transitionEnd.call(this, this) : null;
                // 每一次都会重新监听,所以监听完需要把上一次监听的移除掉
                wrapper.removeEventListener('transitionend', fn);
            };
            wrapper.addEventListener('transitionend', fn);
        }
        // 实现焦点对齐
        autoFocus() {
            let {
                paginationList,
                activeIndex,
                slides
            } = this;
            if (!paginationList) return;
            activeIndex === slides.length - 1 ? activeIndex = 0 : null;
            _each(paginationList, (item, index) => {
                if (index === activeIndex) {
                    item.className = 'active';
                    return;
                }
                item.className = '';
            });
        }
    }

    function bannerPlugin(container, options = {}) {
        let defaultParams = {
            initialSlide: 0,
            autoplay: 3000,
            speed: 300,
            pagination: {
                el: '.xiaozhima-pagination',
                clickable: true
            },
            navigation: {
                nextEl: '.xiaozhima-arrow-next',
                prevEl: '.xiaozhima-arrow-prev'
            },
            on: {
                init() { },
                transitionStart() { },
                transitionEnd() { }
            }
        };
        options = _assignDeep(defaultParams, options);

        typeof container === "string" ? container = document.querySelector(container) : null;
        if (!container || container.nodeType !== 1) {
            throw new TypeError('container must be an element!');
        }

        return new Banner(container, options);
    }

    window.bannerPlugin = bannerPlugin;
})();

5. Инструкция по применению

  • стиль, который должен быть установлен
    • Ширина и высота большого контейнера
    • Ваша собственная фотография
  • Синтаксис использования JS
    • BannerPlugin (большой контейнер, информация о параметрах требуется пользователям)
      • контейнер: этикетка контейнера
      • опции: необходимые элементы конфигурации
        • Информацию о параметрах см. в разделе Описание информации о параметрах.

Автор тоже постоянно учится и пробует на фронтенде.Если что-то не так в содержании текста,просветите,буду очень признателен🙏;

Автор хочет и дальше развиваться в Наггетс.На данном этапе цель поднята на 3 уровень.Надеюсь у всех получится😄,спасибо большое;