Sprite Animator

export type SpriteAnimatorProps = {
  /** The start frame of the animation */
  startFrame?: number

  /** The end frame of the animation */
  endFrame?: number

  /** The desired frames per second of the animation. If set to 0 or negative, animation will be static */
  fps?: number

  /** The animation names of the spritesheet (if the spritesheet -with JSON- contains more animation sequences) */
  animationNames?: Array<string>

  /** The frame identifier to use, must be one of animationNames */
  frameName?: string

  /** The URL of the texture JSON (if using JSON-Array or JSON-Hash) */
  textureDataURL?: string

  /** The URL of the texture image */
  textureImageURL?: string

  /** Whether or not the animation should loop */
  loop?: boolean

  /** The number of frames of the animation (required if using plain spritesheet without JSON) */
  numberOfFrames?: number

  /** Animation auto-start when all assets are loaded */
  autoPlay?: boolean

  /** Event callback when the animation starts or restarts */
  onStart?: (data: AnimationEventData) => void

  /** Event callback when the animation ends */
  onEnd?: (data: AnimationEventData) => void

  /** Event callback when the animation completes a loop cycle */
  onLoopEnd?: (data: AnimationEventData) => void

  /** Event callback fired on each frame change */
  onFrame?: (data: AnimationEventData) => void

  /** @deprecated Use pause={false} instead. Control when the animation runs */
  play?: boolean

  /** Control when the animation pauses */
  pause?: boolean

  /** Whether or not the Sprite should flip sides on the x-axis */
  flipX?: boolean

  /** Sets the alpha value to be used when running an alpha test
   * @default 0.0
   */
  alphaTest?: number

  /** Displays the texture on a Billboard component always facing the camera.
   * @default false
   */
  asSprite?: boolean

  /** Allows for manual update of the sprite animation e.g: via ScrollControls.
   * Value should be between 0 and 1
   */
  offset?: number

  /** Allows the sprite animation to start from the end towards the start */
  playBackwards?: boolean

  /** Allows the animation to be paused after it ended so it can be restarted on demand via autoPlay */
  resetOnEnd?: boolean

  /** Array of Vector3-like positions for creating multiple instances of the sprite */
  instanceItems?: (THREE.Vector3 | [number, number, number])[]

  /** The maximum number of instances to render (for buffer size calculation)
   * @default 1
   */
  maxItems?: number

  /** Pre-parsed sprite data, usually from useSpriteLoader */
  spriteDataset?: {
    spriteTexture: THREE.Texture
    spriteData: SpriteData
  }

  /** Configuration options for the canvas context when loading textures */
  canvasRenderingContext2DSettings?: CanvasRenderingContext2DSettings

  /** Controls whether frame positions are rounded for precise pixel alignment.
   * Enable this if you notice slight texture bleeding between frames.
   * @default false
   */
  roundFramePosition?: boolean

  /** Additional properties to be passed to both mesh and instance components.
   * Only includes safe properties that work across both types.
   * @example { frustumCulled: false, renderOrder: 1 }
   * @see https://threejs.org/docs/#api/en/core/Object3D
   */
  meshProps?: CommonMeshProps
} & GroupProps

The SpriteAnimator component provided by drei is a powerful tool for animating sprites in a simple and efficient manner. It allows you to create sprite animations by cycling through a sequence of frames from a spritesheet image and JSON data.

Notes:

  • The SpriteAnimator component internally uses the useFrame hook from react-three-fiber (r3f) for efficient frame updates and rendering.
  • The sprites (without a JSON file) should contain equal size frames
  • Trimming of spritesheet frames is not yet supported
  • Internally uses the useSpriteLoader or can use data from it directly (which is the recommended way of loading assets)
<SpriteAnimator
  position={[-3.5, -2.0, 2.5]}
  startFrame={0}
  meshProps={{ frustumCulled: false, scale: 2.5 }}
  autoPlay={true}
  loop={true}
  numberOfFrames={16}
  textureImageURL={'./alien.png'}
/>

Load sprite textures via useSpriteLoader

const { spriteObj: statics } = useSpriteLoader('/statics.png', '/statics.json', ['heart', 'skull', 'sword'], null)

<SpriteAnimator
  position={[2, 2.8, 0.01]}
  fps={0}
  meshProps={{ frustumCulled: false, scale: 2.5 }}
  autoPlay={true}
  loop={true}
  flipX={false}
  startFrame={0}
  frameName={'sword'}
  spriteDataset={statics}
  asSprite={false}
  alphaTest={0.01}
/>

ScrollControls example

;<ScrollControls damping={0.2} maxSpeed={0.5} pages={2}>
  <SpriteAnimator
    position={[0.0, -1.5, -1.5]}
    startFrame={0}
    onEnd={doSomethingOnEnd}
    onStart={doSomethingOnStart}
    autoPlay={true}
    textureImageURL={'sprite.png'}
    textureDataURL={'sprite.json'}
  >
    <FireScroll />
  </SpriteAnimator>
</ScrollControls>

function FireScroll() {
  const sprite = useSpriteAnimator()
  const scroll = useScroll()
  const ref = React.useRef()
  useFrame(() => {
    if (sprite && scroll) {
      sprite.current = scroll.offset
    }
  })

  return null
}