---
title: 基础使用
summary: 介绍融合播放器的基础用法、Widget 系统、事件系统及自定义扩展。
---

# 基础使用

融合播放器是基于[轻量播放器](/cn/miniapp/solution-ai/ability/camera-solution/ipc/lite-player/base)再次封装的产物，支持可插拔的 Widget 组件，内置封装了一系列播放器控制的方法，开箱即用。

## 业务匹配

> 如果您的业务场景需要播放器里内置截图、录制、对讲、云台控制、电量显示等基础功能，无需额外开发，只需专注其它功能的交互, 那么**融合播放器**是您的最佳选择, 同时**融合播放器**也支持自定义扩展，可满足您的个性化需求。

### 安装依赖
```bash
yarn add @ray-js/ipc-player-integration
```

### 使用示例
```jsx
import { useCtx, IPCPlayerIntegration, Features } from '@ray-js/ipc-player-integration';

function Page() {
  const deviceId = '1234567890xxxxxx';
  
  const instance = useCtx({
    devId: deviceId,
  });
  return (
    <IPCPlayerIntegration
      instance={instance}
      devId={deviceId}
      style={{
        width: '100%',
        height: '300px'
      }}
    />
  )
}


```

### 获取播放器的数据
播放器向外暴露了很多数据，比如当前是否处于全屏模式、当前是否处于对讲中等，详情请参照 [useCtx](/cn/miniapp/solution-ai/ability/camera-solution/ipc/fusion-player/api/01-instance#返回参数-ctx) api 返回参数中的 `RetAtom` 类型的数据。
为了避免无关数据引起不必要的更新的问题，播放器暴露出的数据使用 `jotai` 包装了一层，要获取状态需要使用 `useStore` 进行读取才能取到真正的数据。

```jsx
import { useEffect } from 'react'
import { useCtx, useStore, IPCPlayerIntegration } from '@ray-js/ipc-player-integration';
import '@ray-js/ipc-player-integration/iconfont/iconfont.css';
 
function Page() {
  const deviceId = '1234567890xxxxxx';

  const instance = useCtx({
    devId: deviceId,
  });
  
  const { screenType } = useStore({ screenType: instance.screenType });

  return (
    <IPCPlayerIntegration
      instance={instance}
      devId={deviceId}
    />
  )
}
```


## Widget

**Widget** 是展示在播放器上的自定义组件。您可通过 useCtx 返回的实例方法，如 addContent、deleteContent，动态向播放器插入或移除 Widget。

在 **Widget** 的 props 中，会自动注入 [useCtx hook](/cn/miniapp/solution-ai/ability/camera-solution/ipc/fusion-player/api/01-instance#返回参数-ctx) 的上下文对象，便于组件内部访问播放器状态与操作接口。

### 部件分类

<Image width="400px" src="https://static1.tuyacn.com/static/tuya-miniapp-doc/_next/static/images/panel/fusion-player-demo.png"/>

将 **Widget** 主要分为了五个展示区域：

1. **topLeft**: 左上角区域
2. **topRight**: 右上角区域
3. **bottomLeft**: 左下角区域
4. **bottomRight**: 右下角区域
5. **absolute**: 绝对定位区域

图中展示的电池电量、温湿度、语音对讲、全屏切换等组件，都是平台已提供的通用 **Widget**。您可通过 API 便捷插入，示例如下:

### 使用示例

```jsx
import { useEffect } from 'react'
import { useCtx, IPCPlayerIntegration, Features } from '@ray-js/ipc-player-integration';
import '@ray-js/ipc-player-integration/iconfont/iconfont.css';
 
function Page() {
  const deviceId = '1234567890xxxxxx';
  const instance = useCtx({
    devId: deviceId,
  });

  useEffect(() => {
    // 初始化 Widget
    // 调用此方法后将为播放器添加项目所有使用到的 Widget
    Features.initPlayerWidgets(instance);
  }, [])

  return (
    <IPCPlayerIntegration
      instance={instance}
      devId={deviceId}
    />
  )
}
```

调用 `Features.initPlayerWidgets` 会给播放器添加与当前组件上相同的 Widget。

`Features.initPlayerWidgets` 方法会接收一些入参，详情请参照: [initPlayerWidgets](/cn/miniapp/solution-ai/ability/camera-solution/ipc/fusion-player/api/04-initPlayerWidgets)


### 添加自定义 Widget

在实际业务场景中，您可能不会使用全部内置的 `Widget`，而是更倾向于以下几种方式：

- ✅ 仅使用部分功能组件（如对讲、截屏）
- 🔧 基于内置组件扩展自定义功能
- 🛠️ 完全自定义实现，满足特定业务需求

接下来我们将通过构建一个 **Demo**，帮助您快速上手

- ✅ 使用播放器内置的 **语音对讲 Widget**
- 🛠️ 自定义实现一个 **截屏 Widget**
- 📸 截图成功后，在播放器中展示 **Toast 提示**

```jsx
// index.tsx
import React, { useEffect, useRef } from 'react';
import { View } from '@ray-js/components';
import {
  IPCPlayerIntegration,
  useCtx,
  useStore,
  Widgets,
  ComponentConfigProps,
} from '@ray-js/ipc-player-integration';
import Styles from './index.module.less';
// 手动引入一下css Widget 所需 icon
import '@ray-js/ipc-player-integration/iconfont/iconfont.css';

// 截图按钮 Widget
function ShotScreen(props: ComponentConfigProps) {
  const {
    IPCPlayerInstance,
    saveToAlbum,
    deleteContent,
    screenType: screenTypeAtom,
    addContent,
  } = props;
  const { screenType } = useStore({
    screenType: screenTypeAtom,
  });
  const timer = useRef(null);
  const timeToCloseToast = () => {
    clearInterval(timer.current);
    // @ts-ignore
    timer.current = setTimeout(() => {
      deleteContent('absolute', 'plugin-screenshot-toast');
    }, 5000);
  };

  const showShotToast = () => {
    // 为了确保只添加了一个 plugin-screenshot-toast 添加之前先删除一遍
    deleteContent('absolute', 'plugin-screenshot-toast');

    // 添加绝对定位的 Widget，
    // 定位信息由 absolutePosition 属性absoluteContentClassName 类型去定义
    addContent('absolute', {
      id: 'plugin-screenshot-toast',
      absoluteContentClassName: 'css name',
      absolutePosition: {
        position: 'absolute',
        top: '12px',
        left: '16px',
        right: '16px',
      },
      content: () => {
        return (
          <View
            style={{ color: '#fff', backgroundColor: '#000', width: '100%', textAlign: 'center' }}
          >
            ScreenShot Success
          </View>
        );
      },
    });

    // 实现 5s 后，从播放器中删除 plugin-screenshot-toast
    timeToCloseToast();
  };

  const handClick = () => {
    ty.authorize({
      scope: 'scope.writePhotosAlbum',
      success: success => {
        IPCPlayerInstance.snapshot({
          saveToAlbum,
          success: res => {
            console.log(res, 'res');
            showShotToast();
          },
        });
      },
    });
  };

  return (
    <View onClick={handClick} style={{ color: '#fff' }}>
      ScreenShot
    </View>
  );
}

export default function Page(props) {
  const devId = props.location.query.deviceId;
  const instance = useCtx({
    devId,
  });

  useEffect(() => {
    // 添加组件提供的 Widget
    instance.addContent('topLeft', {
      id: 'Intercom',
      content: Widgets.VerticalSmallIntercom,
    });
    // 添加自定义实现的 Widget
    instance.addContent('bottomLeft', {
      id: 'ScreenShot',
      content: ShotScreen,
    });
  }, []);

  return (
    <View className={Styles.container}>
      <View className={Styles.playerWarp} style={{ marginTop: 300 }}>
        <IPCPlayerIntegration
          style={{ width: '100%', height: '300px' }}
          playerFit="cover"
          instance={instance}
          devId={instance.devId}
        />
      </View>
    </View>
  );
}

```

### 效果示意图

<Image width="400px" src="https://static1.tuyacn.com/static/tuya-miniapp-doc/_next/static/images/panel/player-widget-demo.png"/>

关于 **Widget** 的更复杂一点的功能实现请参照：[Animation Widget](/cn/miniapp/solution-ai/ability/camera-solution/ipc/fusion-player/advanced-guide/animation-widget)

## 事件系统

事件系统采用发布订阅的形式实现，用于实现 **Widget** 之间的通信，或者从外部获取 **Widget** 传递的信息且解耦了它们之间的联系。

事件系统的实例，需要从 **useCtx** 返回的参数中读取，每一个播放器实例都拥有独立的事件系统

```jsx
import { useCtx, IPCPlayerIntegration, Features } from '@ray-js/ipc-player-integration';
 
function Page() {

  const deviceId = '1234567890xxxxxx';

  const instance = useCtx({
    devId: deviceId,
  });
  const { event } = instance;
  
}
```
### 类型定义
```typescript
type Task = (...args) => void;

export type EventInstance = {
  on: (type: string, cb: Task) => void;
  off: (type: string, cb: Task) => void;
  emit: (type: string, ...args) => void;
};
```

### 使用示例

以下两个 Widget 都被注册到了播放器中

**Widget1**

```jsx
import React, { useEffect } from 'react';
import { View } from '@ray-js/components';
import {
  ComponentConfigProps,
} from '@ray-js/ipc-player-integration';
function Widget1(props: ComponentConfigProps) {
  const { event } = props;
  const handClick = () => {
    // 发布事件
    event.emit('event1', 'hello')
  }
  return (
    <View onClick={handClick}></View>
  )
}
```
**Widget2**

```jsx
import React, { useEffect } from 'react';
import { View } from '@ray-js/components';
import {
  ComponentConfigProps,
} from '@ray-js/ipc-player-integration';
function Widget2(props: ComponentConfigProps) {
  const { event } = props;
  const listen = (e) => {
    console.log(e) // hello
  }
  useEffect(() => {
    // 监听事件
    event.on('event1', listen)
    return () => {
      event.off('event1', listen)
    }
  }, [listen])
  return (
    <View>hello world</View>
  )
}
```