npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@soichiro_nitta/motion

v5.1.3

Published

https://www.npmjs.com/package/@soichiro_nitta/motion

Readme

@soichiro_nitta/motion

https://www.npmjs.com/package/@soichiro_nitta/motion

軽量な DOM トランジションユーティリティ。要素の transform/CSS を手早くアニメーションさせるためのミニマル API を提供します。

インストール

pnpm add @soichiro_nitta/motion

クイックスタート(Next.js 推奨構成)

  1. ID を共通モジュール(例: app/id.ts)で定義
import { createId } from '@soichiro_nitta/motion'

export const ID = createId(['BOX', 'TITLE'])

Next.js App Router ではこの ID モジュールを RSC/Client どちらからでも import できるため、ID を一箇所で管理できます。

  1. クライアント専用モジュールを用意(例: app/motion.ts
'use client'
import { createMotion } from '@soichiro_nitta/motion'
import { ID } from './id'

// `createMotion(ID)` により、第一引数の候補は `keyof typeof ID` になる
export const { motion } = createMotion(ID)
  1. コンポーネントで利用
'use client'
import { useEffectAsync } from '@soichiro_nitta/motion'
import { ID } from '@/app/id'
import { motion } from '@/app/motion'

const Page = () => {
  useEffectAsync(async () => {
    await motion.delay(0.1)
    await motion.to('BOX', 0.5, 'out', { opacity: '1' })
    await motion.to('TITLE', 0.5, 'out', { opacity: '1', translateY: '0px' })
  }, [])

  return (
    <div>
      <div id={ID.BOX.N} style={{ opacity: 0 }}>
        Hello
      </div>
      <h1 id={ID.TITLE.N}>Title</h1>
    </div>
  )
}

export default Page
  • useEffectAsyncasync/await をそのまま書けるようにするラッパーで、内部で void を付けたり Promise を捨てたりする必要がありません。
  • motion.run は即時実行のためのヘルパーで、(async () => { ... })() の代わりに motion.run(async () => { ... }) と書けます(返り値はそのまま Promise)。
  • Next.js 16 でのフル実装例(RSC + Client 分離)は検証リポジトリ motion-rsc-test を参照してください。

ID の使い方

createIdcreateMotion が返す ID はキーごとに N(id 文字列)と E()(DOM 参照)を持ち、motion.* で利用する第1引数のキーとも一致します。

  • ID.BOX.N: RSC でも安全な id 文字列。<div id={ID.BOX.N} /> のように使えます。
  • ID.BOX.E(): ブラウザで HTMLElement を返す関数。'use client' なモジュール内でのみ利用できます。
  • ID.BOX.E() は初回アクセス時に document.getElementById で取得した要素をキャッシュし、2 回目以降は同じ参照を返すため、繰り返し操作でも DOM 探索コストを抑えられます。
  • motion.to('BOX', …)'BOX' は上記キーのエイリアスで、DOM 探索をライブラリが肩代わりします。ID.BOX.E() で取得した要素を直接渡すことも可能です。
const elementBox = ID.BOX.E()
await motion.to(elementBox, 0.2, 'out', { opacity: '1' })
elementBox.style.borderRadius = '12px'

素の DOM での利用

import { createMotion } from '@soichiro_nitta/motion'

const { ID, motion } = createMotion(['BOX'])
await motion.to('BOX', 0.3, 'inout', { translateX: '20px', opacity: '0.8' })

API

  • createId(names: string[])

    • サーバー(RSC)でも安全に利用できる ID 辞書を返します。
    • 返り値: { ID }ID.NAME.N のみ保持)
  • createMotion(names: string[])

    • 指定した id 名、または createId が返す ID 辞書を受け取り、DOM アクセス付き ID と操作関数群 motion を返します。
    • 返り値: { ID, motion }
  • ID[name]

    • name に紐づく要素参照のヘルパ。ID.BOX.Nid 文字列、ID.BOX.E()HTMLElement を返します。
    • createMotion(ID) を利用すると motion.* の第一引数が keyof typeof ID(例: 'BOX' | 'TITLE') で補完されます。
  • motion.set(target, values)

    • 直ちにスタイルを適用します(トランジションなし)。transitionDuration 未指定時は 0s を自動セットします。
  • motion.run(task: () => Promise<unknown>)

    • 即時実行用のユーティリティ。(async () => { ... })() の代替として使えます。
    • await motion.run(async () => { await motion.to(...); }) のように await すれば、内部の最後の motion.to 完了まで待ってから次の行へ進みます。

即時関数パターンの置き換え例

useEffectAsync(async () => {
  const st = refShapeTop.current
  const tt = refTextTop.current
  const sb = refShapeBottom.current
  const tb = refTextBottom.current
  const u = refUnderline.current

  if (st && tt && sb && tb && u) {
    motion.run(async () => {
      await motion.to(st, 1, 'out', { scaleX: '1' })
      motion.set(tt, { opacity: '1' })
      motion.set(st, { transformOrigin: 'right center' })
      await motion.to(st, 1, 'out', { scaleX: '0' })
    })
    await motion.delay(0.2)
    motion.run(async () => {
      await motion.to(sb, 1, 'out', { scaleX: '1' })
      motion.set(tb, { opacity: '1' })
    })
  }
}, [])
  • motion.to(target, duration, easing, values)

    • 指定秒数で目的のスタイルにトランジションします。
    • targetHTMLElementcreateMotion(ID) 由来のキー列挙(例: 'BOX')を受け取ります。
    • easing: in | out | inout | bounce | linear
    • 変形は個別キーで指定します(例: translateX, rotate, scale)。複合 transform は渡さないでください。
    • 任意: options?: { signal?: AbortSignal } を渡すと、signal.abort()await motion.to(...) の待機を短絡できます(スタイル適用自体は行われます)。
  • motion.repeat(target, duration, values)

    • 同一トランジションを繰り返します。{ pause, play, stop, destroy } を返します。
    • pause(): 一時停止(内部のタイマー/RAF をキャンセル)
    • play(): 再開(短い間隔で pause()/play() をトグルしても多重ループ化しません)
    • stop()/destroy(): 完全停止+必要な場合は元の style に復帰(destroystop のエイリアス)
    • prefers-reduced-motion は利用側でガードしてください(例: if (matchMedia('(prefers-reduced-motion: reduce)').matches) return
  • motion.get(target, property)

    • 計算後スタイルを取得します。
  • motion.delay(seconds)

    • seconds 秒待機する Promise<void>
  • useEffectAsync(effect, deps)

    • async/await をそのまま書ける useEffect の薄いラッパー。effectPromise を返した場合も自動でハンドリングし、戻り値のクリーンアップがあれば通常の useEffect 同様に実行されます。

注意(Client-only)

  • createId は RSC から参照可能ですが、createMotion/motion.*/ID.*.E() はブラウザ専用です。
  • ブラウザ環境でない場合にクライアント API を呼び出すと例外を投げます。クライアント用モジュールには 'use client' を付与してください。