[Tools] 非常に軽いカルーセル・ライブラリ作りました

2019年5月4日

Javascript テクノロジー プログラミング

t f B! P L
ホームページの上部に設置されているバナーで左右にスライドするモノを「カルーセル」と言います。 ホームページ制作に関係している人であれば、通じる名前なんですが、名称としてはあまり知られていない感じがします。 ちなみに、「カルーセル(carousel)」は日本語に直訳すると「回転木馬」なのだそうで、ホームページを彩るいい名称だな・・・と思いました。

カルーセルを設置したホームページの効果

さて、このカルーセルは、一体どういう効果があるのでしょうか? 単一のバナーでなく複数のバナーを一定時間で切り替えることができることと、ユーザーが画面をスクロールさせずに、クリックするだけでバナーが選べる機能性で、ECサイトなどで使うと広告とリンクの役割を果たしてくれるため、ECサイトなどで活用されています。 そういえば、Amazonのトップページでも使われていますね。 色々なカルーセルを試してみたんですが、使いにくさ、導入したらホームページが重くなってしまうなど、使用できないライブラリが多かったので、自作してしまおうと思い、作ってみました。 シンプルな基本機能だけしかありませんが、よほど変わったことがやりたい人以外はこれで十分だと思いますので、気になった人は使ってみてください。 jQueryなど他のライブラリも一切使わないので、非常に軽く動作しますよ。

使い方

1. HTMLにカルーセルエリアを構築

<div class="carousel"> <div class="control"> <div class="button" data-button="prev"></div> <div class="button" data-button="next"></div> <div class="pagenation">

2. ヘッダタグに、carousel.jsをセット

<link rel="stylesheet" href="carousel.css" /> <script type="text/javascript" src="carousel.js"></script>

3. ページ下部に、carousel起動コマンドをscriptタグとして記述

<script> new $$carousel({ baseSelector : ".carousel" , moveCount : 30 , dragTranslationRate : 0.3 , dragInertiaSpeed : 10 , autoScroll : true , autoScrollTime : 2000 }); </script> ヘッダタグに、carousel.jsをセット <link rel="stylesheet" href="carousel.css" /> <script type="text/javascript" src="carousel.js"></script> ページ下部に、carousel起動コマンドをscriptタグとして記述 <script> new $$carousel({ baseSelector : ".carousel" , moveCount : 30 , dragTranslationRate : 0.3 , dragInertiaSpeed : 10 , autoScroll : true , autoScrollTime : 2000 }); </script>

