# 腾讯视频互动视频 | 开发指南

# 起步

注意

  • 开始教程前,确保您有 Vue2 的基础
  • 组件的编辑-预览-调试-发布都是在线的,只需要在Chrome浏览器完成开发
  • 官方组件的源码已开放在 Github

# 简介

创作平台上线以后,创作者提出了非常多新颖的互动组件需要平台支持,所以平台开放出互动开放平台,创作者可以根据这篇文档开发自定义互动组件并发布到创作平台上

两个平台的功能需要明确

  • 开放平台 平台职责是生产组件,创作者在该平台开发组件

  • 创作平台 平台职责是生产互动剧,创作者将组件配置到互动剧中

# 快速上手

运行后可以看到如下的效果:

# 开发自定义组件

本教程以开发气泡组件为例。 最终效果如下:

组件开发分为两部分:

  • 组件属性配置 组件的哪些部分是可配置的

  • 组件代码编写 组件内部的逻辑功能实现

# 组件表单配置

比如气泡组件,需要配置的属性包括:

  • x 气泡(左上角)坐标

  • y 气泡(左上角)坐标

  • width 气泡宽度

  • height 气泡高度

  • text 气泡内容

那么,我们需要如下的表单去配置一个气泡: 

生成表单的功能集中在界面右侧,分为 配置属性预览表单 两个Tab。顾名思义,表单需要的控件通过 配置属性 来完成,配置后即可 预览表单

配置属性

创建完后,切换到预览表单,可以看到表单生成好了:

注意

系统内置了下面几个属性,不需要额外配置:

  • x 组件(左上角)坐标

  • y 组件(左上角)坐标

  • width 组件宽度

  • height 组件高度

  • startTime 组件出现时间

  • endTime 组件消失时间

# 组件代码编写

代码编辑器本身采用monaco-editor,参考 vscode快捷键

表单已经开发完了,那么组件如何去引用表单里的数据?下面是一张示意图:

每一个属性都对应着vue组件里面的一个prop属性。为了方便开发,开发者不需要再给vue组件写props。组件会根据表单的属性自动生成props,并注入到组件中。

当然,系统内置的属性(x, y, width, height, startTime, endTime)也会以prop属性注入到组件中,开发者无需重复定义。

接下来实战一下。 系统默认会提供了一段初始代码:

<template>
<div
  class="container"
  v-show="visible"
  :style="{
    top: y + '%',
    left: x + '%',
    width: width + '%',
    height: height + '%'
  }">
  Hello Component!
</div>
</template>
<script>
export default {
  mounted() {
    this.show();
  }
};
</script>
<style>
.container {
  position: absolute;
}
</style>

注意

  • 组件默认是使用绝对定位,如果组件有特殊需求,可以调整为其他样式

  • 组件使用系统内置属性x, y, width, height设置位置和大小,这样组件的大小和位置就可以由表单控制。(组件也可以不使用系统内置属性,视具体需求而定。)

  • visible是组件内置的状态值,表示组件此时的显隐情况

  • show是组件内置的api函数,调用后visible=true

基于初始代码,开发气泡组件,开发步骤分为:

  • 样式开发

  • 逻辑开发

样式开发 需要修改html和css:

<template>
<div
  class="bubble"
  v-show="visible"
  :style="{
    top: y + '%',
    left: x + '%',
    width: width + '%',
    height: height + '%'
  }">
  <span class="bubble-text">{{text}}</span>
</div>
</template>
<script>
// ...
</script>
<style>
.bubble {
  position: absolute;
  display: table;
  border-radius: 25px;
  background-color: rgba(0, 0, 0, 0.5);
  border: 1px solid white;
  text-align: center;
}
.bubble-text {
  display: table-cell;
  vertical-align: middle;
}
</style>

注意

点击“运行”按钮,或者按快捷键ctrl+r(mac也可以为command+r),即可运行组件。

效果如下:

预览表单 Tab输入气泡内容,播放器会同步更新数据哦~

关闭播放器,开始逻辑开发。逻辑功能是用户点击,气泡消失。所以只需要增加一个点击事件让组件消失即可,实现代码如下:

<template>
<div
  class="bubble"
  @click="onClick"
  ...>
  ...
</div>
</template>
<script>
export default {
  mounted() {
    this.show();
  },
  methods: {
    onClick() {
      this.hide();
    }
  }
};
</script>

再次运行,气泡就可以点击关闭了

