Нативная JS-реализация карусельной диаграммы — Глава 2 Реализация анимации

внешний интерфейс CSS
Нативная JS-реализация карусельной диаграммы — Глава 2 Реализация анимации

Недавно я увидел группу фотографий Запретного города и почувствовал себя очень атмосферно, поэтому решил использовать фотографии Запретного города для демонстрации следующим образом:

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

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

  1. Нет операции клика, картинка прокручивается сама (здесь мы оговариваем прокрутку справа налево);
  2. Нажимайте стрелки влево и вправо, чтобы перевернуть картинку;
  3. Щелкните точку подсказки, чтобы отобразить разные изображения;
  4. Функциональная интеграция.

Автоматическая прокрутка петли

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

Введение в таймеры

  • setInterval()Метод многократно вызывает функцию или выполняет сегмент кода с фиксированной задержкой по времени между каждым вызовом, возвращаяintervalID, который можно передать в качестве параметраclearInterval()чтобы отменить таймер.

  • setTimeout()Метод устанавливает таймер, и когда таймер истекает, выполняется функция или фрагмент кода и возвращается возвращаемое значение.timeoutID- положительное целое число, представляющее номер таймера. Это значение можно передатьclearTimeout()чтобы отменить таймер.

setInterval( )

let intervalID = window.setInterval(func, delay);
  • intervalIDявляется уникальным идентификатором для этой повторяющейся операции и может быть передан в качестве параметра вclearInterval().
  • funcэто функция, которую вы хотите вызывать повторно.

setTimeout( )

var timeoutID = scope.setTimeout(function, delay); 
  • functionэто функция, которую вы хотите выполнить после миллисекунд задержки.
  • Количество миллисекунд задержки (одна секунда равна 1000 миллисекундам), после которой произойдет вызов функции.

тематическое исследование

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

Давайте сначала рассмотрим иерархические отношения между каждым контейнером:

<!--最外层的父级容器-->
    <div class="imgScroll">
        <!--图片展示区-->
        <div class="showContainer">
            <div class="container" style="left: 0px;">
                <img src="https://dimg02.c-ctrip.com/images/100b11000000qezw729A4_R_1600_10000_Mtg_7.jpg" alt="A cat">
                <img src="https://dimg05.c-ctrip.com/images/100u0x000000le38uA71D_R_1600_10000_Mtg_7.jpg" alt="A dog">
                <img src="https://dimg08.c-ctrip.com/images/100811000000qrlfxA3E0_R_1600_10000_Mtg_7.jpg" alt="dandelion">
            </div>
        </div>
        <!--底部提示区-->
        <div class="dots">
            <span class="dot active"></span>
            <span class="dot"></span>
            <span class="dot"></span>
        </div>
        <!--左右箭头-->
        <div class="left-triangle triangle">
            <span>&lt;</span>
        </div>
        <div class="right-triangle triangle">
            <span>&gt;</span>
        </div>
    </div>

внешний слойdiv.imgScrollявляется родительским контейнером всей реализации, его дочерние элементыdiv.showContainerконтейнер отображения, внутренний слойdiv.ontainerЯвляется ли контейнер прокрутки, контейнер прокрутки абсолютно позиционирован относительно контейнера отображения, тогда функция перемещения заключается в перемещении контейнера прокрутки, то есть для изменения контейнера прокруткиstyle.leftАтрибуты.

Функция перемещения --movefunc()

Выше мы оговорили, что картинка должна прокручиваться справа налево, то есть контейнер прокрутки каждый раз перемещается на ширину одной картинки влево (переменная записывается какoneImgWidth)которыйdiv.container.style.leftуменьшить на одинoneImgWidth.

Первое, что нужно сделать, это получить контейнер прокрутки и его начальное состояние. Родной метод:getElementsByClassName()а такжеgetElementById()может быть полученDOMэлементы в документе, вhtmlне используется в структуреidДля представления элементов используется первый, но следует отметить, что этот метод даетколлекция элементовHTMLCollection, первый элемент должен быть получен индексацией:

