[ プログラミング ] ナビゲーション付きのカルーセルパネルをjQueryで作る方法

カルーセルパネルのjQueryプラグインは山ほどありますが、自分できちんと作ったことがなかったので、ナビゲーション付きのカルーセルパネルを作ってみました。

一応、配布しているCSSテンプレートにカルーセルタイプのスライドショーがあったので、それをカスタマイズしてたのですが、元があった割にはかなり時間がかかりました…。

デモはこちらから。

DOMO

今回は、かなりソースコードが長くなってしまったので、HTMLとJavaScript、CSSのそれぞれファイルを分けています。

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="css/common.css" rel="stylesheet" type="text/css" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/script.js"></script>
<link rel="stylesheet" href="css/style.css.css" type="text/css" />
<title>ナビゲーション付きのカルーセルパネルをjQueryで作る方法</title>
<script type="text/javascript">
$(function() {
    $('.slide1').slideshow({
        autoSlide    : true,
        stop         : false,
        imgHoverStop : true,
        navHoverStop : true,
        interval     : 3000,
        duration     : 500,
        easing       : 'swing',
        slideUnit    : 1,
        eleClone     : 2
    });

    $('.slide2').slideshow({
        autoSlide    : true,
        stop         : true,
        imgHoverStop : true,
        navHoverStop : true,
        interval     : 3000,
        duration     : 500,
        easing       : 'swing',
        slideUnit    : 3,
        eleClone     : 2
    });

    $('.slide3').slideshow({
        autoSlide    : false,
        stop         : false,
        imgHoverStop : true,
        navHoverStop : true,
        interval     : 3000,
        duration     : 500,
        easing       : 'swing',
        slideUnit    : 3,
        eleClone     : 2
    });
});
</script>
</head>
<body>
<div class="wrap">
    <h3>オートスライドあり・ループあり・画像スライド数1・画像とナビのホバー時にスライド停止</h3>
    <div class="slide slide1">
        <div class="carousel">
            <ul class="slideInner">
                <li><a href="index.html"><img src="images/img01.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img02.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img03.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img04.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img05.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img06.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img07.gif" width="250" height="150" alt="" /></a></li>
           </ul>
        </div>
        <div class="slidePrev"></div>
        <div class="slideNext"></div>
        <div class="controlNav"></div>
    </div><!--/slide-->
    <h3>オートスライドあり・ループなし・画像スライド数3・画像とナビのホバー時にスライド停止</h3>
    <div class="slide slide2">
        <div class="carousel">
            <ul class="slideInner">
                <li><a href="index.html"><img src="images/img01.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img02.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img03.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img04.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img05.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img06.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img07.gif" width="250" height="150" alt="" /></a></li>
           </ul>
        </div>
        <div class="slidePrev"></div>
        <div class="slideNext"></div>
        <div class="controlNav"></div>
    </div><!--/slide-->
    <h3>オートスライドなし・ループあり・画像スライド数3・画像とナビのホバー時にスライド停止</h3>
    <div class="slide slide3">
        <div class="carousel">
            <ul class="slideInner">
                <li><a href="index.html"><img src="images/img01.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img02.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img03.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img04.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img05.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img06.gif" width="250" height="150" alt="" /></a></li>
                <li><a href="index.html"><img src="images/img07.gif" width="250" height="150" alt="" /></a></li>
           </ul>
        </div>
        <div class="slidePrev"></div>
        <div class="slideNext"></div>
        <div class="controlNav"></div>
    </div><!--/slide-->
</div><!--/.wrap-->
</body>
</html>

Java Script