Source

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <meta name="viewport" content="user-scalable=yes, width=device-width, initial-scale=1.0, maximum-scale=1"> <title>Carousel</title> <link rel="stylesheet" href="carousel.css"/> <script type="text/javascript" src="carousel.js"></script> </head> <body> <div class="carousel"> <div class="control"> <div class="button" data-button="prev"></div> <div class="button" data-button="next"></div> <div class="pagenation"></div> </div> <div class="panels"> <div style="background-color:orange;color:white;">1</div> <div style="background-color:blue;color:white;">2</div> <div style="background-color:green;color:white;">3</div> <div style="background-color:red;color:white;">4</div> <div style="background-color:purple;color:white;">5</div> <div style="background-color:black;color:white;">6</div> </div> </div> <script> new $$carousel({ baseSelector : ".carousel" , moveCount : 30 , dragTranslationRate : 0.3 , dragInertiaSpeed : 10 , autoScroll : true , autoScrollTime : 2000 }); </script> </body> </html> html,body{ height:100%; margin:0; padding:0; border:0; } *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; } .carousel{ position:relative; display:block; width:80%; height:300px; border:1px solid #666; white-space:nowrap; font-size:0; overflow:hidden; margin:8px; } /* Panel */ .carousel .panels{ display:block; width:100%; height:100%; z-index:20; } .carousel .panels > *{ display:inline-block; margin:0; padding:0; border:1px solid red; width:100%; height:100%; overflow:hidden; font-size:13px; } .carousel .panels > *.panel:nth-child(1){ margin-left:-100%; } /* Control */ .carousel .control{ position:absolute;; z-index:10; width:100%; height:100%; } /* Button */ .carousel .control .button[data-button="prev"], .carousel .control .button[data-button="next"]{ position:absolute; width:40px; height:40px; border:1px solid #666; background-color:#666; border-radius:50%; top:calc(50% - 20px); cursor:pointer; } .carousel .control .button[data-button="prev"]:hover, .carousel .control .button[data-button="next"]:hover{ opacity:0.5; } .carousel .control .button[data-button="prev"]{ left:4px; } .carousel .control .button[data-button="next"]{ right:4px; } /* Pagenation */ .carousel .control .pagenation{ position:absolute; bottom:0; left:50%; display:inline-block; transform : translate(-50%,0); } .carousel .control .pagenation .point{ display : inline-block; width : 10px; height : 10px; margin:4px; padding:0; border:2px solid #666; border-radius : 50%; background-color : #666; cursor:pointer; } .carousel .control .pagenation .point:hover, .carousel .control .pagenation .point[data-current="1"]:hover{ border-color:red; } .carousel .control .pagenation .point[data-current="1"]{ border:2px solid #666; background-color:white; } /** * Name : Carousel * Since : 2019.04.25 * Author : Yugeta Koji * * # Nest * .carousel * ├ .panels * │ └ .panel * └ control * ├ .button[data-button="prev"] * └ .button[data-button="next"] * * # Function * button : click left-right-button * drag : mouse-drag * pointer : click point-link * autoScroll : timing * */ ;$$carousel = (function(){ var $$event = function(target, mode, func){ if (typeof target.addEventListener !== "undefined"){ target.addEventListener(mode, func, false); } else if(typeof target.attachEvent !== "undefined"){ target.attachEvent('on' + mode , function(){func.call(target , window.event)}); } }; var $$upperSelector = function(elm , selectors) { selectors = (typeof selectors === "object") ? selectors : [selectors]; if(!elm || !selectors){return;} var flg = null; for(var i=0; i<selectors.length; i++){ for (var cur=elm; cur; cur=cur.parentElement) { if (cur.matches(selectors[i])) { flg = true; break; } } if(flg){ break; } } return cur; } var $$ = function(options){ this.setOptions(options); switch(document.readyState){ case "complete": this.start(); break; case "interactive": $$event(window , "DOMContentLoaded" , (function(e){this.start(e)}).bind(this)); break; default: $$event(window , "load" , (function(e){this.start(e)}).bind(this)); break; } }; $$.prototype.start = function(){console.log(this.options); var carousels = document.querySelectorAll(this.options.baseSelector); if(!carousels || !carousels.length){return;} for(var i=0; i<carousels.length; i++){ this.setButtons(carousels[i].querySelector(".control")); this.setPagenation(carousels[i]); var res = this.setPanels(carousels[i]); if(!res){continue;} } this.setCurrentPagenations(); $$event(window , "mousedown" , (function(e){this.dragStart(e)}).bind(this)); $$event(window , "mousemove" , (function(e){this.dragMove(e)}).bind(this)); $$event(window , "mouseup" , (function(e){this.dragEnd(e)}).bind(this)); $$event(window , "touchstart" , (function(e){this.dragStart(e)}).bind(this)); $$event(window , "touchmove" , (function(e){this.dragMove(e)}).bind(this)); $$event(window , "touchend" , (function(e){this.dragEnd(e)}).bind(this)); if(this.options.autoScroll === true){ for(var i=0; i<carousels.length; i++){ carousels[i].setAttribute("data-carousel-number" , i); this.setAutoScroll_next(carousels[i]); } } } // Options $$.prototype.options = { baseSelector : ".carousel" , panelsSelector : ".panels" , panelClass : "panel" , moveTarget : null , moveCount : 30 , dragElement : null , dragTranslationRate : 0.3 , dragInertiaSpeed : 10 , currentNumber : 0 , pagenationElement : null , autoScroll : true , autoScrollflg : {} , autoScrollTime : 2000 }; $$.prototype.setOptions = function(options){ if(!options || !Object.keys(options).length){return;} for(var i in options){ this.options[i] = options[i]; } }; // Intial-Setting $$.prototype.setPanels = function(base){ if(!base){return;} var parent = base.querySelector(":scope " + this.options.panelsSelector); var panels = parent.querySelectorAll(":scope > *"); if(!panels || panels.length <= 1){ return false; } for(var i=0; i<panels.length; i++){ panels[i].setAttribute("class" , this.options.panelClass); panels[i].setAttribute("data-number" , i); } // count-2(x2) if(panels.length <= 2){ var count = panels.length; for(var i=0; i<count; i++){ var newElm = panels[i].cloneNode(true); newElm.setAttribute("data-clone" , "1"); panels[i].setAttribute("data-number" , i); parent.appendChild(newElm); } } // count-3over else{ var firstPanel = parent.firstElementChild; var lastPanel = parent.lastElementChild; parent.insertBefore(lastPanel , firstPanel); } return true; }; // Button (left,right) $$.prototype.setButtons = function(base){ if(!base){return} var button_left = base.querySelector(".button[data-button='prev']"); if(!button_left){ button_left = document.createElement("div"); button_left.setAttribute("class" , "button"); button_left.setAttribute("data-button" , "prev"); base.insertBefore(button_left , base.firstElementChild); } $$event(button_left , "click" , (function(e){this.clickControlButton(e)}).bind(this)); var button_right = base.querySelector(".button[data-button='next']"); if(!button_right){ button_right = document.createElement("div"); button_right.setAttribute("class" , "button"); button_right.setAttribute("data-button" , "next"); base.insertBefore(button_right , base.firstElementChild); } $$event(button_right , "click" , (function(e){this.clickControlButton(e)}).bind(this)); }; $$.prototype.clickControlButton = function(e){ if(this.options.moveTarget !== null){return;} var button = e.currentTarget; if(!button){return} var direction = button.getAttribute("data-button"); if(!direction){return} var base = $$upperSelector(button , this.options.baseSelector); if(!base){return} var panels = base.querySelectorAll(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass); if(!panels || !panels.length){return} if(this.options.autoScroll === true){ var carousel_number = base.getAttribute("data-carousel-number"); clearTimeout(this.options.autoScrollflg[carousel_number]); } this.options.moveTarget = base; this.options.moveCurrentCount = 0; this.options.moveUnit = 100 / this.options.moveCount; switch(direction){ case "prev": this.animButton_right(); break; case "next": this.animButton_left(); break; } }; $$.prototype.animButton_left = function(){ var base = this.options.moveTarget; if(!this.options.moveTarget){return;} var first = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); this.options.moveCurrentCount++; var value = this.options.moveUnit * this.options.moveCurrentCount + 100; if((200 - value) >= this.options.moveUnit){ first.style.setProperty("margin-left" , (value * -1) + "%" , ""); setTimeout((function(){this.animButton_left()}).bind(this),10); } else{ this.options.moveTarget = null; first.style.removeProperty("margin-left"); var parent = base.querySelector(":scope "+this.options.panelsSelector); parent.appendChild(first); this.setCurrentPagenation(base); if(this.options.autoScroll === true){ this.setAutoScroll_next(base); } } } $$.prototype.animButton_right = function(){ var base = this.options.moveTarget; if(!this.options.moveTarget){return;} var first = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); this.options.moveCurrentCount++; var value = this.options.moveUnit * this.options.moveCurrentCount - 100; if((100 - value) > this.options.moveUnit + 100){ first.style.setProperty("margin-left" , (value) + "%" , ""); setTimeout((function(){this.animButton_right()}).bind(this) , 10); } else{ this.options.moveTarget = null; first.style.removeProperty("margin-left"); var parent = base.querySelector(":scope " + this.options.panelsSelector); parent.insertBefore(parent.lastElementChild , first); this.setCurrentPagenation(base); if(this.options.autoScroll === true){ this.setAutoScroll_next(base); } } } // Drag $$.prototype.dragStart = function(e){ var target = e.target; if(!target){return;} if($$upperSelector(target,".carousel .control *")){return;} var base = $$upperSelector(target,this.options.baseSelector); if(!base){return;} this.options.dragElement = base; this.options.dragPosition = e.pageX; this.options.baserWidth = base.offsetWidth; if(this.options.autoScroll === true){ var carousel_number = base.getAttribute("data-carousel-number"); clearTimeout(this.options.autoScrollflg[carousel_number]); } }; $$.prototype.dragMove = function(e){ if(!this.options.dragElement){return;} var firstPanel = this.options.dragElement.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); if(!firstPanel){return;} this.options.dragDiff = e.pageX - this.options.dragPosition; var x = (-1 * this.options.baserWidth + this.options.dragDiff); if(x < this.options.baserWidth * -2){ x = this.options.baserWidth * -2; } else if(x > 0){ x = 0; } firstPanel.style.setProperty("margin-left" , x + "px" , ""); }; $$.prototype.dragEnd = function(e){ if(!this.options.dragElement){return;} this.options.dragElement_motion = this.options.dragElement; this.options.dragElement = null; this.options.moveCurrentCount = 0; var direct = null; // to-left if(this.options.dragDiff < (this.options.baserWidth * this.options.dragTranslationRate * -1)){ this.dragMotion_left(); } // to-right else if(this.options.dragDiff > (this.options.baserWidth * this.options.dragTranslationRate)){ this.dragMotion_right(); } // to-current else if(this.options.dragDiff !== 0){ if(this.options.dragDiff < 0){ this.dragMotionBack_right(); } else if(this.options.dragDiff > 0){ this.dragMotionBack_left(); } } }; $$.prototype.dragMotion_left = function(){ var base = this.options.dragElement_motion; var first = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); this.options.moveCurrentCount++; var value = this.options.dragDiff - (this.options.dragInertiaSpeed * this.options.moveCurrentCount); if(this.options.baserWidth + value > this.options.dragInertiaSpeed * 1.5){ first.style.setProperty("margin-left" , ((-1 * this.options.baserWidth) + value) + "px" , ""); setTimeout((function(){this.dragMotion_left()}).bind(this) , 10); } else{ this.options.dragElement_motion = null; first.style.removeProperty("margin-left"); var parent = base.querySelector(":scope " + this.options.panelsSelector); parent.appendChild(first); this.setCurrentPagenation(base); if(this.options.autoScroll === true){ this.setAutoScroll_next(base); } } } $$.prototype.dragMotion_right = function(){ var base = this.options.dragElement_motion; var first = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); this.options.moveCurrentCount++; var value = this.options.dragDiff + (this.options.dragInertiaSpeed * this.options.moveCurrentCount); if(this.options.baserWidth - value > this.options.dragInertiaSpeed * 1.5){ first.style.setProperty("margin-left" , ((-1 * this.options.baserWidth) + value) + "px" , ""); setTimeout((function(){this.dragMotion_right()}).bind(this) , 10); } else{ this.options.dragElement_motion = null; first.style.removeProperty("margin-left"); var parent = base.querySelector(":scope " + this.options.panelsSelector); parent.insertBefore(parent.lastElementChild , first); this.setCurrentPagenation(base); if(this.options.autoScroll === true){ this.setAutoScroll_next(base); } } } $$.prototype.dragMotionBack_left = function(){ var base = this.options.dragElement_motion; var first = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); this.options.moveCurrentCount++; var value = this.options.dragDiff - (this.options.dragInertiaSpeed * this.options.moveCurrentCount); if(value > this.options.dragInertiaSpeed * 1.5){ first.style.setProperty("margin-left" , (value - base.offsetWidth) + "px" , ""); setTimeout((function(){this.dragMotionBack_left()}).bind(this) , 10); } else{ this.options.dragElement_motion = null; first.style.removeProperty("margin-left"); if(this.options.autoScroll === true){ this.setAutoScroll_next(base); } } } $$.prototype.dragMotionBack_right = function(){ var base = this.options.dragElement_motion; var first = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); this.options.moveCurrentCount++; var value = this.options.dragDiff + (this.options.dragInertiaSpeed * this.options.moveCurrentCount); if(value < -this.options.dragInertiaSpeed * 1.5){ first.style.setProperty("margin-left" , (value - base.offsetWidth) + "px" , ""); setTimeout((function(){this.dragMotionBack_right()}).bind(this) , 10); } else{ this.options.dragElement_motion = null; first.style.removeProperty("margin-left"); if(this.options.autoScroll === true){ this.setAutoScroll_next(base); } } } // Pagenation $$.prototype.setPagenation = function(base){ if(!base){return;} var pagenation_parent = base.querySelector(":scope .control .pagenation"); if(!pagenation_parent){ var div = document.createElement("div"); div.setAttribute("class" , "pagenation"); base.querySelector(".control").appendChild(div); } var panels = base.querySelectorAll(":scope "+ this.options.panelsSelector +" > *"); if(!panels || !panels.length){return;} for(var i=0; i<panels.length; i++){ var div = document.createElement("div"); div.setAttribute("class" , "point"); div.setAttribute("data-number" , i); $$event(div , "click" , (function(e){this.clickPagenation(e)}).bind(this)); pagenation_parent.appendChild(div); } }; $$.prototype.clickPagenation = function(e){ var point_target = e.currentTarget; if(!point_target){return;} if(point_target.getAttribute("data-current") === "1"){return;} var number = point_target.getAttribute("data-number"); if(!number){return;} var base = $$upperSelector(point_target , this.options.baseSelector); if(!base){return;} var panel_target = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+"[data-number='"+number+"']"); if(!panel_target){return;} this.options.pagenationElement = panel_target; this.getPagenationMotion(); if(this.options.autoScroll === true){ var carousel_number = base.getAttribute("data-carousel-number"); clearTimeout(this.options.autoScrollflg[carousel_number]); } }; $$.prototype.setCurrentPagenations = function(){ var bases = document.querySelectorAll(this.options.baseSelector); if(!bases || !bases.length){return;} for(var i=0; i<bases.length; i++){ this.setCurrentPagenation(bases[i]); } }; $$.prototype.setCurrentPagenation = function(base){ if(!base){return;} var panel_target = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":nth-child(2)"); if(!panel_target){return;} var currentNumber = panel_target.getAttribute("data-number"); if(!currentNumber){return;} var pagenation_target = base.querySelector(":scope .control .pagenation .point[data-number='"+currentNumber+"']"); if(!pagenation_target){return;} var points = base.querySelectorAll(":scope .control .pagenation .point"); if(!points || !points.length){return;} for(var i=0; i<points.length; i++){ if(points[i].getAttribute("data-number") === currentNumber){ points[i].setAttribute("data-current" , "1"); } else{ points[i].removeAttribute("data-current"); } } this.options.currentNumber = Number(currentNumber); } $$.prototype.getPagenationMotion = function(){ if(!this.options.pagenationElement){return;} var base = $$upperSelector(this.options.pagenationElement , this.options.baseSelector); var first = base.querySelector(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass+":first-child"); if(!first){return;} var currentPosition = this.options.pagenationElement.offsetLeft; if(currentPosition < -10){ first.style.setProperty("margin-left", (first.offsetLeft + 20) + "px" , "");//(currentPosition / 4) setTimeout((function(e){this.getPagenationMotion(e)}).bind(this) , 10); } else if(currentPosition > 10){ first.style.setProperty("margin-left", (first.offsetLeft - 20) + "px" , ""); setTimeout((function(e){this.getPagenationMotion(e)}).bind(this) , 10); } else{ var panels = base.querySelectorAll(":scope "+this.options.panelsSelector+" > ."+this.options.panelClass); var order = 0; for(var i=0; i<panels.length; i++){ if(panels[i] === this.options.pagenationElement){ order = i; break; } } var parent = base.querySelector(":scope "+ this.options.panelsSelector); if(order < 1){ for(var i=0; i<(1 - order); i++){ var currentFirst = parent.querySelector(":scope > ."+this.options.panelClass+":first-child"); var currentLast = parent.querySelector(":scope > ."+this.options.panelClass+":last-child"); parent.insertBefore(currentLast , currentFirst); } } else{ for(var i=0; i<(order - 1); i++){ var currentFirst = parent.querySelector(":scope > ."+this.options.panelClass+":first-child"); parent.appendChild(currentFirst); } } first.style.removeProperty("margin-left"); this.setCurrentPagenations(); this.options.pagenationElement = null; if(this.options.autoScroll === true){ this.setAutoScroll_next(base); } } }; // AutoScroll $$.prototype.setAutoScroll_next = function(base){ if(!base){return;} var carousel_number = base.getAttribute("data-carousel-number"); if(!carousel_number){return;} if(typeof this.options.autoScrollflg[carousel_number] !== "undefined"){ clearTimeout(this.options.autoScrollflg[carousel_number]); } this.options.autoScrollflg[carousel_number] = setTimeout((function(base){ this.autoScrollMotion(base); }).bind(this,base),this.options.autoScrollTime); }; $$.prototype.autoScrollMotion = function(base){ var nextButton = base.querySelector(":scope .control .button[data-button='next']"); if(!nextButton){return;} nextButton.click(); this.setAutoScroll_next(base); }; return $$; })();

Download

Github https://github.com/yugeta/carousel

Demo

Codepen

See the Pen lite-carousel by YugetaKoji (@geta1972) on CodePen.

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