# 运行调试

在当前页面载入播放器运行的方式能快速的看到组件开发的效果,但是却无法调试移动端特性,例如touch事件无法调试 于是系统提供了三种运行方式供开发者调试,开发者按需使用:

  • 本页面直接运行(不再累述)

  • 通过手机扫码,在手机浏览器上运行

    • step1

    • step2

    • step3 手机调试支持livereload,直接修改代码,点击运行,手机就会自动刷新

  • 新开浏览器标签页运行

    • 流程和移动端差不多

# 进阶指南

进阶指南主要介绍系统提供的附加功能的使用

# 组件显示隐藏控制

# • 必须使用show/hide函数

上述的demo代码中,频繁出现this.show/hide的调用,这两个函数就是组件显示/隐藏的控制开关。但为什么要调用这个函数去控制组件显示/隐藏,而不是直接控制顶层div的样式?原因如下: 如上图,绿色箭头表示一个视频随着时间往后播放。而这个视频上有两个组件,组件的花括号表示组件的展示的时间段。蓝色箭头表示组件触发show或者hide函数,这其实是在告诉互动播放器,在组件不展示的时间段内,播放器可以展示进度条以及其他播放器相关的UI。简而言之,组件和播放器上的控件是互斥的

那么,组件不调用show或hide会带来什么问题?假如上图组件1没有调用hide,那么互动播放器就会认为组件1一直没有消失,播放器进度条就展示不出来,如下图:

注意

注意:这就是为什么明明组件div已经隐藏了,但是互动播放器上依然点不出进度条点原因!!!

# • 平台建议写法

所有组件都要依赖组件内置的属性visible来控制显示和隐藏,且要通过show/hide两个函数控制visible属性的变化。举个例子:

<template>
    <div v-show="visible"></div>
</template>

<!-- 如果组件需要渐变动画 -->
<template>
    <div class="mycls" :style="{opacity: visible ? 1 : 0}" />
</template>
<style>
.mycls {
    transition: opacity .2s ease;
}
</style>

show/hide两个函数除了改变visible的值,还会把组件显隐状态通知给互动播放器。

# 自定义组件销毁时机

组件默认是在当前视频播放结束后销毁,但如果组件希望在视频跳转时展示一个过渡效果,那么就可以自定义组件的销毁时机。

# • 如何自定义组件销毁时机

自定义组件销毁时机分为两个步骤: ● 通知引擎在视频结束时,不要销毁此组件 ● 组件到了需要销毁的时机,通知引擎销毁它 代码如下

<template>
  <div
    class="container"
    :style="{
      top: y + '%',
      left: x + '%',
      width: width + '%',
      height: height + '%',
      opacity: visible ? 1 : 0
    }"
    @click="onClick"
  >
  点我跳转
  </div>
</template>
<script>
export default {
  mounted() {
    this.show();
    // 步骤1 通知引擎在视频结束时,不要销毁当前组件
    this.$emit('interruptdestroy');
  },
  methods: {
    onClick() {
      // action是配置跳转行为
      this.bridge.action(this.action);
      this.visible = false;
      setTimeout(() => {
        // 步骤2 通知引擎销毁当前组件
        this.$emit('requestdestroy');
      }, 3000);
    }
  }
};
</script>
<style>
.container {
  position: absolute;
  transition: opacity 3s ease;
  background: red;
}
</style>

效果如下:

# 属性的配置

  • 属性名 默认会自动生成,但建议改成业务相关的变量名,便于理解
  • 类型 决定属性的数据结构以及表单控件的类型
  • 属性含义 表单控件对应的label文本,方便用户理解表单的含义。例如:

# 素材列表的使用

开发组件的过程中,可能需要在代码中使用图片或音频。系统提供了素材列表的功能,开发者可以上传素材,并复制链接到相应的代码区。

素材列表的入口在界面右上方

打开素材列表后的界面如下,使用教程:

# 第三方js/css库的引用

第三方js/css库的引用的功能在素材列表功能中。

# • 创建库

以elementui为例。elementui需要引入elementui的js和css,将它们上传到平台。 所有上传的脚本有个审核的过程,脚本审核通过后就可以正常使用了。(PS:审核通过后需要重新打开素材列表面板才会刷新列表。)

创建好的库可以在其他组件中使用,无需重复上传,运行时也不会重复加载同一个库

# • 使用库

