<div class="scroll-anim-18-kv" id="js-scroll-anim-18">
<div class="scroll-anim-18-kv__slider">
<div class="scroll-anim-18-kv__slider-item">
<div class="scroll-anim-18-kv__img-wrapper"><img class="scroll-anim-18-kv__img" src="https://picsum.photos/id/1000/800/400" alt="" /></div>
</div>
<div class="scroll-anim-18-kv__slider-item">
<div class="scroll-anim-18-kv__img-wrapper"><img class="scroll-anim-18-kv__img" src="https://picsum.photos/id/1001/800/400" alt="" /></div>
</div>
<div class="scroll-anim-18-kv__slider-item">
<div class="scroll-anim-18-kv__img-wrapper"><img class="scroll-anim-18-kv__img" src="https://picsum.photos/id/1002/800/400" alt="" /></div>
</div>
</div>
</div>
<div class="scroll-anim-18-header">
<nav>
<ul class="scroll-anim-18-header__nav">
<li><a class="scroll-anim-18-header__nav-item" href="#">項目1</a></li>
<li><a class="scroll-anim-18-header__nav-item" href="#">項目2</a></li>
<li><a class="scroll-anim-18-header__nav-item" href="#">項目3</a></li>
</ul>
</nav><a class="scroll-anim-18-header__btn" href="#">ボタン</a>
</div>
<div style="height: 1000px; padding: 16px">
<p>テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。</p>
</div>
.scroll-anim-18-kv#js-scroll-anim-18
.scroll-anim-18-kv__slider
each item in sliderItems
.scroll-anim-18-kv__slider-item
.scroll-anim-18-kv__img-wrapper
img.scroll-anim-18-kv__img(src=item.img, alt='')
.scroll-anim-18-header
nav
ul.scroll-anim-18-header__nav
- for (var i = 1; i <= 3; i++)
li
a.scroll-anim-18-header__nav-item(href='#') 項目#{ i }
a.scroll-anim-18-header__btn(href='#') ボタン
div(style='height: 1000px; padding: 16px')
p テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。
{
"sliderItems": [
{
"img": "https://picsum.photos/id/1000/800/400"
},
{
"img": "https://picsum.photos/id/1001/800/400"
},
{
"img": "https://picsum.photos/id/1002/800/400"
}
]
}
$BLOCK_KV_NAME: '.scroll-anim-18-kv';
$BLOCK_HEADER_NAME: '.scroll-anim-18-header';
// 変数
$color_primary: #2c2929;
$color_white: #fff;
$height_header: 80px;
$shadow_header: 0 5px 25px rgba(86, 49, 56, 0.15),
0px 0px 8px rgba(86, 49, 56, 0.15);
$ff_serif: 'Noto Serif JP', Garamond, 'Yu Mincho', 'YuMincho', 'Meiryo',
sans-serif;
$ff_sans_serif: 'Open Sans', 'Yu Gothic', '游ゴシック', 'YuGothic',
'游ゴシック体', Hiragino Kaku Gothic ProN, 'Meiryo', sans-serif;
#{ $BLOCK_KV_NAME } {
position: relative;
height: calc(100vh - #{$height_header});
overflow: hidden;
background: $color_white;
&__slider {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
}
&__slider-item {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
overflow: hidden;
opacity: 0;
animation: 6s ease-out forwards;
&.is-active {
z-index: 2;
animation-name: anim18ShowImage;
}
&.is-leave {
z-index: 2;
animation-name: anim18HideImage;
}
}
&__img-wrapper {
position: absolute;
top: 0;
right: -50px;
bottom: 0;
left: -50px;
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
animation: inherit;
animation-timing-function: linear;
@at-root #{ $BLOCK_KV_NAME }__slider-item.is-active & {
animation-name: anim18SlideIn;
}
@at-root #{ $BLOCK_KV_NAME }__slider-item.is-leave & {
animation-name: anim18SlideOut;
}
}
&__img {
display: none;
}
}
#{ $BLOCK_HEADER_NAME } {
position: sticky;
top: 0;
display: flex;
justify-content: space-between;
height: $height_header;
font-family: $ff_serif;
background: $color_white;
box-shadow: $shadow_header;
&__nav {
display: flex;
height: $height_header;
font-size: 12.5px;
}
&__nav-item {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 20px 16px;
&::before {
position: absolute;
right: 16px;
bottom: 20px;
left: 16px;
height: 1px;
content: '';
background: $color_primary;
transition: 0.2s ease-out;
transform: scale(0);
transform-origin: left;
}
&:hover::before {
transform: scale(1);
}
}
&__btn {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 160px;
height: $height_header;
font-family: $ff_sans_serif;
font-size: 13px;
color: $color_white;
text-align: center;
background: $color_primary;
&:hover {
color: $color_white;
}
}
}
@keyframes anim18ShowImage {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 1;
}
}
@keyframes anim18HideImage {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@keyframes anim18SlideIn {
0% {
transform: translate3d(50px, 0, 0);
}
100% {
transform: translate3d(0, 0, 0);
}
}
@keyframes anim18SlideOut {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(-50px, 0, 0);
}
}
'use strict';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
export const scrollAnim18 = () => {
const anim = new Anim18('js-scroll-anim-18');
anim.init();
}
class Anim18 {
el: HTMLElement;
sliderEl: HTMLElement;
sliderItemEls: NodeListOf<HTMLElement>;
imgLoadedNum: number;
currentSliderIndex: number;
duration: number;
offsetParallax: number;
constructor(elId: string) {
this.el = document.getElementById(elId);
if (!this.el) return;
this.sliderEl = this.el.querySelector('.scroll-anim-18-kv__slider');
this.sliderItemEls = this.sliderEl.querySelectorAll('.scroll-anim-18-kv__slider-item');
this.imgLoadedNum = 0;
this.currentSliderIndex = 0;
this.duration = 6000;
this.offsetParallax = 60;
}
init() {
if (!this.el) return;
this.parallax();
this.onLoadHandler();
}
onLoadHandler() {
this.sliderItemEls.forEach(el => {
const imgWrapperEl = <HTMLElement>el.querySelector('.scroll-anim-18-kv__img-wrapper');
const imgEl = imgWrapperEl.querySelector('img');
imgEl.addEventListener('load', () => {
const src = imgEl.getAttribute('src');
imgWrapperEl.style.backgroundImage = `url(${ src })`;
this.imgLoadedNum++;
if (this.imgLoadedNum >= this.sliderItemEls.length) {
this.startSlide();
}
});
});
}
startSlide() {
this.sliderItemEls[this.currentSliderIndex].classList.add('is-active');
this.next();
}
next() {
const self = this;
setTimeout(() => {
const prevIndex = self.currentSliderIndex === 0 ? self.sliderItemEls.length - 1 : self.currentSliderIndex - 1;
const nextIndex = self.currentSliderIndex < self.sliderItemEls.length - 1 ? self.currentSliderIndex + 1 : 0;
self.sliderItemEls[prevIndex].classList.remove('is-leave');
self.sliderItemEls[self.currentSliderIndex].classList.remove('is-active');
self.sliderItemEls[self.currentSliderIndex].classList.add('is-leave');
self.sliderItemEls[nextIndex].classList.add('is-active');
self.currentSliderIndex = nextIndex;
self.next();
}, this.duration);
}
parallax() {
gsap.to(this.sliderEl, {
scrollTrigger: {
trigger: this.el,
start: 'top top',
end: 'bottom top',
scrub: .3,
// markers: true,
},
y: this.offsetParallax,
});
}
}