前端实现儿童故事主播数字人:从 SDK 接入到交互落地

摘要

本文基于魔珐星云具身智能 SDK,详细讲解少儿故事主播数字人的搭建全流程。该数字人主打睡前故事、儿童启蒙、国学小故事、儿歌互动功能,采用可爱治愈的卡通画风,适配低龄儿童审美。全程无需 3D 建模、专业渲染能力,普通开发者或教育从业者均可快速落地,适合儿童早教、亲子陪伴、少儿内容创作等场景。


一、项目背景与场景定位

随着少儿早教市场快速发展,亲子陪伴、睡前故事、启蒙教育需求持续增长。传统图文、音频内容形式单一,缺乏互动性;而真人主播成本高、内容量产效率低。

少儿故事主播数字人依托具身智能技术,打造卡通可爱、互动性强、内容量产的 AI 主播,核心场景如下:

  • 睡前故事:安徒生童话、格林童话、原创睡前小故事

  • 儿童启蒙:拼音、汉字、数字、颜色、认知科普

  • 国学小故事:三字经、弟子规、传统美德小故事

  • 儿歌互动:经典儿歌演唱、律动互动、歌词讲解

  • 亲子陪伴:日常互动、情绪安抚、趣味问答

数字人风格采用圆润卡通造型、明亮柔和配色、夸张可爱表情,贴合儿童审美,提升孩子接受度与互动意愿。


二、技术原理与核心优势

2.1 技术原理

本项目基于魔珐星云云端大脑 + 多模态感知 + 表达引擎三层架构,打通 “文本理解→语音合成→表情动作驱动→实时渲染” 全链路:

  • 云端大脑:对接大模型,生成少儿友好的故事 / 启蒙文案

  • 多模态感知:接收文本指令,驱动语音与动作

  • 表达引擎:实时生成卡通数字人表情、手势,低延迟(<500ms)同步口型

2.2 核心优势

  • 零建模门槛:平台内置少儿卡通形象库,直接选用无需建模

  • 低代码开发:Vue3 轻量框架,代码简洁易修改

  • 可爱适配:专属童声音色、萌系动作、柔和配色

  • 内容易做:少儿文案简短口语,批量生成效率高

  • 跨端适配:网页 / 平板 / 手机均可访问,适配亲子场景


三、前期准备(合规开发流程)

3.1 平台注册与应用创建

  1. 访问魔珐星云官网,注册个人开发者账号

  2. 登录控制台,进入「应用管理」→「创建驱动应用」

  3. 选择少儿卡通风格数字人形象,配置童声音色(温柔亲切)

  4. 生成并保存开发凭证:appIdappSecret

注:以上为平台标准开发流程,仅用于个人学习与非商业项目测试。

3.2 开发环境

  • Node.js 16+

  • Vue3 + Vite

  • VS Code 编辑器

  • Chrome/Edge 浏览器

3.3 项目结构

plaintext

vue-kids-storyteller/
├── src/
│   ├── components/
│   │   └── KidsStory.vue  # 少儿主播主界面
│   ├── services/
│   │   ├── xingyun.js     # 星云SDK封装
│   │   └── llm.js         # 文案生成服务
│   └── main.js
├── index.html
└── package.json

四、核心代码实现(可直接复制)

4.1 SDK 封装(src/services/xingyun.js)

javascript

运行

import { onUnmounted } from 'vue'let sdkInstance = nulllet isInit = falseexport function useXingyun() {// 初始化SDKconst init = async (appId, appSecret, containerId, callbacks) => {if (isInit) returntry {
      sdkInstance = new window.XmovAvatar({
        appId,
        appSecret,container: `#${containerId}`,gatewayServer: 'https://nebula-agent.xingyun3d.com/user/v1/ttsa/session',socket_io_url: 'wss://gateway.xingyun3d.com',onStateChange: callbacks.onStateChange,onSubtitle: callbacks.onSubtitle,onError: callbacks.onError})
      isInit = true} catch (e) {console.error('SDK初始化失败', e)}}// 播报文本const speak = (text) => {if (!sdkInstance || !isInit) return
    sdkInstance.speak(text, true, true)}// 销毁实例const destroy = () => {if (sdkInstance) sdkInstance.destroy()
    isInit = false}onUnmounted(destroy)return { init, speak }}

4.2 主界面组件(src/components/KidsStory.vue)

vue

<template>
  <div class="kids-container">
    <!-- 顶部标题 -->
    <header class="header">
      <div class="title">
        <h1>🌟 少儿故事主播</h1>
        <p>睡前故事|儿童启蒙|国学小故事|儿歌互动</p>
      </div>
      <div class="status" :class="{ ready: isReady }">
        {{ isReady ? '已上线' : '准备中' }}
      </div>
    </header>

    <!-- 主内容区 -->
    <div class="main-box">
      <!-- 数字人渲染区 -->
      <div class="avatar-wrap" id="kids-avatar"></div>

      <!-- 交互区 -->
      <div class="chat-wrap">
        <!-- 快捷按钮 -->
        <div class="quick-btns">
          <button @click="sendQuick('讲一个睡前小故事')">睡前故事</button>
          <button @click="sendQuick('教我认识颜色')">颜色启蒙</button>
          <button @click="sendQuick('三字经小故事')">国学启蒙</button>
          <button @click="sendQuick('唱一首小星星')">儿歌互动</button>
        </div>

        <!-- 对话记录 -->
        <div class="chat-history" ref="chatRef">
          <div v-for="(item, idx) in history" :key="idx" :class="['msg', item.role]">
            {{ item.content }}
          </div>
        </div>

        <!-- 输入区 -->
        <div class="input-bar">
          <input 
            v-model="inputText" 
            placeholder="输入想听的故事/问题..."
            @keyup.enter="sendMsg"
          >
          <button @click="sendMsg">发送</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick, onMounted } from 'vue'