平台提供了两种库的引用的方式:

  • 初始化时加载 用户打开互动播放器时就会加载此脚本(默认)
  • 按需加载 用到该组件时才会加载相应的库

引用库的方式:

  • 选中需要的库
  • 关闭素材列表面板
  • 点击运行即可生效

素材列表选择示例图:

组件代码(就是加了el-button,其余代码省略):

<template>
  <div class="container" ....>
    <el-button>Button</el-button>
  </div>
</template>
<script>
 // ...
</script>

运行结果:

由于elementui的vue组件必须在vue实例化之前注册,所以elementui不能用按需加载,否则elementui的组件将不会生效,其他的库视情况而定

# 组件控制视频跳转

  • 在配置属性tab新增属性action,类型为行为
  • 在预览表单tab设置视频跳转到测试视频1,在实际互动剧中,这里可以选择故事线中的视频

写一个简单的点击跳转逻辑

<template>
  <div
    class="container"
    :style="{
      // ...省略
    }"
    @click="onClick"
  >click me!!!
  </div>
</template>
<script>
export default {
  mounted() {
    this.show();
  },
  methods: {
    onClick() {
      // this.action则是上面表单配置的内容
      this.bridge.invoke('engine', 'action', this.action);
    }
  }
};
</script>
<style>
.container {
  position: absolute;
}
</style>

# 数值的使用

我们引入了数值体系的概念来解决组件间通信的问题:

由图可以看出,图中有两个数值,一个叫 apple,一个叫 banana组件4监听数值 apple 的变化,而 组件1 通过某种交互触发 apple=apple+1 ,从而修改了 apple 的值,于是数值体系就会将 apple 的变化通知给 组件4 ,达到通信的效果。 数值相关的JSApi(参考JSApi使用指南)有:

  • action 执行数值表达式,从而修改数值

  • listenValue 监听某个数值的变化

  • listenCondition 监听某个条件表达式的变化,举例:apple > 1

# • 如何监听数值

实践一下,举个背包物品组件的例子说明数值的使用方法。假设一个场景,有个物品是苹果,UI上需要实时展示苹果数量。步骤如下:

  • 配置属性 Tab新增属性vkey,类型为 数值

  • 预览表单 Tab新增一个数值叫苹果,并且把属性vkey设置为苹果

  • 组件代码部分监听数值变化,并把数量展示在UI上
<template>
    <div
        class="container"
        :style="{
            top: y + '%',
            left: x + '%',
            width: width + '%',
            height: height + '%',
            backgroundImage: `url(http://ivimgbucket-30295.sz.gfp.tencent-cloud.com/ivopenplatimg/1061215784665206140642842FA3775A8292C1083B321E4223778464.png)`
        }"
    >
    {{ count }}
    </div>
</template>
<script>
export default {
    data() {
        return {
            count: 0
        };
    },
    mounted() {
        this.show();
        this.bridge.listenValue(this.vkey, val => {
            this.count = val;
        });
    }
};
</script>
<style>
.container {
    position: absolute;
    background-size: 100% 100%;
    text-align: center;
    font-size: 500%;
    line-height: 155px;
}
</style>

效果如下:

注意

  • 例子中的数值列表是开发组件的时候调试用的,实际互动作品创作需要在作品创作平台创建。

  • 数值外显名称只是用来UI显示用的,数值实际的变量名是系统会自动为它生成的,开发者无需关心。

# • 如何修改数值

那么组件如何去修改苹果数量呢?为了方便演示,把修改数值和监听数值都做在同一个组件里。 在上面的例子的基础上加点功能:点击物品,物品的数量会加1。修改步骤如下:

  • 配置属性 Tab新增属性action,类型为 行为

  • 预览表单 Tab设置行为的值为 苹果+1

  • 代码修改如下(篇幅有限,所以用省略号代替部分代码):
<template>
    <div
        ...
        @click="plus"
    >
    {{ count }}
    </div>
</template>
<script>
export default {
    ...

    methods: {
        plus() {
            this.bridge.invoke('engine', 'action', this.action);
        }
    }
};
</script>

效果如下:

# • 如何监听数值条件

再次改造上述例子,演示数值条件的使用,场景为:当数值大于3时,字体变红。修改步骤如下:

  • 配置属性 Tab新增属性 condition,类型为 数值条件

  • 预览表单 Tab设置行为的值为 苹果>3

  • 代码修改如下(篇幅有限,所以用省略号代替部分代码):
