最初はJSのみで実装しようと思いましたが、難しくて挫折。CSSアニメーションを使えばかなり簡単に作れましたー。ちなみに、ライブラリは使っていません。
何かしら良さげなライブラリがありそうですが、パッと見た感じなさそうなので作ってみました。
HTMLは必要最低限。
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>trace huuuu</title>
<link rel="stylesheet" href="style.css">
</head>
<body style="height: 200vh; width: 100vw;">
<div class="shakes"></div>
<div class="shakes"></div>
<div class="shakes"></div>
<div class="shakes"></div>
<div class="shakes"></div>
<div class="shakes"></div>
<script src="script.js"></script>
</body>
</html>
揺らす要素にshakesというクラスを付けています。
transform-origin: 50% 0;
と書いておくと、揺れてる感が出て良き良き。
CSS
.shakes {
width: 200px;
height: 100px;
margin: 150px;
transform-origin: 50% 0;
background-color: pink;
}
正直JSはコードに自信がないので晒したくはないのですが、ひとまず下記のような感じにしてみました。
各要素ごとに、振幅にばらつきがあったり、振れの速度が違った方が面白いので、それぞれランダムな値を持たせています。
JavaScript
let oldPos = 0;
let timeoutId = 0;
let scrolls = [];
let minShakeAngle = 0.3; // 最小振れ角
let attenuation = 0.8; // 振幅減衰率
let defaultAngle = 45; // 基準振れ角
window.onload = () => {
oldPos = window.scrollY;
let shakes = Array.from(document.getElementsByClassName('shakes'));
shakes.forEach((shake, index) => {
scrolls.push(new Scroll(shake));
});
window.addEventListener( "scroll", () => {
clearTimeout( timeoutId );
let timeoutId = setTimeout( function () {
let distance = Math.abs(window.scrollY - oldPos);
let angleRate = distance / (document.body.clientHeight - window.innerHeight);
let angle = angleRate * defaultAngle;
scrolls.forEach((scroll) => {
scroll.shakeSet(angle);
});
oldPos = window.scrollY;
}, 15);
});
}
class Scroll {
constructor(el) {
this.el = el; // 要素
this.currentAngle = 0; // 現在の振れ角
this.add = 1; // 振れ方向の正負
this.animationId;
this.animateFlg = false; // 振れているかどうか
this.angleRate = Math.random() + 0.5; // 要素ごとの振れ角のばらつき
this.animationInterval = 200 * Math.random() + 500; // 要素ごとの振れ速度
// 要素ごとに振れ速度を変えて、
el.style.transition = `transform ${this.animationInterval}ms ease-in-out`;
// インスタンス生成時に要素を揺らしています。
this.shakeSet(10);
}
shakeSet(angle) {
angle *= this.angleRate;
if(this.currentAngle < angle) {
this.currentAngle = angle;
if(!this.animateFlg) {
this.animateFlg = true;
this._shake();
}
}
}
_shake() {
this.animationId = setInterval(() => {
this.el.style.transform = `rotate(${this.currentAngle * this.add}deg)`;
this.add = this.add * -1;
if(this.currentAngle < minShakeAngle) {
this.el.style.transform = `rotate(0deg)`;
clearInterval(this.animationId);
this._clearVariable();
return;
}
this.currentAngle *= attenuation;
}, this.animationInterval);
}
_clearVariable() {
this.add = 1;
this.currentAngle = 0;
this.animateFlg = false;
}
}
動作は下記URLより