$.fn.slideshow = function (options) {
    // オプション初期値
    var o = $.extend({
        autoSlide    : true,
        stop         : true,
        imgHoverStop : true,
        navHoverStop : true,
        interval     : 3000,
        duration     : 500,
        easing       : 'swing',
        slideUnit    : 3,
        eleClone     : 2
    }, options);

    // セレクタ設定
    var $slider     = $(this),
        $container  = $slider.find('.slideInner'),
        $element    = $container.children(),
        $prevNav    = $slider.children('.slidePrev'),
        $nextNav    = $slider.children('.slideNext'),
        $controlNav = $slider.children('.controlNav');

    // 初期設定
    var count = 1,
        imgNum = 1,
        slideCount = 1,
        stopFlag = false;

    // 各種設定
    var windowWidth = $(window).width(),
        imgWidth = $element.outerWidth(true),
        slideWidth = imgWidth * o.slideUnit,
        totalWidth = ($element.length * imgWidth) * Math.pow(2, o.eleClone),
        slidePosition = slideWidth + ($element.length * imgWidth),
        slideCounter = Math.ceil($element.length / o.slideUnit),
        lastGroupEle = $element.length % o.slideUnit,
        lastWidth = imgWidth * lastGroupEle;

    // スライド画像設定
    $container.css('width',totalWidth + 'px');
    for(i=0; i<o.eleClone; i++){
        $('li', $container).clone().appendTo($container);
    }
    for(i=0; i<o.slideUnit; i++){
        $('li:last-child', $container).prependTo($container);
    }
    $container.css('margin-left',-slidePosition + 'px');

    // コントールナビデザイン
    var controlNavDesign = function () {
        if(count > 0) {
            count = count % $controlNav.children('span').length;
        }
        if(count < 1) {
            count = (count % $controlNav.children('span').length) + $controlNav.children('span').length;
        }
        $controlNav.children('span').removeClass('current');
        $controlNav.children('span').eq(count -1).addClass('current')
    };
    if (o.stop) {
        $prevNav.addClass('hidden');
    }

    // 自動切り替えスタート
    var start;
    var startTimer = function () {
        start = setInterval(function(){
            nextSlide();
        },o.interval);
    };

    // 自動切り替えストップ
    var stopTimer = function () {
         clearInterval(start);
    };

    // ストップ機能
    var slideStop = function () {
        if (o.stop) {
            if(count >= slideCounter){
                $nextNav.addClass('hidden');
                stopTimer();
                stopFlag = true;
            }else{
                $nextNav.removeClass('hidden');
                $nextNav.show();
            }
            if(count == 1){
                $prevNav.addClass('hidden');
            }else{
                $prevNav.removeClass('hidden');
            }
        }
    };

    // ホバー時に画像静止(自動切り替えストップ)
    if(o.imgHoverStop){
        $container.hover(
            function () {
                stopTimer();
            },
            function () {
                hoverOff();
            }
        );
    }

    // ナビゲーションのホバー動作
    if(o.navHoverStop){
        $prevNav.hover(
            function () {
                $prevNav.addClass('hover');
                stopTimer();
            },
            function () {
                $prevNav.removeClass('hover');
                hoverOff();
            }
        );
        $nextNav.hover(
            function () {
                $nextNav.addClass('hover');
                stopTimer();
            },
            function () {
                $nextNav.removeClass('hover');
                hoverOff();
            }
        );
        $controlNav.hover(
            function () {
                stopTimer();
            },
            function () {
                hoverOff();
            }
        );
    }

    var hoverOff  = function (){
        if(stopFlag || !o.autoSlide) {
            stopTimer();
        }else {
            startTimer();
        }
    }

    // 左方向スライド実行
    var sliderPrev = function (moveWidth, moveImg) {
        $container.not(':animated').animate({
            marginLeft:parseInt($container.css('margin-left')) + moveWidth + 'px'
        },o.duration,o.easing,
        function(){
            for(i=0; i < moveImg; i++){
                $('li:last-child', $container).prependTo($container);
            }
            $container.css('margin-left',-slidePosition + 'px');
        });
    }

    // 右方向スライド実行
    var sliderNext = function (moveWidth, moveImg) {
        $container.not(':animated').animate({
            marginLeft:parseInt($container.css('margin-left')) - moveWidth + 'px'
        },o.duration,o.easing,
        function(){
            for(i=0; i < moveImg; i++){
                $('li:first-child', $container).appendTo($container);
            }
            $container.css('margin-left',-slidePosition + 'px');
        });
    }

    // 左方向スライド設定
    var prevSlide = function () {
        // スライドに余りがある場合
        if(lastGroupEle >0) {
            if(count== slideCounter){
                var moveWidth = lastWidth;
                var moveImg = lastGroupEle;
                sliderPrev(moveWidth, moveImg);
            }else{
                var moveWidth = slideWidth;
                var moveImg = o.slideUnit;
                sliderPrev(moveWidth, moveImg);
            }
        // スライドに余りがない場合
        }else{
            var moveWidth = slideWidth;
            var moveImg = o.slideUnit;
            sliderPrev(moveWidth, moveImg);
        }
        count --;
        controlNavDesign();
        slideStop();
    }

    // 右方向スライド設定
    var nextSlide = function () {
        // スライドに余りがある場合
        if(lastGroupEle >0) {
            if(count== slideCounter -1){
                var moveWidth = lastWidth;
                var moveImg = lastGroupEle;
                sliderNext(moveWidth, moveImg);
            }else{
                var moveWidth = slideWidth;
                var moveImg = o.slideUnit;
                sliderNext(moveWidth, moveImg);
            }
        // スライドに余りがない場合
        }else{
            var moveWidth = slideWidth;
            var moveImg = o.slideUnit;
            sliderNext(moveWidth, moveImg);
        }
        count ++;
        controlNavDesign();
        slideStop();
    }

    // コントローラーの生成
    for(e=0; e < slideCounter; e++) {
        $('<span/>').text(e+1).appendTo($controlNav).click(function () {
            if($container.is(':animated')){
                return false;
            }
            var select = $controlNav.children('span').index(this);
            var current = $controlNav.children('span.current').index();
            // スライドなし
            if(select == current) {
                return false;
            }
            // 左方向スライド
            if(select < current) {
                var skip = current - select;
                // スライドに余りがある場合
                if(lastGroupEle >0) {
                    if(current == slideCounter -1){
                        var moveWidth = lastWidth + (slideWidth * (skip - 1));
                        var moveImg = (o.slideUnit * (skip - 1)) + lastGroupEle;
                        sliderPrev(moveWidth, moveImg);
                    }else{
                        var moveWidth = slideWidth * skip;
                        var moveImg = o.slideUnit * skip;
                        sliderPrev(moveWidth, moveImg);
                    }
                // スライドに余りがない場合
                }else{
                    var moveWidth = slideWidth * skip;
                    var moveImg = o.slideUnit * skip;
                    sliderPrev(moveWidth, moveImg);
                }
                count -= skip;
                controlNavDesign();
                slideStop();
            }
            // 右方向スライド
            if(select > current) {
                var skip = select - current;
                // スライドに余りがある場合
                if(lastGroupEle >0) {
                    if(select == slideCounter -1){
                        var moveWidth = lastWidth + (slideWidth * (skip - 1));
                        var moveImg = (o.slideUnit * (skip - 1)) + lastGroupEle;
                        sliderNext(moveWidth, moveImg);
                    }else{
                        var moveWidth = slideWidth * skip;
                        var moveImg = o.slideUnit * skip;
                        sliderNext(moveWidth, moveImg);
                    }
                // スライドに余りがない場合
                }else{
                    var moveWidth = slideWidth * skip;
                    var moveImg = o.slideUnit * skip;
                    sliderNext(moveWidth, moveImg);
                }
                count += skip;
                controlNavDesign();
                slideStop();
            }
        });
        $controlNav.children('span:first-child').addClass('current');
    };

    // 自動スタート設定
    if(o.autoSlide){
        startTimer();
    }

    // 戻るボタン
    $prevNav.click(function(){
        if($container.is(':animated') || (o.stop) && (count == 1)){
            return false;
        }
        prevSlide();
   });

    // 進むボタン
    $nextNav.click(function(){
        if($container.is(':animated') || (o.stop) && (count >= slideCounter)){
            return false;
        }
        nextSlide();
    });

};