<template>
    <div
        ...
        :style="{
            ...
            color: red ? 'red' : ''
        }"
        ...
    >
    {{ count }}
    </div>
</template>
<script>
export default {
    data() {
        return {
            count: 0,
            red: false
        };
    },
    mounted() {
        ...
        this.bridge.listenCondition(this.condition, red => {
            this.red = red;
        });
    },
    ...
};
</script>

效果如下:

# • 变量类型

数值分为:

  • 普通变量

  • 永久变量

区别在于:

  • 普通变量随着存档,存档删除了,普通变量会重置

  • 永久变量则相反,存档删除不会重置永久变量,例如成就/称号这样的可以用永久变量实现

# • 创建故事线

需要开发者自己实现一个 story.js,需要实现以下功能:

在window上挂载一个全局构造函数VideoInteractOverview, 并实现以下接口:

// 以es6为例,需要最终转换成es5的代码
export default class VideoInteractOverview {
  /**
   * @construct
   * @param conf {Object} 初始化参数
   * @param conf.mode {String} 请求后台接口的模式. 默认: '', 可选: 'test',当传入test时,希望请求后台测试接口
   * @param conf.el {String} 故事线被挂载的dom节点选择器。例如: '#overview'
   * @param conf.appId {String} 互动剧的appid
   * @param conf.dramaId {String} 互动剧的剧id
   * @param conf.source {String} 预览模式时需要带上的请求字符串. 默认: '', 可选值: 'preview'
   * @param conf.preview_session {String} 预览模式时需要带上的session. 默认: ''
   **/
  construct(conf) {
    // 在这里实现初始化故事线的逻辑
  }

  /**
   * @name show
   * @description 外部调用展示故事线,例如: 故事线按钮
   **/
  show() {
    // 在这里实现渲染故事线的逻辑
  },

  /**
   * @name hide
   * @description 外部调用隐藏故事线,例如: 故事线按钮
   **/
  hide() {
    // 在这里实现隐藏故事线的逻辑
  },

  /**
   * @name onClose
   * @description 外部监听故事线内部的关闭事件
   * @param callback { Function } 故事线关闭时需要触发的回调函数
   **/
  onClose(callback) {
    // 在这里将callback加入到关闭事件的回调队列中
  },

  /**
   * @name onSelectView
   * @description 外部监听故事线选择某个分支剧情事件
   * @param callback { Function } 故事线选择某个分支剧情时需要触发的回调函数
   **/
  onSelectView(callback) {
    // 在这里将callback加入到点击分支剧情事件的回调队列中
    // 回调函数需要传入以下参数: 
    // {
    //   branch_id, // 表示当前选择的分支id
    //   video_id, // 当前视频的vid
    // }
    // 例如
    // callback({branch_id: 12345678, video_id: 'abcdefg'})
  },

  /**
   * @name selectBranch
   * @description 外部调用切换分支剧情
   * @param conf { Object } 切换分支剧情的参数
   * @param conf.branch_id 分支剧情的id
   **/
  selectBranch(conf) {
    // 在这里实现切换分支后渲染新的分支的逻辑
  }
}

# 组件引用

组件可以引用其他的组件,组件的引用也是严格遵循vue的语法。本节依然是举例介绍组件引用。

# • 简单引用

例子:写一个组件,引用官方组件“气泡”,使“气泡”出现的时候有个淡入动画,且气泡内容可以由外部设置。步骤如下:

  • 配置属性 Tab新增属性child,类型为 组件 ,组件为 气泡

  • 在预览表单tab设置子组件的气泡内容为:这是一个淡入的气泡

  • 代码如下:
<template>
    <div
        class="container"
        :style="{
            top: y + '%',
            left: x + '%',
            width: width + '%',
            height: height + '%',
            opacity: visible ? 1 : 0
        }"
    >
        <V1392548344947063822
            v-bind="child"
            :startTime="startTime"
            :endTime="endTime"
            :width="100"
            :height="100"
        />
    </div>
</template>
<script>
export default {
    mounted() {
        this.bridge.on('player', 'timeupdate', time => {
            if (time >= this.startTime && time <= this.endTime) {
                this.show();
            } else {
                this.hide();
            }
        });
    }
};
</script>
<style>
.container {
    position: absolute;
    transition: opacity 1s ease;
}
</style>

效果如下:

注意

父组件可以把子组件的普通属性配置child通过v-bind传递给子组件;而内置属性需要根据具体情况设置给子组件 PS:内置属性包括(x,y,width,height,startTime,endTime)

