<div class="scroll-anim-24" id="js-scroll-anim-24">
<div class="scroll-anim-24__item js-scroll-anim-24">
<div class="scroll-anim-24__img"><svg viewBox="0 0 300 300" preserveAspectRatio="xMidYMid slice">
<defs>
<mask class="scroll-anim-24__img-mask" id="mask0">
<path class="scroll-anim-24__img-mask-base" d="M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z"></path>
</mask>
</defs>
<foreignObject class="scroll-anim-24__img-img-wrapper" width="300" height="300" mask="url(#mask0)">
<div class="scroll-anim-24__img-img" style="background-image: url(https://picsum.photos/id/1000/400/400);"></div>
</foreignObject>
</svg></div>
<div class="scroll-anim-24__content">
<div class="scroll-anim-24__heading">見出し01</div>
<div class="scroll-anim-24__text">テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。</div>
</div>
</div>
<div class="scroll-anim-24__item js-scroll-anim-24">
<div class="scroll-anim-24__img"><svg viewBox="0 0 300 300" preserveAspectRatio="xMidYMid slice">
<defs>
<mask class="scroll-anim-24__img-mask" id="mask1">
<path class="scroll-anim-24__img-mask-base" d="M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z"></path>
</mask>
</defs>
<foreignObject class="scroll-anim-24__img-img-wrapper" width="300" height="300" mask="url(#mask1)">
<div class="scroll-anim-24__img-img" style="background-image: url(https://picsum.photos/id/1001/400/400);"></div>
</foreignObject>
</svg></div>
<div class="scroll-anim-24__content">
<div class="scroll-anim-24__heading">見出し02</div>
<div class="scroll-anim-24__text">テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。</div>
</div>
</div>
<div class="scroll-anim-24__item js-scroll-anim-24">
<div class="scroll-anim-24__img"><svg viewBox="0 0 300 300" preserveAspectRatio="xMidYMid slice">
<defs>
<mask class="scroll-anim-24__img-mask" id="mask2">
<path class="scroll-anim-24__img-mask-base" d="M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z"></path>
</mask>
</defs>
<foreignObject class="scroll-anim-24__img-img-wrapper" width="300" height="300" mask="url(#mask2)">
<div class="scroll-anim-24__img-img" style="background-image: url(https://picsum.photos/id/1002/400/400);"></div>
</foreignObject>
</svg></div>
<div class="scroll-anim-24__content">
<div class="scroll-anim-24__heading">見出し03</div>
<div class="scroll-anim-24__text">テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。</div>
</div>
</div>
</div>
.scroll-anim-24#js-scroll-anim-24
each item, index in items
.scroll-anim-24__item.js-scroll-anim-24
.scroll-anim-24__img
svg(viewBox='0 0 300 300', preserveAspectRatio='xMidYMid slice')
defs
mask.scroll-anim-24__img-mask(id='mask' + index)
path.scroll-anim-24__img-mask-base(d='M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z')
foreignObject.scroll-anim-24__img-img-wrapper(width='300', height='300', mask='url(#mask' + index + ')')
.scroll-anim-24__img-img(style='background-image: url(' + item.url + ');')
.scroll-anim-24__content
.scroll-anim-24__heading #{ item.heading }
.scroll-anim-24__text #{ item.text }
{
"items": [
{
"url": "https://picsum.photos/id/1000/400/400",
"heading": "見出し01",
"text": "テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。"
},
{
"url": "https://picsum.photos/id/1001/400/400",
"heading": "見出し02",
"text": "テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。"
},
{
"url": "https://picsum.photos/id/1002/400/400",
"heading": "見出し03",
"text": "テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。"
}
]
}
$BLOCK_NAME: '.scroll-anim-24';
// 変数
$duration_default: 0.5s;
$duration_delay: 0.5s;
#{ $BLOCK_NAME } {
&__item {
display: flex;
align-items: center;
& + & {
margin-top: 24px;
}
}
&__img {
flex-shrink: 0;
width: 25%;
}
&__img-img {
width: 100%;
height: 100%;
background-position: center center;
background-size: cover;
}
&__content {
width: 75%;
padding: 24px;
padding-left: 48px;
}
&__heading {
font-size: 24px;
font-weight: bold;
opacity: 0;
transition: all $duration_default $duration_delay;
transform: translateX(-20px);
@at-root #{ $BLOCK_NAME }__item.is-animated & {
opacity: 1;
transform: translateX(0);
}
}
&__text {
margin-top: 8px;
font-size: 16px;
opacity: 0;
transition: all $duration_default $duration_delay;
transform: translateX(-20px);
@at-root #{ $BLOCK_NAME }__item.is-animated & {
opacity: 1;
transform: translateX(0);
}
}
}
'use strict';
import 'snapsvg-cjs';
declare const Snap: any;
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
export const scrollAnim24 = () => {
const itemEls = document.querySelectorAll('.js-scroll-anim-24');
[...itemEls].forEach(el => {
const anim = new Anim24(<HTMLElement>el);
anim.init();
});
}
class Anim24 {
el: HTMLElement;
svg: any;
mask: any;
maskBase: any;
img: any;
maskGroups: Array<any>;
maskPoints: Array<Array<string>>;
constructor(el: HTMLElement) {
this.el = el;
if (!this.el) return;
this.svg = Snap(this.el.querySelector('.scroll-anim-24__img svg'));
this.mask = this.svg.select('.scroll-anim-24__img-mask');
this.maskBase = this.svg.select('.scroll-anim-24__img-mask-base');
this.maskGroups = [];
this.maskPoints = [
['0 150'],
['30 120', '30 180'],
['60 90', '60 150', '60 210'],
['90 60', '90 120', '90 180', '90 240'],
['120 30', '120 90', '120 150', '120 210', '120 270'],
['150 60', '150 120', '150 180', '150 240'],
['180 90', '180 150', '180 210'],
['210 120', '210 180'],
['240 150'],
];
}
/**
* 初期化
*/
init(): void {
if (!this.el) return;
this.setMasks();
this.scrollHandler();
}
/**
* マスクを作成
*/
setMasks(): void {
this.maskPoints.forEach(points => {
const g = this.mask.g();
points.forEach(point => {
const pathVal = `M ${point} l 30 -30 l 30 30 l -30 30 l -30 -30 Z`;
const path = g.path(pathVal);
g.add(path);
});
g.attr({
fill: '#fff',
fillOpacity: 0,
});
this.mask.append(g);
// グループを配列に追加(画像表示アニメーションに利用)
this.maskGroups.push(g);
});
}
/**
* 画像の表示
*/
showImg(): void {
this.maskGroups.forEach((group, index) => {
// 100ms間隔でグループを表示
setTimeout(() => {
group.animate({
fillOpacity: 1,
}, 500, () => {
// 最後にベースのマスクを表示(個別のマスクのみだと境目に線が残ってしまうため)
if (index === (this.maskGroups.length - 1)) {
this.maskBase.animate({
fill: '#fff'
}, 500);
}
});
}, 100 * index);
});
}
/**
* スクロール連動のイベント設定
*/
scrollHandler(): void {
const self = this;
ScrollTrigger.create({
trigger: this.el,
start: 'top 70%',
onEnter: self => {
this.showImg();
this.el.classList.add('is-animated');
self.kill();
}
});
}
}
No notes defined.