Demo 源码体验
申请权限
接入微剪
1. 在项目中新建
index.js
暴露插件所需的接口:module.exports = {downloadFile: wx.downloadFile,loadFontFace: wx.loadFontFace,// 文件管理系统getFileSystemManager: wx.getFileSystemManager,USER_DATA_PATH: wx.env.USER_DATA_PATH}
2. 在
app.json
中引入插件:"plugins": {"myPlugin": {"version": "1.7.0","provider": "wx76f1d77827f78beb","export": "index.js" // 注意暴露index}}
调用思路
一个短视频往往是多个素材的组合,诸如主视频/图片,贴纸,文字,特效,转场,动效等。为了实现更灵活的编辑功能,微剪采用轨道的数据结构来组合视频。
核心组件是
wj-player
播放器组件和wj-export
导出器组件,调用步骤如下:

引入播放器组件
1. 新建一个页面,在json文件中引入组件:
{"usingComponents": {"wj-player": "plugin://myPlugin/wj-player"}}
2. 编写 wxml:
<wj-playerid="my-player"bindready="onPreviewerReady"bindtimeupdate="onPreviewerTimeUpdate"/>
3. 在 js 文件中获取到播放器实例为后面的使用做准备:
Page({...onReady() {let player = this.selectComponent("#my-player")this.player = player}...})
构造轨道数据
主轨道
首先来构造运行一个播放器的主视频轨道。
说明
主视频轨支持图片和视频类型的片段素材。
图片在播放器中将会默认当做3秒的静态视频播放。
1. 创建视频轨道 Track,设置视频轨道的 type 为 media。
this.mediaTrack = new global['wj-types'].Track({type: 'media',clips: []});
2. 添加视频 Clip:
2.1 向 mediaTrack 媒体轨道中添加视频 Clip,设置视频 Clip 的 type 为 video。
let videoClip1 = new global['wj-types'].Clip({id: 'video1',type: 'video',info: {tempFilePath: 'wxfile:xxxx',width: '',height: '',duration: 5},section: new global['wj-types'].ClipSection({start: 0,end: 4}),startAt: 0})
参数 | 说明 |
info | tempFilePath 为视频的本地路径;width、height 对应视频的宽高;duration 为视频时长。 |
section | 利用插件提供的 ClipSection 进行视频的时间范围选择,示例中选择了该视频0秒-4秒的区间片段。缺省值 start 为0,end 为 info 的 duration 值。 |
startAt | 视频在轨道中的起始位置,也就是基于整个播放时长的起始时间,同一个 media track 中存在多个 clip 的情况非常有用,它决定了某个 clip 在整个 track 中的位置。 |
id | id 可以自定义,如果不传则由播放器内部自动生成。 |
2.2 因为 Clip 需要运行在 Track 中,接下来将 Clip 添加进 media 轨道:
this.mediaTrack.clips = [videoClip1];
3. 添加图片 Clip。
3.1 初始化添加图片 Clip,设置图片的 Clip 的 type 为 image。
let imageClip1 = new global['wj-types'].Clip({id: 'image1',type: 'image',info: {tempFilePath: 'wxfile:xxxx',width: '',height: '',duration: 3},startAt: 4})
说明
上述 Clip 的 startAt 值为4,是因为此前我们已经加入了一个 video Clip,其 section 为4(end-start),即当前 Clip 之前的所有 Clip 的有效 section 之和。
3.2 把图片添加到 media 轨道:
this.mediaTrack.clips = [videoClip1, imageClip1];
说明
可以看到此时媒体轨道中已经添加一个视频和一张图片。以此类推,您可以按照这种方式添加更多的视频或者图片。
4.
更新播放器
。
播放器均通过 updateData 方法实现更新,updateData 接受的参数为包含轨道的数组。以创建的媒体轨道后更新播放器为例,只需updateData([媒体轨道])
即可 :this.player.updateData([this.mediaTrack]);
说明
以此类推,若您的播放器中包含视频,音乐,特效等,则
updateData([媒体轨道,音乐轨道, 特效轨道])
。5. 视频播放区间。
5.1 以视频片段 videoClip1 时长为例,直接修改 videoClip1 的 section 属性。
videoClip1.section = new global['wj-types'].ClipSection({start: 1,end: 4}),
同理,若您想修改 videoClip1 基于整体的开始播放时间。
videoClip1.startAt = 1;
注意
对 Clip 的 section 或者 startAt 进行修改后,会影响到后续 Clip 的 startAt 值,需要按照上文提到的累加原则将其值更新一下。
5.2 修改完成后,更新一下播放器即可。
this.player.updateData([this.mediaTrack]);
6. 删除某个视频。
在 media 轨道中删除对应的 Clip 即可,以删除 videoClip1 为例:
6.1 获取视频对应的 id(
video1
)进行删除。this.mediaTrack.clips.forEach((item, index) => {if(item.id === 'video1') {this.mediaTrack.clips.splice(index, 1)}})
6.2 更新 Clip 的 startAt 值。
6.3 更新播放器。
this.player.upda teData([this.mediaTrack]);
画中画
1.6.0 之后的版本支持多轨道叠加组合实现画中画效果,思路如下:
1. 在主轨道前增加一个”video“类型或“image”类型的轨道,在轨道中构造背景元素。
2. 主轨道上禁用自动模糊背景,设置 clip 的
disableAutoBackground
属性为true
。{"type": "image","id": "background-image","clips": [{"id": "background-image-1","type": "image","isTemplate": true,"startAt": 0,"section": {"start": 0,"end": 100},"info": {"tempFilePath": "xxxxx",...}}]},{"type": "media","id": "main-track","clips": [{"id": "some-prefix-video","type": "image","startAt": 0,"section": {"start": 0,"end": 2.8},"info": {"tempFilePath": "xxxxx",...}"operations": []},{"id": 1,"type": "image","startAt": 2.8,"section": {"start": 0,"end": 0.83},"isTemplate": false,"info": {"tempFilePath": "xxxx",...},"operations": [...]}]}
音乐
1. 添加音乐轨道:
this.musicTrack = new global['wj-types'].Track({type: 'music',clips: []});
2. 添加音乐片段:
2.1 添加音乐 Clip,设置音乐 Clip 的 type 为 music。
let musicClip1 = new global['wj-types'].Clip({id: 'music1',type: 'music',info: {tempFilePath: 'http://xxx.xxx.mp3',},section: new global['wj-types'].ClipSection({start: 0,end: 60 // end - start超过视频总时长时,音乐会自动截断}),startAt: 0})this.musicTrack.clips = [musicClip1];
2.2 将音乐轨道加入播放器轨道:
this.player.updateData([this.mediaTrack, this.musicTrack]);
可以看到此时播放器内拥有了两条轨道,媒体和音乐。
3. 修改音乐属性:
修改音乐和修改视频 Clip 是一样的,具体详情请参见 修改视频时长。以修改音乐的起始时间为例:
musicClip1.startAt = 8;
音乐有一个比较特殊的属性:音量,取值范围为[0,1],1为最大音量。示例:
musicClip1.audio.volume = 0.5;
修改完成后,更新播放器即可。
this.player.updateData([this.mediaTrack, this.musicTrack]);
4. 删除音乐:
在播放器的轨道中去掉
this.musicTrack
音乐轨道,并重新调用 updateData
即可成功删除音乐。this.player.updateData([this.mediaTrack]);
滤镜
1. 构造滤镜轨道。
this.filterTrack = new global['wj-types'].Track({type: 'filter',clips: []});
2. 构造滤镜片段。
2.1 获取播放器内部提供的默认滤镜:
const filterList = await requirePlugin("myPlugin").getFilterList();
f
ilterList 的数据结构如下所示:
[{key: 'effect1',name: '特效1'},{key: 'effect2',name: '特效2'}]
2.2 创建 filter clip:
let filterClip1 = new global['wj-types'].Clip({id: 'filter1',type: 'filter',key: 'baixi',section: new global['wj-types'].ClipSection({start: 0,end: 3}),startAt: 0})
参数 | 说明 |
key |
说明:
2.3 将 Clip 加入轨道:
this.filterTrack.clips = [filterClip1]
2.4 更新播放器:
this.player.updateData([this.mediaTrack, this.musicTrack, this.filterTrack]);
说明
此时您的播放器中拥有了3条轨道,媒体,音乐和滤镜。
3. 添加多个滤镜片段。
和添加多个视频 Clip 类似,您需要按照 添加滤镜片段 创建另一个 filterClip2,添加到轨道中,然后更新播放器即可。
this.filterTrack.clips = [filterClip1, filterClip2]
4. 修改滤镜。
修改滤镜对应的 Clip 属性,更新播放即可查看。以修改滤镜的时间信息为例:
filterClip1.section = new global['wj-types'].ClipSection({start: 0,end: 5});
更新播放器即可查看效果。
5. 删除滤镜,主要分以下两种情况:
删除滤镜轨道中的滤镜:只需在
this.filterTrack.clips
中通过 id 找到对应的 Clip 进行删除即可。this.filterTrack.clips.forEach((item, index) => {if(item.id === 'filter_id') {this.filterTrack.clips.splice(index, 1)}})
删除整个滤镜轨道:在 updateData 的数组去掉
this.filterTrack
即可。特效
1. 添加特效轨道。
this.effectTrack = new global['wj-types'].Track({type: 'effect',clips: []});
2. 添加滤镜片段。
2.1 获取播放器内部提供的默认特效:
const effectList = await requirePlugin("myPlugin").getEffectList();
effectList 的数据结构如下:
[{key: 'effect1',name: '特效1',previewUrl: 'xxxx'},{key: 'effect2',name: '特效2',previewUrl: 'xxxx'}]
2.2 创建 effect Clip:
let effectClip1 = new global['wj-types'].Clip({id: 'effect1',type: 'effect',key: 'effect1',section: new global['wj-types'].ClipSection({start: 0,end: 3}),startAt: 0})
参数 | 说明 |
key |
2.3 将 Clip 加入轨道:
this.effectTrack.clips = [effectClip1]
2.4 更新播放器:
this.player.updateData([this.mediaTrack, this.musicTrack, this.filterTrackm, this.effectTrack]);
说明
此时您的播放器中拥有了4条轨道:媒体、音乐、滤镜和特效。
3. 添加多个特效片段。
3.1 创建多个 effectClip:
let effectClip2 = new global['wj-types'].Clip({id: 'effect2',type: 'effect',key: 'effect2',section: new global['wj-types'].ClipSection({start: 0,end: 2}),startAt: 3})let effectClip3 = ...
3.2 将新创建的 effectClip 添加到轨道中:
this.effectTrack.clips = [filterClip1, filterClip2, filterClip3...]
更新播放器即可查看效果。
4. 修改特效。
修改特效对应的 Clip 属性,更新播放即可查看。以修改特效的持续时间为例:
effectClip1.section = new global['wj-types'].ClipSection({start: 2,end: 5});
5. 删除特效,主要分以下两种情况:
删除轨道中的滤镜,以删除 effectClip2 为例:
this.effectTrack.clips = [effectClip1, effectClip3...];this.player.updateData([this.mediaTrack, this.musicTrack, this.filterTrackm, this.effectTrack]);
删除整个特效轨道:
this.player.updateData([this.mediaTrack, this.musicTrack, this.filterTrack]);
视频特效-1.6.0 版本新增
插件在1.6.0版本新增了3种金粉系列特效,原理和其他特效有些区别,所以这里接入方法会有一些差异。
1. 先从播放器中获取所有的特效类型:
const effetcs = this.player.getEffects();
新增
effetcs
为如下结构:{key: 'jinfen1',name: '闪粉',is_alpha: true,previewImage: 'https://imgcache.qq.com/qcloud/vod/dist/mp-video-edit/effect/ScNine.gif'}, {key: 'jinfen2',name: '星火',is_alpha: true,previewImage: 'https://imgcache.qq.com/qcloud/vod/dist/mp-video-edit/effect/ScNine.gif'}, {key: 'jinfen3',name: '金粉',is_alpha: true,previewImage: 'https://imgcache.qq.com/qcloud/vod/dist/mp-video-edit/effect/ScNine.gif'}]
说明
新增了3种特效,分别为
闪粉
、 星火
、金粉
。字段相比
1.6.0
之前的版本新增了is_alpha
字段,意味着需要做如下处理才能使用。2. 下载特效素材:
1.6.0
版本素材都为内置 shader 的模式,但是新增的金粉系列特效底层使用 MP4 进行渲染,因此需要预下载素材。下载素材只需要使用播放器内置方法即可:
假设您要加载 jinfen1
,在 downLoadEffect
方法中传入 key 即可。downLoadEffect
方法包装为 Promise,下载完成之后就可以进行播放器的渲染了。this.player.downLoadEffect('jinfen1').then(() => {...})
3. 特效渲染:
视频类型的特效唯一的区别就是需要在 Clip 中加一个字段
videoType
为 black
。let effectClip2 = new global['wj-types'].Clip({id: 'jinfen1',type: 'effect',key: 'jinfen1',section: new global['wj-types'].ClipSection({start: 0,end: 2}),videoType: "black",effectType: 'custom',startAt: 3})
参数解释:
effectType:
custom
| template
。 如果用于 Clip 场景使用 custom
, 如果用于模板使用 template
。videoType:
black
| alpha
。 内置金粉系列使用 black
,其他一律使用 alpha
即可。说明
1.6.0版本的 isAlpha 字段已废弃。
文字
1. 添加文字轨道。
this.textTrack1 = new global['wj-types'].Track({type: 'text',clips: []});
2. 添加文字片段。
2.1 创建 textClip:
let textClip1 = new global['wj-types'].Clip({type: 'text',startAt: 0,section: new global['wj-types'].ClipSection({start: 0,end: 100}),content: {content: text.value, // 文字内容style: {type: text.bgColor === 'transparent' ? 'normal' : 'background', // 文字样式color: text.color, // 文字颜色backgroundColor: text.bgColor}},})
参数 | 说明 |
content | content 为文字的内容,可通过 style 自定义文字的颜色和背景颜色。 |
说明:
文字的位置控制逻辑在播放器内部,所以只需要把文字添加进播放器,通过拖拽挪动文字位置,相应的位置信息将初始化传入的文字统一位于视频的中心位置。
2.2 将新创建的 textClip 添加到轨道中:
this.textTrack.clips = [textClip1]
当然,您也可以在一个轨道添加多个文字,文字 Clip 的 section 没有重叠。
this.textTrack.clips = [textClip1, textClips2, ...]
3. 更新文字。
前往所需更新的文字修改对应的 Clip 属性即可,以更新 textClip1 的文字颜色为例:
textClip1.style.color = 'blue'
4. 删除文字,主要分以下两种情况:
删除 Clip,以删除 textClips2 为例:
this.textTrack.clips = [textClip1, textClips3]
删除整个文字轨道:
this.player.updateData([this.mediaTrack, this.musicTrack, this.filterTrack]);
5. 多个文字共存。
在同一个轨道中添加多个 文字 Clip 的情况下,各个文字 Clip 之间的 section 是不存在交集的,即某一时刻页面最多显示一个文字。假设需要多个文字同时展示的情况下,则需要通过多个文字 Track 实现,每个轨道中只包含一个文字 Clip。
this.textTrack1 = new global['wj-types'].Track({type: 'text',clips: []});this.textTrack1.clips = [textClip1];this.textTrack2 = new global['wj-types'].Track({type: 'text',clips: []});this.textTrack1.clips = [textClip2];... // 以此类推
创建完文字轨道后,更新播放器即可。
this.player.updateData([this.mediaTrack, this.musicTrack, this.filterTrack, this.textTrack1, this.textTrack2]);
注意
由于小程序限制,最多只能存在5个文字。 可以添加5个文字轨道,一个轨道中一段文字;或者一个轨道中,5段文字,即文字 Clip 总共不能超过5个。
6. 给文字添加字体。
由于小程序插件无法调用 wx.loadFontFace 方法,因此需要小程序手动暴露该接口给插件,或者在小程序内提前加载字体后再传入插件渲染。详情可参考 自定义贴纸和字体。
加载字体:
loadFontFace({family: 'fangzhengyouhei',source: "https://fontPath",scopes: ['webview','native'],success(res) {console.log('font success')console.log(res.status)resolve()},fail: function(res) {console.log('font fail')console.log(res.status)reject()}});
构造对应的文字 clip:
let mytext = new global['wj-types'].Clip({type: 'text',content: {content: "文字", // 文字内容style: {type: 'background', // 文字样式color: '#ffffff', // 文字颜色backgroundColor: '#ff00ff',fontfamily: 'fangzhengyouhei',fontloaded: true},position: {x: 50,y: 90},},section: {start: 0,end: 10,duration: 10},})
说明
贴纸
1. 创建贴纸轨道
this.stickerTrack = new global['wj-types'].Track({type: 'sticker',clips: []});
2. 添加贴纸片段
let stickerclip = new global['wj-types'].Clip({id: 'my-sticker',type: 'sticker',section: {start: 0,end: 10,duration: 10},designSize: {xPercent: 0.20, //初始位置yPercent: 0.30,rotation: 0.4, // 旋转角度(单位弧度)scale: 2 // 缩放尺寸},startAt: 0,key: 'guodong', // 贴纸key})this.stickerTrack.clips.push(stickerclip)this.player.updateData([this.mediaTrack, this.musicTrack, this.filterTrack, this.stickerTrack]);
转场
微剪在 1.5.0 版本支持转场和动效,可以实现更加平滑的过渡和视频效果,可以用于影集模式的视频、图片拼接。
获取内置转场
调用微剪提供的方法获取 operation 列表:
const operations = await requirePlugin("myPlugin").getTransitionList()
获取到的数据如下:
[{key: "burn",paramsTypes: { "color": "vec3" }},{key: "Bounce",paramsTypes: {}},...]
微剪内置了数十个流畅炫酷的转场效果,开发者可以获取到 key 值后直接构造数据传递给播放器和导出器,以实现更丰富的编辑效果。
使用转场
在微剪中转场也是一个
Clip
对象。作为一个特殊的片段存在于主视频轨(类型为media
的轨道)中,串联起两个视频。步骤1:构造视频轨
let mediaTrack = new new global['wj-types'].Track({type: "media",clips: [new global['wj-types'].Clip({...,id: 'clip1',type: 'image',info: {tempFilePath: 'xxxx',...},startAt: 0,section: {start: 0,end: 3}}),new global['wj-types'].Clip({...,id: 'clip2',type: 'video',info: {tempFilePath: 'xxxx',...},startAt: 3,section: {start: 2.5,end: 6}}),new global['wj-types'].Clip({...,id: 'clip3',type: 'image',info: {tempFilePath: 'xxxx',...},startAt: 6.5,section: {start: 0,end: 3}}),],})
这里构造了三个连续播放的
Clip
,下一步需要添加clip1
和clip2
、clip2
和clip3
之前的转场。步骤2:构造 transition clip
先构建
clip1
和clip2
之间的转场。let clip1 = mediaTrack.clips[0]let clip2 = mediaTrack.clips[1]let transitionDuration = 1let transition01 = new global['wj-types'].Clip({type: 'transition',info: { // 标记转场的两个片段fromId: clip1.id,toId: clip2.id},startAt: clip1.startAt + clip1.section.end - clip1.section.start - transitionDuration,section: {start: 0,end: transitionDuration // 转场持续的时长},key: 'Blur' // 转场的key})
说明
步骤3:时间关系
微剪处理转场过渡时间关系如下图:

这种时间关系可以保证过渡过程中两个视频均处于播放状态,衔接更加自然。
在两个片段之间有 transition 的情况下,假设

transition
的时长为transitionDuration
秒。
则在 clip1
结束播放的前 transitionDuration
秒,即时间轴上 clip1.startAt + clip1.section.end - clip1.section.start - transitionDuration
时间点,Clip2
开始播放,并且同时 transition
开始播放。
因此Clip2
的startAt
字段需要更新:clip2.startAt -= transitionDuration
将
transition
加入到mediaTrack
中:mediaTrack.clips.push(transition)
步骤4:参数传递
获取到的内置转场效果会有
paramsTypes
这个字段,根据这个字段包含的信息,开发者可以自行规定转场的某些参数,以达到差异化的效果。
例如burn
这个效果:{"name": "burn","paramsTypes": { "color": "vec3" }}
paramsTypes
中键值对的含义为:参数名-参数类型,其中参数类型是一一对应glsl es
的类型。
则根据此属性构造转场需要的参数结构,并传入到clip.info
中:transition01.info.params = {color: [0.5, 0.7, 0.8]}
注:
glsl es
与Javascript
类型对应关系如下:glsl es 类型 | Javascript 类型 |
int | number |
float | number |
vec2 | [v1, v2] |
vec3 | [v1, v2, v3] |
bool | Boolean |
步骤5:传给播放器
let player = ...player.updateData([mediaTrack])
动效
1.7.0版本的微剪开放了一些动效供开发者调用,动效可以应用于单张图片或者视频,使素材在画面中能够动起来。
获取内置动效
const operations = await requirePlugin("myPlugin").getOperationList()
数据格式
[{"key": "Blur","name": "模糊","type": "common","paramsTypes": {"paramName": "paramType" // 参数描述,参数名称: 参数类型(对应glsl es的类型)},"desc": ""// 描述},{"key": "BlurCommon","name": "模糊变清晰","type": "","colorMode": 1,"paramsTypes": {}}]
使用动效
一个动效只会对一个图片或视频 Clip 生效,一个 Clip 以有多个动态效果(如果需要主轨道上两个素材产生共同效果则需要使用转场)。
在微剪中动效是作为 Clip 的附加属性管理的,存储在 Clip 的 operations 属性中。
let clip = new global['wj-types'].Clip({type: 'video',...})let operations = [{"id": "CropByRatio","key": "CropByRatio","params": {}},{"id": "xxx","key": "xxx","params": {"startTime": 0,"transferDuration": 0.5}}]clip.operations = operations
params 可以传递参数,微剪的封装了一些常用的通用参数可供调用。
通用参数
参数 | 含义 | 默认值 |
startTime | 动效开始播放的时间(相对于 clip 的 startAt) | 0 |
transferDuration | 动效持续时长 | clip 的播放时长, clip.section.end - clip.section.start |
ratio | 图片比例(width/height),不传则默认保持原素材比例 | - |
模板
在微剪中,模板也是一个 Clip,可以由用户输入部分内容,在播放器中完成复杂的效果显示,同时可以和除 Media 类型以外的元素轨道组合。
微剪有提供内置的模板可供直接调用。
说明
拿到内置模板资源后,可调用播放器的
preloadTemplate
方法预加载模板并获取到需要模板需要输入的内容:this.player.preloadTemplate("newyear1").then(cfg => {console.log(cfg)}) // 通过模板key预加载并拿到需要输入的内容// 格式如下[{"id": "播放器识别素材填充位置的唯一标记","name": "中文名称","type": "类型(目前仅有 text/video/image)","value": "缺省默认值","duration": "在模板中显示的时长"},...]
用户输入内容后,将对应内容组织成为数组,并作为 resources 字段传入 clip:
let templateClip = new global["wj-types"].Clip({type: 'template',key: '模板key',info: {resources: [{"id": "播放器识别素材填充位置的唯一标记","type": "类型","value": "文件路径或文字内容"}...]}})let templateTrack = new global["wj-types"].Track({type: 'template'clips: [templateClip]})
说明
微剪播放器播放的一组轨道中,只能有一个主轨道。主轨道可以是 media 类型或 template 类型。
导出
构造完了视频轨道之后,可以将轨道数据直接传给导出组件进行导出。
let tracks = [this.mediaTrack, this.musicTrack, this.filterTrack, this.stickerTrack]this.setData({tracks})
Wxml:
<wj-exporttracks="{{tracks}}"></wj-export>
说明
小程序的性能有限,导出组件和播放器组件尽量不要放在同个页面,且数量不宜太多。
微剪的导出全部为小程序端导出,导出耗时较为依赖手机配置。