import { useXingyun } from '../services/xingyun'

const { init, speak } = useXingyun()
const isReady = ref(false)
const inputText = ref('')
const history = ref([])
const chatRef = ref(null)

// 初始化数字人
onMounted(async () => {
  await init(
    '你的appId',
    '你的appSecret',
    'kids-avatar',
    {
      onStateChange: (state) => {
        if (state === 'speak') isReady.value = true
      },
      onSubtitle: (text) => addMsg('avatar', text),
      onError: (e) => console.error('错误', e)
    }
  )
  addMsg('avatar', '小朋友你好呀~我是你的故事主播,想听故事、儿歌还是启蒙知识,都可以告诉我哦!')
})

// 添加消息
const addMsg = (role, content) => {
  history.value.push({ role, content })
  nextTick(() => {
    chatRef.value.scrollTop = chatRef.value.scrollHeight
  })
}

// 发送消息
const sendMsg = async () => {
  if (!inputText.value.trim()) return
  const text = inputText.value
  addMsg('user', text)
  inputText.value = ''

  // 模拟少儿友好回复(可对接大模型)
  let reply = ''
  if (text.includes('睡前故事')) reply = '好哒,从前有一只可爱的小兔子,住在森林里...'
  else if (text.includes('颜色')) reply = '小朋友,红色是苹果,黄色是香蕉,蓝色是天空哦~'
  else if (text.includes('三字经')) reply = '人之初,性本善。性相近,习相远...'
  else if (text.includes('儿歌')) reply = '一闪一闪亮晶晶,满天都是小星星~'
  else reply = '这个问题问得真好,我们一起来学习吧~'

  addMsg('avatar', reply)
  speak(reply)
}

// 快捷发送
const sendQuick = (text) => {
  inputText.value = text
  sendMsg()
}
</script>

<style scoped>
.kids-container {
  width: 100vw;
  height: 100vh;
  background: #fff0f6;
  display: flex;
  flex-direction: column;
  font-family: "Microsoft YaHei", sans-serif;
}

.header {
  background: #ff87ab;
  color: white;
  padding: 18px 30px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-radius: 0 0 20px 20px;
}

.title h1 { margin: 0; font-size: 24px; }
.title p { margin: 5px 0 0; font-size: 14px; opacity: 0.9; }

.status {
  padding: 8px 20px;
  background: #ffe0eb;
  border-radius: 20px;
  font-size: 14px;
}

.status.ready {
  background: #4ade80;
  color: white;
}

.main-box {
  flex: 1;
  display: flex;
  padding: 30px;
  gap: 30px;
}

.avatar-wrap {
  width: 450px;
  background: #ffe6f0;
  border-radius: 24px;
  overflow: hidden;
  box-shadow: 0 8px 20px rgba(255, 135, 171, 0.15);
}

.chat-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.quick-btns {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

.quick-btns button {
  padding: 14px;
  background: #ff87ab;
  color: white;
  border: none;
  border-radius: 12px;
  font-size: 15px;
  cursor: pointer;
  transition: all 0.2s;
}

.quick-btns button:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(255, 135, 171, 0.3);
}

.chat-history {
  flex: 1;
  background: white;
  border-radius: 20px;
  padding: 25px;
  overflow-y: auto;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.05);
}

.msg {
  margin-bottom: 18px;
  line-height: 1.6;
  font-size: 15px;
}

.msg.user {
  text-align: right;
  color: #ff87ab;
}

.msg.avatar {
  color: #333;
}

.input-bar {
  display: flex;
  gap: 15px;
}

.input-bar input {
  flex: 1;
  padding: 15px 20px;
  border: 2px solid #ffe0eb;
  border-radius: 12px;
  font-size: 15px;
  outline: none;
}

.input-bar button {
  padding: 15px 30px;
  background: #ff87ab;
  color: white;
  border: none;
  border-radius: 12px;
  font-size: 15px;
  cursor: pointer;
}
</style>

五、项目运行与调试

5.1 安装依赖

bash

运行

npm install

5.2 启动项目

bash

运行

npm run dev

访问:http://localhost:3000,即可看到可爱的少儿数字人主播。

5.3 常见问题排查

  1. 数字人不显示:核对appId/appSecret,检查网络

  2. 无语音播报:确认浏览器音频权限,检查 SDK 初始化状态

  3. 文案生硬:优化提示词,增加 “简单、可爱、口语化” 要求


六、内容创作与扩展

在这里插入图片描述

6.1 少儿文案创作技巧

  • 睡前故事:简短温暖、主角可爱、结局美好

在这里插入图片描述

  • 启蒙知识:短句、重复、举例生活化

  • 国学内容:节选经典、通俗解释、简单道理
    在这里插入图片描述

6.2 功能扩展方向

  • 多风格形象:切换兔子、小熊等卡通形象

  • 定时故事:设置睡前定时自动播放

  • 亲子互动:支持家长录入故事,数字人复刻播报

  • 短视频生成:一键生成故事口播短视频

七、总结

本文详细讲解了少儿故事主播数字人的搭建流程,基于魔珐星云 SDK,无需专业建模和渲染能力,普通开发者即可快速实现睡前故事、儿童启蒙、国学小故事、儿歌互动等功能。

数字人采用可爱卡通风格,贴合儿童审美,文案简单易做,适合个人创作者、教育从业者搭建少儿内容 IP,也可用于亲子陪伴、早教工具开发等场景。

Logo

一站式 AI 云服务平台

更多推荐