🐩
要素をフワフワさせたい
世の中には「要素をフワフワさせたい」という需要が一定数あります。要素を上下に動かすシンプルなアニメーションですが、いざ実装してみると自然にフワフワさせるのは案外難しい...と思われるかもしれません。
そんなあなたにプテラノドン!
上下にフワフワ
上下に動くキーフレームを作って、in-out 系のイージングを指定して alternate-reverse したらフワフワします。
.target {
animation: floating-y 1.8s ease-in-out infinite alternate-reverse;
}
@keyframes floating-y {
0% {
transform: translateY(-10%);
}
100% {
transform: translateY(10%);
}
}
左右にもフワフワ
上下の動きだけだと単調な場合は、少し左右の動きを足したら良くなるかもしれません。div で囲ってx方向に動かします。
.wrapper {
animation: floating-x 7.2s ease-in-out infinite alternate-reverse;
}
.target {
animation: floating-y 1.8s ease-in-out infinite alternate-reverse;
}
@keyframes floating-x {
0% {
transform: translateX(-5%);
}
100% {
transform: translateX(5%);
}
}
@keyframes floating-y {
0% {
transform: translateY(-10%);
}
100% {
transform: translateY(10%);
}
}
正弦波でフワフワ
さらに複雑なフワフワを実装したい場合、闇雲にHTMLのネストを深くするのは罪悪感があるので、JSを検討するタイミングかもしれません。JSでフワフワさせたいときは、正弦波に乗せてあげるとシンプルに実装できます。このサンプルコードでは若干の rotate を追加しました。
style を毎フレーム更新していますが、 2Dの canvas でもフワフワの考え方は同じです。
TypeScript
const fly = (target: HTMLElement) => {
const startTime = performance.now()
const amplitude = { x: 10, y: 10, rotation: -2 }
const speed = { x: 0.0004, y: 0.001 }
const tick = () => {
const diff = performance.now() - startTime
const x = amplitude.x * Math.sin(speed.x * diff)
const y = amplitude.y * Math.sin(speed.y * diff)
const rotation = amplitude.rotation * (1 + Math.sin(speed.y * diff))
target.style.transform = `rotate(${rotation}deg) translate(${x}%, ${y}%)`
requestAnimationFrame(tick)
}
tick()
}
const target = document.querySelector<HTMLElement>('.target')
if (target) {
fly(target)
}
JavaScript
const fly = (target) => {
const startTime = performance.now()
const amplitude = { x: 10, y: 10, rotation: -2 }
const speed = { x: 0.0004, y: 0.001 }
const tick = () => {
const diff = performance.now() - startTime
const x = amplitude.x * Math.sin(speed.x * diff)
const y = amplitude.y * Math.sin(speed.y * diff)
const rotation = amplitude.rotation * (1 + Math.sin(speed.y * diff))
target.style.transform = `rotate(${rotation}deg) translate(${x}%, ${y}%)`
requestAnimationFrame(tick)
}
tick()
}
const target = document.querySelector('.target')
if (target) {
fly(target)
}
インタラクティブにフワフワ
フワフワだけでは飽き足らず、画面いっぱいに動かしたい、かつクリックで加速させたいという要件が追加されたら...パワーで解決!横100%の動きはキーフレームの方が楽なので Web Animations API を使いました。
これであなたもプテラノドン!
TypeScript
const fly = (wrapper: HTMLElement, target: HTMLElement) => {
const amplitude = { y: 14, rotation: -2.5 }
const speed = { y: 0.001 }
const anime = wrapper.animate(
[{ transform: 'translateX(100%)' }, { transform: 'translateX(-100vw)' }],
{ duration: window.innerWidth * 24, iterations: Infinity}
)
let multiplier = 1
let previousTime = performance.now()
let yRadian = 0
const tick = () => {
const now = performance.now()
const diff = now - previousTime
previousTime = now
yRadian += multiplier * speed.y * diff
const y = amplitude.y * Math.sin(yRadian)
const rotation = amplitude.rotation * (1 + Math.sin(yRadian))
target.style.transform = `rotate(${rotation}deg) translateY(${y}%)`
requestAnimationFrame(tick)
}
tick()
return {
faster: () => {
multiplier += 1
anime.updatePlaybackRate(multiplier)
}
}
}
const wrapper = document.querySelector<HTMLElement>('.wrapper')
const target = document.querySelector<HTMLElement>('.target')
if (wrapper && target) {
const flyingPteranodon = fly(wrapper, target)
document.body.addEventListener('click', () => flyingPteranodon.faster())
}
JavaScript
const fly = (wrapper, target) => {
const amplitude = { y: 14, rotation: -2.5 }
const speed = { y: 0.001 }
const anime = wrapper.animate(
[{ transform: 'translateX(100%)' }, { transform: 'translateX(-100vw)' }],
{ duration: window.innerWidth * 24, iterations: Infinity}
)
let multiplier = 1
let previousTime = performance.now()
let yRadian = 0
const tick = () => {
const now = performance.now()
const diff = now - previousTime
previousTime = now
yRadian += multiplier * speed.y * diff
const y = amplitude.y * Math.sin(yRadian)
const rotation = amplitude.rotation * (1 + Math.sin(yRadian))
target.style.transform = `rotate(${rotation}deg) translateY(${y}%)`
requestAnimationFrame(tick)
}
tick()
return {
faster: () => {
multiplier += 1
anime.updatePlaybackRate(multiplier)
}
}
}
const wrapper = document.querySelector('.wrapper')
const target = document.querySelector('.target')
if (wrapper && target) {
const flyingPteranodon = fly(wrapper, target)
document.body.addEventListener('click', () => flyingPteranodon.faster())
}
▼ 素材
・プテラノドン - iStockで購入
・宇宙(NASA) - Unsplash
▼ こちらの商品もおすすめ
プテラノ丼
Discussion