var container = document.getElementsByClassName("container")[0];    //获取滚动容器
var oldLeftPos = parseInt(container.style.left);    //获取滚动容器的初始状态,并利用parseInt()方法取整

Примечание: в приведенном вышеhtmlструктура, вы заметите, что есть встроенный стильstyle="left: 0px;", это для предотвращенияstyle.leftСообщается об ошибке, когда значение не может быть получено.

Затем получите ширину изображения, которая представляет собой размер шага каждого движения контейнера прокрутки:

var oneimgWidth = container.children[0].offsetWidth;    //这是一个整数

Прокрутите контейнер влево:

var newLeftPos = oldLeftPos - oneimgWidth;
container.style.left = newLeftPos + "px";   //记得加上单位px

Это достигает эффекта движения--функция перемещения:

  function movefunc() {
    var container = document.getElementsByClassName("container")[0];    //获取滚动容器
    var oneImgWidth = container.children[0].offsetWidth;    //获取一张图片的宽度,即每次左移的距离
    var oldLeft = parseInt(container.style.left);    //获取滚动容器的初始的左距离,利用parseInt()方法取整
    var newLeft = oldLeft - oneImgWidth;
    container.style.left = newLeft + "px";      //改变滚动容器的左距离
  }

Функция автоматической прокрутки --rollAuto()

После реализации функции перемещения мы запускаем первый анимационный эффект реальной реализации изображения карусели ——Автоматическая прокрутка петли, основное использование таймеров было представлено ранее, и теперь мы начинаем использовать таймеры и функции движения для достижения кругового движения для формирования анимации:

СоздаватьrollAuto()Функция таймера:

function rullAuto() {
  setInterval("movefunc()", 800);   //每隔800毫秒执行一次移动函数
}
rullAuto();     //需要先执行一次定时器函数,才可以开始定时器

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

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

var timer = setInterval("movefunc()", 800);   //每隔800毫秒执行一次移动函数
clearInterval(timer);   //清除使移动函数循环执行的定时器

Движение мыши вызовет событие события мыши, например, здесь движение мыши вызоветonmouseoverсобытие, отключение мыши вызоветonmouseoutсобытие, мы можем привязать эти два события к элементу области отображения:

var showContainer = document.getElementsByClassName("showContainer")[0];     //获取展示区元素
//绑定鼠标事件
showContainer.onmouseover = function() {
    clearInterval(timer);
};
showContainer.onmouseout = function() {
    rullAuto();   //鼠标移出,继续执行移动函数
};

Наконец, давайте приведем в порядок эту функцию с автоматической прокруткой:

function autoRullImg() {
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var timer = null;
    function rullAuto() {
        timer =  setInterval("movefunc()", 800); 
    }
    rullAuto();
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };
}

Таким образом, он используется как полноценная функция, которая вас точно разочарует, после прокатки картинка исчезнет.containerизstyle.leftвсе еще снижается. Вы обнаружите, что это не тот результат, который нам нужен. Мы надеемся, что когда последнее изображение выйдет за пределы области отображения, должно появиться первое изображение, и тогда здесь необходимо будет принять решение о предельном условии.

var container = document.getElementsByClassName("container")[0];    //获取滚动容器
var imgNum = container.children.length;     //获取图片数量
var oneImgWidth = container.children[0].offsetWidth;    //获取每张图的宽度(CSS样式设置中,每张图宽带一样)
//如果最后一张图移出展示区,初始化滚动容器的.style.left属性值
if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
    container.style.left = "0px";
}

Окончательная интегрированная версия выглядит следующим образом:

function autoRullImg() {
    var timer = null;
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var container = document.getElementsByClassName("container")[0];  
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;  

    function rullAuto() {
        timer =  setInterval(function(){
            movefunc();
            if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
                container.style.left = "0px";
            }
        }, 800); 
    }
    rullAuto();
    
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };
}

