---
title: "useFrameIcon"
description: "注册帧动画图标的底层原语，处理样式重载补回与缺图兜底，由 SpriteImage / AnimatedImage 共用。"
seo_title: "useFrameIcon"
seo_description: "Low-level primitive that registers a frame-animated StyleImageInterface icon, handling style reload re-registration and missing-image fallback."
canonical_url: "https://mapbox.mhaibaraai.cn/docs/composables/use-frame-icon"
---
# useFrameIcon

> 注册帧动画图标的底层原语，处理样式重载补回与缺图兜底，由 SpriteImage / AnimatedImage 共用。

## 简介

`useFrameIcon` 是帧动画图标的底层原语：把一组逐帧 `ImageData` 注册为 `StyleImageInterface`，按 `fps` 或每帧 `durations` 循环播放，并自动处理样式重载补回、`styleimagemissing` 兜底与卸载清理。[MapboxSpriteImage](/docs/effects/sprite-image) 与 [MapboxAnimatedImage](/docs/effects/animated-image) 即基于它实现。

> \[\!NOTE\]
> 
> 在 
> 
> <MapboxMap>
> 
>  子树内调用（内部依赖 
> 
> useMap
> 
> ）。
> 
> frames
> 
>  / 
> 
> durations
> 
>  为取值函数，返回值变化即生效，便于异步解码后再填充。多数场景直接用 SpriteImage / AnimatedImage 组件即可，仅在自定义帧来源时才需此原语。

## 用法

子组件用 canvas 逐帧绘制扩散圆环，经 `useFrameIcon` 注册为 symbol 图标：

```vue [UseFrameIconExample.vue]
<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/dark-v11', center: [116.397, 39.908], zoom: 13 }">
      <UseFrameIconLayerExample />
    </MapboxMap>
  </div>
</template>
```

子组件实现（位于 `<MapboxMap>` 子树内）：

```vue
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'

const size = 64
const FRAME_COUNT = 30
const data: FeatureCollection = {
  type: 'FeatureCollection',
  features: [{ type: 'Feature', properties: {}, geometry: { type: 'Point', coordinates: [116.397, 39.908] } }]
}

function buildFrames(): ImageData[] {
  const canvas = document.createElement('canvas')
  canvas.width = size
  canvas.height = size
  const context = canvas.getContext('2d')!
  return Array.from({ length: FRAME_COUNT }, (_, i) => {
    const t = i / FRAME_COUNT
    context.clearRect(0, 0, size, size)
    context.beginPath()
    context.arc(size / 2, size / 2, (size / 2 - 3) * t, 0, Math.PI * 2)
    context.strokeStyle = `rgba(34, 211, 238, ${1 - t})`
    context.lineWidth = 3
    context.stroke()
    return context.getImageData(0, 0, size, size)
  })
}

const { map } = useMap()
const frames = ref<ImageData[]>([])
onMounted(() => {
  frames.value = buildFrames()
  map.value?.triggerRepaint()
})

useFrameIcon({ imageName: 'pulse-ring', size, frames: () => frames.value, fps: 24 })
</script>

<template>
  <MapboxSource source-id="pulse-pts" :source="{ type: 'geojson', data }">
    <MapboxLayer layer-id="pulse-pts" type="symbol" source="pulse-pts" :layout="{ 'icon-image': 'pulse-ring', 'icon-allow-overlap': true }" />
  </MapboxSource>
</template>
```

## API

### `useFrameIcon()`

注册帧动画图标，无返回值（副作用型）。

**options.imageName** (`string`) *required*: 注册到样式的图片名（symbol 图层 icon-image 引用）。

**options.size** (`number`) *required*: 纹理边长（像素）。

**options.frames** (`() => ImageData[]`) *required*: 当前帧序列（响应式取值）。

**options.fps** (`number`): 固定帧率，durations 缺省时生效。

**options.durations** (`() => number[] | undefined`): 每帧时长 ms（响应式取值），优先于 fps。

## Changelog

See commit history for [src/runtime/composables/useFrameIcon.ts](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/composables/useFrameIcon.ts).


## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
