【更新日 : 】
【Web Animations API】JavaScriptで要素数が変わっても一定速度で動くスクロールスライダーを実装する
- Category:
- css / js
- Tags:
- JavaScript, Vanilla JS, Web Animations API, アニメーション, スライド, 脱Jquery
Web Animations APIで要素数が変わっても一定の速度を保つ等速スクロールスライダーを実装したサンプルです。
CSSなど横スクロールに時間を指定するものは要素数が変わるとスピードも変わってしまいますが、要素数がバラバラでも一定の速度を保って動きます。
See the Pen Constant velocity scroll slider independent of number Vanilla JS (Web Animations API) by web_nak (@web_walking_nak) on CodePen.
JavaScript
//要素数が変わっても一定の速度を保つ等速スクロールスライダー
const scrollSlider = document.querySelectorAll('.js-scroll-slider');
if(scrollSlider.length > 0) {
//スクロールスピード設定
const speed = 0.05;
scrollSlider.forEach((slider) => {
//スライド要素を取得
const children = slider.children;
const childLength = children.length;
//スライド要素一式を文字列で取得
let baseChildren = '';
for (let i = 0; i < children.length; i++) {
baseChildren += children[i].outerHTML;
}
//スライド要素の横幅を取得
const firstChild = slider.firstElementChild;
const styles = getComputedStyle(firstChild);
let width = parseFloat(styles.width);
let marginRight = parseFloat(styles.marginRight);
//スライダーの途切れ対策
let winWidth = window.innerWidth;
let checkWidth = winWidth * 2;
let sliderWidth = (width + marginRight) * childLength;
let countWidth = 0;
let addCount = 1;
while(countWidth < checkWidth) {
slider.insertAdjacentHTML('beforeend',baseChildren);
++addCount;
countWidth = sliderWidth * addCount;
}
//反転処理
let unit = '-';
const isReverse = slider.classList.contains('is-reverse');
if(isReverse) {
unit = '';
slider.style.marginLeft = '-' + (countWidth - winWidth - marginRight) + 'px';
}
//アニメーションセット
const keyframes = {
transform: 'translateX('+ unit + sliderWidth + 'px)'
};
const timing = {
fill: 'backwards',
duration: sliderWidth / speed,
easing: 'linear',
iterations: Infinity
};
let slideAnime = slider.animate(keyframes,timing);
//レスポンシブ対応
let timeoutId;
let lastWinWidth = window.innerWidth ;
window.addEventListener('resize', () => {
clearTimeout(timeoutId);
//リサイズを停止した時のみ処理
timeoutId = setTimeout(() => {
// 現在と前回の横幅が違う場合だけ実行
if (lastWinWidth !== window.innerWidth) {
lastWinWidth = window.innerWidth;
winWidth = lastWinWidth;
//スライダーが途切れないかチェック
width = parseFloat(styles.width);
marginRight = parseFloat(styles.marginRight);
sliderWidth = (width + marginRight) * childLength;
checkWidth = winWidth * 2;
countWidth = sliderWidth * addCount;
while(countWidth < checkWidth) {
slider.insertAdjacentHTML('beforeend',baseChildren);
++addCount;
countWidth = sliderWidth * addCount;
}
//設定をリセット
if(isReverse) {
slider.style.marginLeft = '-' + (countWidth - winWidth - marginRight) + 'px';
}
slideAnime.effect.updateTiming({iterations:1});
slideAnime.finish();
//スライダー再定義
keyframes.transform = 'translateX('+ unit + sliderWidth + 'px)';
timing.duration = sliderWidth / speed;
slideAnime = slider.animate(keyframes,timing);
}
}, 500);
});
});
}
HTML
<div class="wrapper">
<div class="js-scroll-slider">
<!-- スライド要素 -->
<div class="js-scroll-slider__item"><img src="" alt=""></div>
<div class="js-scroll-slider__item"><img src="" alt=""></div>
</div>
<!-- 逆方向の場合は is-reverse を追加 -->
<div class="js-scroll-slider is-reverse">
<!-- スライド要素 -->
<div class="js-scroll-slider__item"><img src="" alt=""></div>
<div class="js-scroll-slider__item"><img src="" alt=""></div>
</div>
</div>
CSS
アニメーション中は要素が画面外へ出ますので、外側に overflow: hidden; を必ずかけてください。
.wrapper {
overflow: hidden;
}
.js-scroll-slider {
display: flex;
margin-bottom: 20px;
}
.js-scroll-slider__item {
flex-shrink: 0;
width: 400px;
margin-right: 20px;
}