Нажимайте стрелки влево и вправо, чтобы перевернуть изображение

Щелкните стрелку влево, контейнер прокрутки должен переместиться вправо; щелкните стрелку вправо, контейнер прокрутки переместится влево, здесь есть проблема, наша функция перемещения состоит в перемещенииcontainer.style.leftУменьшение ширины изображения, то есть перемещение изображения влево, имеет тот же эффект, что и функция автопрокрутки и щелчок по стрелке вправо.

Но щелчок по стрелке влево указывает направление, поэтому наша предыдущая функция перемещенияmovefuncЕго нужно изменить. Чтобы различать две функции, мы теперь переименуем улучшенную функцию перемещения вmovement(), больше не просто перемещайте изображение, если другие элементы перемещаются, вы можете повторно использовать функцию.

Многоразовая функция движения --movement()

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

  function movement(offset) {
    var container = document.getElementsByClassName("container")[0]; 
    var oldLeft = parseInt(container.style.left); 
    var newLeft = oldLeft + offset;
    container.style.left = newLeft + "px";
  }

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

Так,autoRullImg()В функции исходная функция перемещения должна быть заменена новой функцией перемещения, и в нее передается отрицательное значение:

movement(-oneImgWidth);

Привязать события щелчка к левой и правой стрелкам:

Также перед этим вам нужно сначала получить элемент:

var leftTriangle = document.getElementsByClassName("left-triangle")[0];         //左箭头
var rightTriangle = document.getElementsByClassName("right-triangle")[0];       //右箭头

Нажмите стрелку вправо здесь,containerДвигайтесь влево; нажмите стрелку влево,containerПереместитесь вправо и привяжите события кликов к стрелкам влево и вправо:

leftTriangle.onclick = function() {
    movement(oneImgWidth);
};
rightTriangle.onclick = function() {
    movement(-oneImgWidth);
};

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

function clickTriangle() {
    var container = document.getElementsByClassName("container")[0]; 
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;
    leftTriangle.onclick = function() {
        if (parseInt(container.style.left) >= 0) {
            container.style.left = -(imgNum - 1) * oneImgWidth + "px";
        } else {
            movement(oneImgWidth);     
        }
    };
    rightTriangle.onclick = function() {
        if (parseInt(container.style.left) <= -(imgNum - 1) * oneImgWidth) {
            container.style.left = "0px";
        } else {
            movement(-oneImgWidth);
        }
    };
}

Нажмите точку подсказки, чтобы отобразить разные изображения

Свяжитесь кнопки «События» к каждой подсказке:

function clickDots(){
    var container = document.getElementsByClassName("container")[0];  
    var oneImgWidth = container.children[0].offsetWidth;
    var dots = document.getElementsByClassName("dots")[0].children;     //获取提示圆点
    for(var i = 0; i < dots.length; i++){
        (function(n){
            dots[n].onclick = function(){
                container.style.left = -n*oneImgWidth + "px";
            }
        })(i);
    }
}

Поскольку переменная цикла используется в теле циклаi, Необходимо использоватьСоздайте анонимную функцию и немедленно выполните ее.способ решения проблемы закрытия.

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

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

Следовательно, должен быть маркер для обозначения каждого отображаемого изображения. Итак, мы объявляем такой токен с именемindex, используемый для идентификации отображаемого в данный момент изображения, его начальное состояние0, то есть в области отображения отображается первое изображение.

Вариационный анализ обозначений:

контейнер прокруткиdiv.containerКаждый раз, когда вы прокручиваете влево,indexдолжно увеличиться1, и когдаindexувеличился доimgNum, то есть, когда последнее изображение только что переместилось за пределы области отображения влево, должно отображаться первое изображение, т.е.indexсбросить на0; Контейнер прокруткиdiv.containerПри прокрутке вправо все наоборот.