# • 引用多个同类型组件

例子:写一个组件,引用官方组件“气泡”,使组件内部可以展示多个气泡,气泡数量和内容可以由外部设置。步骤如下:

  • 配置属性 Tab新增属性children,类型为 多组件,组件为 气泡

  • 预览表单 Tab设置子组件的气泡内容为:气泡1

  • 代码如下:
<template>
    <div
        class="container"
        :style="{
            top: y + '%',
            left: x + '%',
            width: width + '%',
            height: height + '%'
        }"
    >
        <V1392548344947063822
            v-for="(child, i) in children"
            v-bind="child"
            :startTime="startTime"
            :endTime="endTime"
            :x="i * 100 / children.length"
            :width="100 / children.length"
            :height="100"
        />
    </div>
</template>
<script>
export default {
    mounted() {
        this.show();
    }
};
</script>
<style>
.container {
    position: absolute;
}
</style>

效果如下:

# JSApi使用指南

在组件中,开发者可以通过 this.bridge 提供的接口和事件,实现更灵活的逻辑。下面提供一个简单的demo。

// 视频播放的第10s的时候执行action

export default {
  data() {
    return {
        hasAction: false
    };
  },
  mounted() {
    this.bridge.on('player', 'timeupdate',(time)=>{
        if(time >= 10 && !this.hasAction) {
            this.bridge.invoke('engine', 'action', this.action);
            this.hasAction = true;
        }
    })
  }
};

# 常用API文档

  • action
/**
 * @description 通过action方法,执行配置的“行为”
 * {Object} actions 行为
 * {
 *    jump, // 分支切换
 *    expression  // 执行表达式
 * }
*/

this.bridge.invoke('engine', 'action', actions) // 传入需要执行的行为
  • pause
/**
 * @description 暂停当前播放
*/

this.bridge.invoke('player', 'pause');
  • play
/**
 * @description 继续当前播放
*/

this.bridge.invoke('player', 'play');
  • seek
/**
 * @description 通过seek方法,跳到视频的指定进度
 * {Number} time 单位 秒
*/

this.bridge.invoke('player', 'seek', time);
  • listenValue
/**
 * @description 监听数值变化 
 *  {String} vname 需要监听的v数值
 *  {Function} callback  变化触发的回调函数 绑定时会马上执行一次
 */

this.bridge.listenValue(vname, callback)

// 监听数值变化,例如在上一章节中设置一个数值属性,属性名为apple
this.bridge.listenValue(this.apple, (value)=>{
    console.log('数值apple发生变化', value) 
})
  • listenCondition
/**
 * @description 监听条件判断表达式结果变化
 *  {String} condition 需要监听的条件判定
 *  {Function} callback  变化触发的回调函数 绑定时会马上执行一次
 */

this.bridge.listenCondition(condition, callback)

// 监听数值变化,例如在上一章节中设置一个数值条件属性,
// 属性名为appleEnough
this.bridge.listenCondition(this.appleEnough, (result)=>{
    console.log('apple 足够了', result) 
})
  • px2Percent
/**
 * @description 传入宽高px值 换算成相对于视频宽高的百分比
 *  {Object} options 宽高信息
 *  {
 *    x, // 宽
 *    y, // 高
 *  }
 *  
 */

this.bridge.invoke('utils', 'px2Percent', options) // 返回Object {x,y} 单位%
  • percent2Px
/**
 * @description 传入宽高百分比 换算成相对于视频宽高的px值
 *  {Object} options 宽高信息
 *  {
 *    x, // 宽
 *    y, // 高
 *  }
 *  
 */

tthis.bridge.invoke('utils', 'percent2Px', options) // 返回Object {x,y} 单位px
  • stateChange
/**
 * @description 监听播放状态变化
 */

this.bridge.on('player', 'stateChange',(state)=>{
    console.log('当前播放状态是', state);
}) 
  • timeupdate
/**
 * @description 监听播放进度变化
 */

this.bridge.on('player', 'timeupdate', (time) => {
  console.log('当前播放时间是', time);
});
  • videoNodeChange
/**
 * @description 监听播放视频发生切换
 */

this.bridge.on('engine', 'videoNodeChange',(videoInfo)=>{
    // {streamRatio, 视频宽高比
    //  vid, 当前播放的vid
    //  duration 当前视频总时长 单位 ms
    // }
    console.log('当前播放视频信息', videoInfo);
}) 
  • interactInfoUpdate
