Vue.jsの勉強中でスライドショーを自作したので自分用のメモ
カスタムイベントtransitionendを定義して終了時に再度setIntervalを実行。
<div id="top_slide">
<div class="sliderpro_wrap">
<div class="sliderpro_main" ref="getParentWidth">
<ul :style="{width:containerW * itemsCount + 'px',transform: `translateX(${sliderTransform}px)`}"
v-transitionend="slideInterval" @touchstart="touchstart" @touchmove="touchMove" @touchend="touchEnd">
<li v-for="item in items" :key="item.id" :style="{width:containerW + 'px'}"><img :src="item.src"></li>
</ul>
<div class="sliderpro_arrows">
<div class="sliderpro_arrow sliderpro_arrow_prev" v-on:click="onClickPrev"></div>
<div class="sliderpro_arrow sliderpro_arrow_next" v-on:click="onClickNext"></div>
</div>
</div>
<div class="sliderpro_thumbnails">
<ul>
<li v-for="(item,index) in items" :key="item.id" v-on:click="onClickThumbnal(index)"
:class="{ active: item.activeClass}"><img :src="item.src"></li>
</ul>
</div>
</div>
</div>
const top_slide = Vue.createApp({
data: function () {
return {
// カルーセル画像の配列
items: [
{
id: 1,
src: "img/slide/img_01.jpg"
},
{
id: 2,
src: "img/slide/img_02.jpg"
},
{
id: 3,
src: "img/slide/img_03.jpg"
}
],
sliderTransform:0,
selectedIndex: 0,
containerW:0,
timer:false,
startX:0,
moveX:0,
}
},
mounted () {
this.initFunc();
// 要素の幅を取得するメソッド
this.getTargetWidth();
// ユーザーがウィンドウサイズを変更したら実行されるようにする
window.addEventListener('resize', this.getTargetWidth)
},
beforeDestroy(){
//コンポーネントが破棄されたとき
this.slideStopInterval();
},
computed: {
itemsCount:function(){
return this.items.length
}
},
methods:{
initFunc:function(){
this.onClickThumbnal(this.selectedIndex);
this.slideInterval();
},
getTargetWidth(){
this.containerW = this.$refs.getParentWidth.clientWidth;
this.sliderTransform = -(this.containerW * this.selectedIndex);
},
onClickPrev:function(){
this.selectedIndex < 1 ? this.selectedIndex = this.itemsCount -1: this.selectedIndex -= 1;
this.onClickThumbnal(this.selectedIndex);
},
onClickNext:function(){
this.selectedIndex < this.itemsCount - 1 ? this.selectedIndex += 1 : this.selectedIndex = 0;
this.onClickThumbnal(this.selectedIndex);
},
onClickThumbnal:function(index){
this.slideStopInterval(); //スライド一旦停止
this.selectedIndex = index;
this.sliderTransform = -(this.containerW * index);
this.items.filter(function(value,f_index,array){
//activeClass切り替え
return f_index === index ? value.activeClass = true: value.activeClass = false
});
},
slideInterval:function(){
this.timer = setInterval(function(){
this.onClickNext();
}.bind(this),4000);
},
slideStopInterval(){
if(this.timer){
clearInterval(this.timer);
this.timer = 0;
}
},
touchstart(e){
this.slideStopInterval();
this.startX = e.touches[0].pageX;
},
touchMove(e){
this.moveX = e.touches[0].pageX - this.startX
},
touchEnd(){
if(this.moveX > 10){
this.onClickPrev();
//右スワイプ
}else if(this.moveX < -10){
//左スワイプ
this.onClickNext();
}
this.slideStopInterval();
}
}
});
top_slide.directive('transitionend', {
mounted(el,binding) {
el.addEventListener('transitionend', () => {
binding.value();
});
}
});
top_slide.mount("#top_slide");
.sliderpro_wrap{
position: relative;
max-width: 1400px;
margin: 0 auto;
}
.sliderpro_wrap .sliderpro_main{
overflow: hidden;
position: relative;
margin-bottom: 10px;
}
.sliderpro_wrap .sliderpro_main ul{
transition: transform 1.1s ease-in-out;
}
.sliderpro_wrap .sliderpro_main ul > li{
float: left;
}
.sliderpro_wrap img{
width: 100%;
}
.sliderpro_wrap .sliderpro_thumbnails ul{
letter-spacing: -0.5em;
text-align: center;
}
.sliderpro_wrap .sliderpro_thumbnails ul li{
cursor: pointer;
display: inline-block;
letter-spacing: normal;
vertical-align: top;
width: 9%;
margin-right: 1.111%;
}
.sliderpro_wrap .sliderpro_thumbnails ul li:nth-child(10){
margin-right: 0;
}
.sliderpro_wrap .sliderpro_thumbnails ul li:not(:nth-child(-n + 10)){
margin-top: 1.1111%;
}
.sliderpro_wrap .sliderpro_thumbnails ul img{
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
transition:filter 0.5s ease-in-out;
}
.sliderpro_wrap .sliderpro_thumbnails ul li.active img,
.sliderpro_wrap .sliderpro_thumbnails ul li:hover img{
-webkit-filter: grayscale(0%);
filter: grayscale(0%);
}
/* 矢印ここから */
.sliderpro_wrap .sliderpro_arrows .sliderpro_arrow{
position: absolute;
left: 20px;
top: 50%;
transform: translateY(-50%) rotate(45deg);
transform-origin: center center;
width: 25px;
height: 25px;
border-left: 6px solid #fff;
border-bottom: 6px solid #fff;
transition: opacity 0.6s ease-in-out;
cursor: pointer;
}
.sliderpro_wrap .sliderpro_arrows .sliderpro_arrow.sliderpro_arrow_next{
transform: translateY(-50%) rotate(-135deg);
left: auto;
right: 20px;
}
.sliderpro_wrap .sliderpro_arrows .sliderpro_arrow:hover{
opacity: 0.7;
}
@media screen and (max-width:767px) {
.sliderpro_wrap .sliderpro_arrows .sliderpro_arrow{
width: 15px;
height: 15px;
border-width: 3px;
}
}