Будь то функция автоматической прокрутки или функция события щелчка, она вызовет отметкуindexменяется, поэтому нам нужноindexОбъявление и инициализация размещаются на самом внешнем слое всех этих функций, а оценка предельных условий помещается в каждую функцию анимации.autoRullImg(),clickTriangle(),clickDots()в:

//点击向右箭头
rightTriangle.onclick = function() {
    if (parseInt(container.style.left) <= -(imgNum - 1) * oneImgWidth) {
        container.style.left = "0px";
    } else {
        movement(-oneImgWidth);
    }
    index = index + 1;
    if (index > imgNum - 1) {
        index = 0;
    }
};

//点击向左箭头
leftTriangle.onclick = function() {
    if (parseInt(container.style.left) >= 0) {
        container.style.left = -(imgNum - 1) * oneImgWidth + "px";
    } else {
        movement(oneImgWidth);     
    }
    index = index - 1;
    if (index < 0) {
        index = imgNum - 1;
    }
};

Функция автопрокрутки изменена следующим образом:

function autoRullImg() {
    var timer = null;
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var container = document.getElementsByClassName("container")[0];  
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;  

    function rullAuto() {
        timer =  setInterval(function(){
            index++;
            if (index > imgNum - 1) {
                index = 0;
            }
            movement(-oneImgWidth);
            if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
                container.style.left = "0px";
            }
        }, 800); 
        if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
            container.style.left = "0px";
        }
    }
    rullAuto();
    
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };
}

Функция выделения --showDots()

При обозначении требуется выделенная функция, согласно обозначениюindexЧтобы сделать нижнюю точку подсказки выделенной, мы сначала получаем ту, которая содержит точку подсказки.Контейнер точек, а затем получить его дочерние элементы, т.е.<span class="dot"></span>Маленькие точки, пройдитесь по этим элементам, сравните их индексы, если и знакиindexЕсли то же самое, измените точку подсказки на выделение, то есть добавьтеactiveДобрый.

    function showDots(target) {
        var dots = document.getElementsByClassName("dots")[0].children;
        for (var i = 0; i < dots.length; i++) {
            if (i !== target) {
                dots[i].classList.remove("active");       //增加表示高亮的类名
            } else {
                dots[i].classList.add("active");      //去掉高亮
            }
        }
    }

Уведомление:Поскольку нажатие на нижнюю точку подсказки сделает отметкуindexесть биение, т.е. нетindexизменения не1внутри не только+1а также-1изменений, что требует особого внимания. Функция самопрокрутки выполняется в бесконечном цикле, при нажатии на нижнюю точку подсказки значение метки удваивается, поэтому необходимоindexЗначение возвращается, чтобы его могла получить самопрокручивающаяся функция:

    function clickDots(){
        for(var i = 0; i < dots.length; i++){
            (function(n){
                dots[n].onclick = function(){
                    container.style.left = -n*oneImgWidth + "px";
                    index = n;
                    showDots(index);
                }
            })(i);
        }
        return index;       //返回记号值
    }

Функциональная интеграция

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

Чтобы избежать глобальных переменных, мы помещаем все функции и переменные в одну функцию, объявляем эту функцию с именемslideShow(), чтобы реализоватьHTMLЭта функция начинает выполняться после загрузки документа, вам нужно использоватьwindow.onload()метод:

window.onload = slideShow;

Мы неоднократно получали несколько функцийDOMэлемент, теперь мы помещаем элемент get вslideShow()Заголовок этой функции:


function autoRullImg() {
    var timer = null; 
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var container = document.getElementsByClassName("container")[0];  
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;  
    var leftTriangle = document.getElementsByClassName("left-triangle")[0];
    var rightTriangle = document.getElementsByClassName("right-triangle")[0];
    var dots = document.getElementsByClassName("dots")[0].children;     //获取提示圆点
    var index = 0;
    
    function showDots(target) {
        var dots = document.getElementsByClassName("dots")[0].children;
        for (var i = 0; i < dots.length; i++) {
            if (i !== target) {
                dots[i].classList.remove("active");       //增加表示高亮的类名
            } else {
                dots[i].classList.add("active");      //去掉高亮
            }
        }
    }

    function movement(offset) {
        var oldLeft = parseInt(container.style.left); 
        var newLeft = oldLeft + offset;
        container.style.left = newLeft + "px";
      }

    
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };

    function clickTriangle() {
        leftTriangle.onclick = function() {
            if (parseInt(container.style.left) >= 0) {
                container.style.left = -(imgNum - 1) * oneImgWidth + "px";
            } else {
                movement(oneImgWidth);     
            }
            index = index - 1;
            if( index < 0){ index = imgNum - 1;}
            showDots(index);
        };
        rightTriangle.onclick = function() {
            if (parseInt(container.style.left) <= -(imgNum - 1) * oneImgWidth) {
                container.style.left = "0px";
            } else {
                movement(-oneImgWidth);
            }
            index = index + 1;
            if( index > imgNum - 1){ index = 0;}
            showDots(index);
        };
    }
    clickTriangle() 

    function clickDots(){
        for(var i = 0; i < dots.length; i++){
            (function(n){
                dots[n].onclick = function(){
                    container.style.left = -n*oneImgWidth + "px";
                    index = n;
                    showDots(index);
                }
            })(i);
        }
        return index;
    }
    clickDots()

    function rullAuto() {
        timer =  setInterval(function(){
            movement(-oneImgWidth);
            index = index + 1;
            if( index > imgNum - 1){ index = 0;}
            showDots(index);
            if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
                container.style.left = "0px";
            }
        }, 2000); 
    }
    rullAuto();
}
window.onload = autoRullImg;

HTML-код выглядит следующим образом:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>img scroll</title>
    <link rel="stylesheet" href=".layout.css">
    <script src="./scrollImg.js"></script>
</head>

<body>
    <div class="imgScroll">
        <div class="showContainer">
            <div class="container">
                <img src="https://dimg02.c-ctrip.com/images/100b11000000qezw729A4_R_1600_10000_Mtg_7.jpg" alt="A cat">
                <img src="https://dimg05.c-ctrip.com/images/100u0x000000le38uA71D_R_1600_10000_Mtg_7.jpg" alt="A dog">
                <img src="https://dimg08.c-ctrip.com/images/100811000000qrlfxA3E0_R_1600_10000_Mtg_7.jpg" alt="dandelion">
            </div>
        </div>
        <div class="dots">
            <span class="dot active"></span>
            <span class="dot"></span>
            <span class="dot"></span>
        </div>
        <div class="left-triangle triangle">
            <span>&lt;</span>
        </div>
        <div class="right-triangle triangle">
            <span>&gt;</span>
        </div>
    </div>

</body>
</html>

Код CSS выглядит следующим образом:

.imgScroll {
  width: 800px;
  margin: 0 auto;
  position: relative;
}
.imgScroll .showContainer {
  width: 800px;
  height: 533px;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}
.imgScroll .showContainer .container {
  width: 9999px;
  position: absolute;
  left: 0px;
}
.imgScroll .showContainer .container img {
  display: block;
  float: left;
  width: 800px;
}
.imgScroll .triangle {
  position: absolute;
  top: 40%;
  cursor: pointer;
  font-size: 40px;
  color: lightgray;
}
.imgScroll .triangle:hover {
  color: gray;
}
.imgScroll .triangle.left-triangle {
  left: -50px;
}
.imgScroll .triangle.right-triangle {
  right: -50px;
}
.imgScroll .dots {
  width: 100%;
  text-align: center;
  height: 50px;
  line-height: 50px;
  cursor: pointer;
}
.imgScroll .dots .dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: lightgray;
  margin-right: 10px;
}
.imgScroll .dots .dot.active {
  background-color: gray;
}