/**
 * @description 监听互动节点变化
 */

this.bridge.on('engine', 'interactInfoUpdate',(interactInfo)=>{
    console.log('当前互动节点信息', interactInfo);
}) 

# player命名空间完整API

player命名空间完整API

# bridge命名空间完整API

bridge命名空间完整API

# engine命名空间完整API

engine命名空间完整API

# utils命名空间完整API

utils命名空间完整API

# 音频播放类完整API

音频播放类完整API

# 组件指令使用

  • v-fseq/序列帧动画

    • 属性配置
    // 序列帧表单属性设置可以参考附录2 
    // 一个设置好的序列帧数据命名为 bg 如下所示
    const bg = {
        // 上传的序列帧图片地址
        url: 'http://ivimgbucket-30295.sz.gfp.tencent-cloud.com/ivopenplatimg/413241578642147677805555B68903BC4AA5428A712DB093C3DDD804.png',
        // 图片的宽 后台计算
        width: 400,
        // 图片的高 后台计算
        height: 4400,
        // 帧数
        freq: 11,
        // 速率
        speed: 20,
        // 是否循环
        loop: true,
      };
    
    • 代码
    <template>
    <div
        class="container"
        v-show="visible"
        :style="{
            top: y + '%',
            left: x + '%',
            width: width + '%',
            height: height + '%'
        }">
        <div style="width:100%;height:100%" v-fseq="{
            // 传入设置的属性
            ...bg
            // 通过此参数控制序列帧动画是否运行
            active: true | false, 
            // 每次循环播放的间隔 单位 秒
            interval: 1
            // 控制循环间隔时画面停止在第一帧还是最后一帧 默认false 停在最后一帧
            isStart: true | false
            // 控制画面停在某一帧 此时active须设置为false
            stopsAt: 1
            // 对应css animationDirection属性
            direction
            // 对应css animationFillMode属性
            fillmode 
            // 对应css animationDelay属性
            delay
        }" />
    </div>
    </template>
    

# 常见问题

  • Q1: 如何判断当前互动节点的视频播放结束?
// 方法一:通过时间变化判断 
let duration;
this.bridge.on('engine', 'videoNodeChange',(videoInfo)=>{
    duration = videoInfo.duration ; 
})

this.bridge.on('player', 'timeupdate',(time)=>{
    let miliSec = time * 1000 // 单位统一成毫秒
    if(duation > 0 && miliSec >= duration) {
        console.log('播放结束')
    }
    
    // 由于部分设备取时间不准确 建议采用以下方式 结束前200ms判断为结束。
    if(duation > 0 && miliSec >= duration - 200) {
        console.log('播放结束')
    }
})


// 方法二:通过事件判断 (此功能待发布,预计1月15号)
this.bridge.on('engine', 'videoNodePlayEnd',()=>{
    console.log('播放结束')
})

# 附录

# 属性类型

  • 文本 {String} 配置组件中的字符串属性

  • 数字 {Number} 配置组件中的数字属性

  • 开关 {Boolean} 配置组件中的布尔属性

  • 枚举 {String} 枚举类型

对应的值是选中的选项的id。选中套餐1,则对应值就是suit1。

  • 图片 {Object} 对应的值的数据结构如下:
{
  url: 'https://domain/path/pic',
    width: 100, // 原始图片的宽
    height: 100 // 原始图片的高
}

  • 序列帧 {Object} 支持单列竖排的序列帧图片
{
  url: 'https://domain/path/pic',
    width: 10, // 原始图片的宽
    height: 480, // 原始图片的高
    freq: 48, // 序列帧图片的帧数
    speed: 24, // 序列帧动画播放速度。单位是帧/秒,默认为24
    loop: false // 序列帧动画是否循环播放
}

  • 音频 {Object} 对应的值的数据结构
{
  url: 'https://domain/path/audio'
}

  • 数值 {String} 对应的值是数值的变量名,可以通过api监听此变量的值的变化。

  • 数值条件 {String} 对应的值是表达式,例如 v123>1 ,可以通过api监听表达式返回值的变化

  • 行为 {Object} 包括:视频跳转和数值变更。对应的值的数据结构如下:
{
  nextBranch: '123',
  nextChapter: '456',
  expression: 'a=a+1,b=b-2'
}

一般情况下把这个配置透传给api处理就能自动完成视频跳转或者数值变更。