CSS

html {
    overflow-y:scroll;
}

body {
    margin:0;
    padding:0;
    line-height:1.6;
    letter-spacing:1px;
    font-family:"Hiragino Kaku Gothic Pro",HiraKakuPro-W3,"ヒラギノ角ゴ Pro W3","メイリオ", Meiryo,"MS Pゴシック",verdana,sans-serif;
    font-size:13px;
    color:#000;
}

.wrap {
    width:830px;
    margin:50px auto;
}

.slide {
    position:relative;
    width:830px;
    height:150px;
    overflow:hidden;
    padding-bottom:25px;
}

.carousel {
    position:absolute;
    overflow:hidden;
    width:760px;
    left:35px;
}

.slidePrev {
    width:30px;
    height:150px;
    position:absolute;
    top:0;
    left:0;
    cursor:pointer;
    z-index:100;
    background:url(../images/nav.gif) 0px 0 no-repeat;
}

.slidePrev.hover {
    background:url(../images/nav.gif) -30px 0 no-repeat;
}

.slidePrev.hidden {
    cursor:default;
    background:url(../images/nav.gif) -60px 0 no-repeat;
}

.slideNext {
    display:block;
    width:30px;
    height:150px;
    position:absolute;
    top:0;
    right:0;
    cursor:pointer;
    z-index:100;
    background:url(../images/nav.gif) -90px 0 no-repeat;
}

.slideNext.hover {
    background:url(../images/nav.gif) -120px 0 no-repeat;
}

.slideNext.hidden {
    cursor:default;
    background:url(../images/nav.gif) -150px 0 no-repeat;
}

.slideInner {
    position:relative;
    margin:0;
    padding:0;
}

.slideInner li {
    float:left;
    margin:0;
    padding:0;
    list-style:none;
}

.slideInner li img {
    margin:0;
    padding:0;
    padding:0 5px 0 0;
}

.controlNav {
    position:absolute;
    float:left;
    left:50%;
    bottom:0;
}

.controlNav span {
    position:relative;
    left:-50%;
    float:left;
    margin:5px;
    -webkit-border-radius:5px;
    -moz-border-radius:5px;
    border-radius:5px;
    width:10px;
    height:10px;
    overflow:hidden;
    text-indent:-9999px;
    vertical-align:middle;
    background:#ddd;
}

.controlNav span:hover {
    background:#ccc;
    cursor:pointer;
}

.controlNav span.current {
    background:#b7bd50;
}

最初に基本設計をしていればよかったのですが、思いつきでコーディングしていったので、結構煩雑になってしまいました。オートスライドや、左右とフッターのナビゲーションなど基本的な機能は入れました。

長いコードになったときに、もっとわかりやすいコードの書き方をしたいです。どのように書けばよいのでしょうか?

あと、アニメーション時に、一切の動作を無効にするときのスマートな書き方を知りたいです。とりあえず、クリックしたら、「$container.is(‘:animated’)){return false;}」で実行しないようにしているのですが…。