はてなブログ用カルーセル仕様スライダー「imzCarousel」jQueryなし

あまり需要はなさそうですが、「はてなブログ用スライダー」をもう一歩進化させて、最後の画像までいったら最初に戻るいわゆるカルーセル(回転木馬)仕様にしたものを作ってみました。レスポンシブデザイン対応、同一ページに複数個使用可、jQueryは必要ありません。

(2017/12/1)バグ修正済み(詳細記事



デモ

オートスタート、チェンジ間隔3000ミリ秒


dotsナビなし、オートスタートなし、画像サイズ小(400px)


使用方法

  1. 下記 Javascript を カスタマイズ > フッタ にいれる
    または、ssl 対応サーバがあれば、サーバにおいてインポートする
  2. 下記 css を カスタマイズ > デザインCSS にいれる
  3. 画像ファイルを次のようにマークアップし、その後ろに呼び出しスクリプトをいれる
  4. 画像サイズは同サイズで揃えれば指定の必要はなくレスポンシブに対応している(はず)
<!-- id は呼び出しスクリプトの id と一致させれば任意 -->
<div id="myslider" class="imzcarousel">
  <div class="carousel-inner">
    <!-- 画像の挿入はいずれの編集モードでも可、ただし下記オプション参照 -->
    [f:id:ausnichts:20171113140837j:plain]
    [f:id:ausnichts:20171113140931j:plain]
    [f:id:ausnichts:20171113140911j:plain]
  </div>
</div>
<script>
window.addEventListener("load", function() {
  var slider = new imzCarousel({
    id: 'myslider',
    arrows: true,
    dots: true,
    autoplay: true
  });
});
</script>


オプション

Option Type Default Description
id string 一意の文字列で指定し、呼び出しの id と一致させてください
cleanup boolean false はてな記法の場合は<p><span>以外のタグが入りますので true を指定してください
arrows boolean false 前へ(<)、次へ(>)の表示、非表示です
dots boolean false 画像の枚数分のナビ(ドット)の表示、非表示です
autostart boolean false オートスタートのオンオフです
interval int 3000 オートスタートの場合のスライド間隔(ミリ秒)です
  • id を変えれば同一ページでも複数個利用できます。
  • 画像は <p><span> で括られることを前提としていますので、htmlを直書きする場合は <p>で括ってください。
  • 画像のスライドスピードは、CSS で 400ms 固定になっています。


Javascript

<script>
/**
 * はてなブログ専用のカルーセル仕様スライダーです.
 * @author imuza.com
 * @version 1.1.0
 *
 * @param {object} options - オプション・パラメータを指定します.
 * @param {string} options.id - 一意の文字列で指定し、呼び出しの id と一致させてください.
 * @param {(boolean)} [options.cleanup=false] - はてな記法の場合、<p><span>以外のタグが入りますので true を指定してください.
 * @param {(boolean)} [options.arrows=false] - 前へ(<)、次へ(>)の表示、非表示です.
 * @param {(boolean)} [options.dots=false] - 画像の枚数分のナビ(ドット)の表示、非表示です.
 * @param {(boolean)} [options.autostart=false] - オートスタートのオンオフです.
 * @param {number} [options.interval=3000] - オートスタートの場合のスライド間隔(ミリ秒)です.
*/

function imzCarousel(options) {
    var id     = options.id,
        cleanup  = options.cleanup || false,
        arrows   = options.arrows || false,
        dots     = options.dots || false,
        autoplay = options.autoplay || false,
        interval = options.interval || 3000,
        timer    = null,
        active   = 0,
        wrap     = document.getElementById(id),
        inner    = wrap.getElementsByClassName('carousel-inner')[0],
        slides   = inner.children;
    if (cleanup) cleanUp();

    var count  = slides.length;
    if (count > 1) {
        var imgwidth = inner.getElementsByTagName('img')[0].clientWidth,
                cwidth   = document.getElementsByClassName('entry-content')[0].clientWidth,
                swidth   = (imgwidth < cwidth) ? imgwidth : cwidth,
                dotswrap  = document.createElement('div');
        initial();
        if (arrows) showArrows();
        if (dots) {
            var navdots  = [];
            showDots();
        }
        if (autoplay) initAutoPlay();
        if (dots || autoplay) {
            wrap.getElementsByClassName('prevslide')[0].style.bottom = dotswrap.offsetHeight + 'px';
            wrap.getElementsByClassName('nextslide')[0].style.bottom = dotswrap.offsetHeight + 'px';
        }
    }

    function cleanUp() {
        Array.prototype.forEach.call(slides, function(s){
            var tag = s.tagName.toLowerCase();
            if((tag !== 'p' && tag !== 'span') || s.innerHTML === '') {
                inner.removeChild(s);
            }
        });
    }

    function initial() {
        wrap.style.width = swidth + 'px';
        Array.prototype.forEach.call(slides, function(s){
            s.style.width = swidth + 'px';
        });
        inner.style.width = swidth * count + 'px';
        wrap.style.opacity = '1';
        var element = slides[count-1];
        element.style.marginLeft = -swidth + 'px';
        inner.removeChild(element);
        inner.insertAdjacentHTML('afterbegin', element.outerHTML);

    }

    function showArrows() {
        var divPrev = document.createElement('div');
        divPrev.classList.add('prevslide');
        divPrev.addEventListener('click', imzSlidePrev, false);
        var divNext = document.createElement('div');
        divNext.classList.add('nextslide');
        divNext.addEventListener('click', imzSlideNext, false);
        wrap.appendChild(divPrev);
        wrap.appendChild(divNext);
    }

    function showDots() {
        var ul = document.createElement('ul');
        ul.classList.add('navdots');
        var fragment = document.createDocumentFragment();
        for(var i=0; i<count; i++){
            var li = document.createElement('li');
            li.addEventListener('click', navDot.bind(this), false);
            fragment.appendChild(li);
        }
        ul.appendChild(fragment);
        dotswrap.appendChild(ul);
        wrap.appendChild(dotswrap);
        navdots = ul.children;
        navdots[active].classList.add('isactive');
    }

    function navDot(e) {
        var t = e.target;
        var c = Array.prototype.indexOf.call(navdots, t);
        if (c > active) while(c > active) imzSlideNext();
        else while(c < active) imzSlidePrev();
    }

    function initAutoPlay() {
        inner.addEventListener('mouseover', autoStop.bind(this), false);
        inner.addEventListener('mouseout', autoPlay.bind(this), false);
        var message = document.createElement('div');
        message.textContent = 'Pause on Mouseover';
        message.classList.add('msgautoplay');
        dotswrap.insertBefore(message, dotswrap.firstChild);
        autoPlay();
    }

    function autoPlay() {
        if(timer) return;
        timer = setInterval(imzSlideNext.bind(this), interval);
    }

    function autoStop() {
        clearInterval(timer);
        timer = null;
    }

    function imzSlidePrev(){
        slides[0].style.marginLeft = '';
        var element = slides[count-1];
        element.style.marginLeft = -swidth + 'px';
        inner.removeChild(element);
        inner.insertAdjacentHTML('afterbegin', element.outerHTML);
        if(dots) {
            navdots[active].classList.remove('isactive');
            active--;
            if (active<0) active = count-1;
            navdots[active].classList.add('isactive');
        }
    }

    function imzSlideNext(){
        slides[1].style.marginLeft = -swidth + 'px';
        var element = slides[0];
        element.style.marginLeft = '';
        inner.removeChild(element);
        inner.insertAdjacentHTML('beforeend', element.outerHTML);
        if(dots) {
            navdots[active].classList.remove('isactive');
            active++;
            if (active>count-1) active = 0;
            navdots[active].classList.add('isactive');
        }
    }
}
</script>


Javascript Minify & ダウンロード

Javascript Minify などで圧縮してください。

GitHub から一式ダウンロードできます。

github.com


CSS

.imzcarousel {
  position: relative;
  overflow: hidden;
  opacity: 0;
  -webkit-transition: opacity 300ms;
  transition: opacity 300ms; }
  .imzcarousel img.hatena-fotolife {
    pointer-events: none; }
  .imzcarousel .prevslide {
    display: block;
    position: absolute;
    background: rgba(255, 255, 255, 0.2);
    width: 6vw;
    height: 6vw;
    border-radius: 50%;
    top: 0;
    bottom: 0;
    left: 0;
    margin: auto;
    text-align: center;
    cursor: pointer; }
    .imzcarousel .prevslide::before {
      font-family: blogicon;
      content: "\f005";
      font-size: 5vw;
      line-height: 6vw;
      color: rgba(255, 255, 255, 0.6); }
  .imzcarousel .nextslide {
    display: block;
    position: absolute;
    background: rgba(255, 255, 255, 0.2);
    width: 6vw;
    height: 6vw;
    border-radius: 50%;
    top: 0;
    bottom: 0;
    right: 0;
    margin: auto;
    text-align: center;
    cursor: pointer; }
    .imzcarousel .nextslide::before {
      font-family: blogicon;
      content: "\f006";
      font-size: 5vw;
      line-height: 6vw;
      color: rgba(255, 255, 255, 0.6); }
  .imzcarousel .navdots {
    padding: 0;
    margin: 0;
    list-style: none;
    text-align: center;
    border-bottom: 2px solid #ccc; }
    .imzcarousel .navdots li {
      display: inline-block;
      width: 10px;
      height: 10px;
      background: #ccc;
      margin: 0;
      padding: 0;
      margin-right: 20px;
      border-radius: 5px;
      cursor: pointer; }
      .imzcarousel .navdots li.isactive {
        background: red;
        box-shadow: 0px 0 5px red; }
  .imzcarousel .msgautoplay {
    text-align: center;
    font-size: 0.8em; }

.carousel-inner {
  display: block;
  *zoom: 1; }
  .carousel-inner:after {
    display: block;
    visibility: hidden;
    font-size: 0;
    height: 0;
    clear: both;
    content: "."; }
  .carousel-inner > p, .carousel-inner span {
    -webkit-transition: margin-left 400ms ease-in-out;
    transition: margin-left 400ms ease-in-out;
    line-height: 0;
    display: inline-block;
    float: left;
    margin: 0;
    padding: 0; }

@media (min-width: 768px) and (max-width: 1024px) {
  .imzcarousel .prevslide,
  .imzcarousel .nextslide {
    width: 10vw;
    height: 10vw; }
    .imzcarousel .prevslide::before,
    .imzcarousel .nextslide::before {
      font-size: 7vw;
      line-height: 10vw; } }
@media (max-width: 767px) {
  .imzcarousel .prevslide,
  .imzcarousel .nextslide {
    width: 15vw;
    height: 15vw; }
    .imzcarousel .prevslide::before,
    .imzcarousel .nextslide::before {
      font-size: 10vw;
      line-height: 15vw; } }
  • メディアクエリで指定しているのは、矢印(前、次)のサイズだけです。
  • はてな公式の boilerplate のメディアクエリを使用しています。


お問い合わせ等

当スライダー使用によるいかなる損害についても責任を負いかねますので自己責任でご使用ください。

お問い合わせ、バグの報告、仕様変更のご要望等は Contact Us までお願いします。