🐩

# 要素をフワフワさせたい

2023/07/10に公開

そんなあなたにプテラノドン！

## 上下にフワフワ

``````.target {
animation: floating-y 1.8s ease-in-out infinite alternate-reverse;
}
@keyframes floating-y {
0% {
transform: translateY(-10%);
}
100% {
transform: translateY(10%);
}
}
``````

## 左右にもフワフワ

``````.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()

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)

}
``````
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()

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)