相关资料获取:SwipePlayer三方库:https://gitee.com/hadss/swipe-player。
SwipePlayer简介
SwipePlayer三方库主要聚焦短视频流畅点播切换场景,提供短视频上下滑动切换和快速起播能力,同时通过自定义能力的开放,满足应用不同短视频滑动场景的业务诉求。
SwipePlayer底层封装了系统AVPlayer及XComponent、Swiper、LazyForEach组件,基于AVPlayer的预创建、预加载以及提前起播实现了快速起播的能力。SwipePlayer三方库通过SwipePlayer组件以及SwipePlayerController控制器对外提供能力,包括上下滑动、快速起播、动态添加数据源、自定义跟随视频滑动的组件、自定义滑动组件切换、全屏和弹框等,目的是让开发者在开发过程中无需关注繁琐的AVPlayer创建、管理和播放逻辑,可以基于SwipePlayer库能力快速实现短视频流畅滑动的场景开发体验,使开发者可以更加聚焦实际场景业务的开发。
特性
- 支持网络视频(http/https)上下滑动及快速起播。
- 支持动态添加/刷新播放源数据。
- 支持自定义跟随视频滑动的组件。
- 支持自定义滑动组件切换。
- 支持自定义弹框(可自定义动画和弹框方向,提供默认动画实现)。
- 支持全屏/退出全屏播放,支持定义全屏播放界面组件。
- 支持进度条(通过组件形式提供)。
效果图
依赖版本
- HarmonyOS 5.0.0 Release及以上
- 手机版本:5.0.0.102
下载安装
使用ohpm安装依赖
ohpm install @hadss/swipeplayer
或者按需在模块中配置运行时依赖,修改oh-package.json5
{
"dependencies": {
"@hadss/swipeplayer": "^1.0.0-rc.3"
}
}
快速开始
支持网络视频(http/https)上下滑动及快速起播
定义控制器SwipePlayerController,初始化数据源,初始化SwipePlayer入口组件即可实现短视频滑动和快速起播能力。
import { util } from '@kit.ArkTS';
@Entry
@Component
struct Index {
sourceUrls: Array<MediaData> = [
{ url: 'https://xxx.mp4' },
{ url: 'https://xxx.mp4' },
{ url: 'https://xxx.mp4' }
];
playerData:VideoData[] = [];
// 1.定义控制器
swipePlayerController: SwipePlayerController = new SwipePlayerController();
dataSources: SwipePlayerIDataSource = new SwipePlayerIDataSource();
// 2.创建播放源
aboutToAppear(): void {
this.sourceUrls.forEach((item, i) => {
let tempData = new VideoData();
tempData.url = item.url;
this.playerArr.push(tempData);
});
this.dataSources.pushData(this.playerData);
}
build() {
Column() {
// 3.创建入口组件
SwipePlayer({
datasource: this.dataSources,
swipePlayerController: this.swipePlayerController,
options: {
viewBuilder: (type: string, isFullScreen?: boolean) => {
return viewScreen;
}
}
})
}
.width('100%')
.height('100%')
}
}
@Builder
export function viewScreen(dataObj: ESObject, playerSession: PlayerSession | undefined,
swipePlayerController: SwipePlayerController): void {
}
export interface MediaData {
url: string;
}
export class VideoData implements PlayerData {
url: string = '';
id: string = util.generateRandomUUID(true);
getId(): string{
return this.id
}
getSource(): string {
return this.url;
}
getCover(): string | undefined {
return undefined;
}
getType(): string {
if(this.url) {
return 'video';
} else {
return 'ad';
}
}
}
支持动态添加/刷新播放源数据
@Entry
@Component
struct Index {
sourceUrls: Array<MediaData> = [
{ url: 'https:xxx.mp4' },
{ url: 'https:xxx.mp4' },
{ url: 'https:xxx.mp4' }
];
playerData:VideoData[] = [];
// 1.定义控制器
swipePlayerController: SwipePlayerController = new SwipePlayerController();
dataSources: SwipePlayerIDataSource = new SwipePlayerIDataSource();
// 2.创建播放源
aboutToAppear(): void {
// 同上在此插入播放源
...
}
refreshText(): string {
if (this.offsetY > 200) {
return '释放刷新';
} else {
return '下拉刷新';
}
}
build() {
Column() {
// 3.创建入口组件
SwipePlayer({
datasource: this.dataSources,
swipePlayerController: this.swipePlayerController,
options: {
swiperCallback: {
onAnimationStart: (targetIndex: number) => {
let totalNum = this.dataSources.totalCount();
if (totalNum - targetIndex <= 5) {
this.dataSources.pushData(this.playerData);
}
},
onChange: (index: number) => {
if (index === 0 || !index) {
this.panDirection = PanDirection.Down;
} else {
this.panDirection = PanDirection.None;
}
this.isSelected = 1;
}
},
viewBuilder: (type: string, isFullScreen?: boolean) => {
return viewScreen;
}
}
})
}
.width('100%')
.height('100%')
// 下拉刷新视频
.parallelGesture(
PanGesture({ fingers: 1, direction: this.panDirection })
.onActionUpdate((event: GestureEvent) => {
if (event.offsetY < 0) {
this.offsetY = 0;
} else {
this.offsetY = event.offsetY;
}
})
.onActionEnd(() => {
if (this.offsetY > 200) {
this.dataSources.reloadData(this.playerData.reverse());
}
this.offsetY = 0;
})
);
}
}
@Builder
export function viewScreen(dataObj: ESObject, playerSession: PlayerSession | undefined,
swipePlayerController: SwipePlayerController): void {
}
在滑动视频组件上添加自定义组件
短视频滑动场景往往包含很多随视频一起滑动的组件,如点赞评论转发,视频内容描述等,由于不同的应用这些自定义组件的内容各不相同。SwipePlayer提供自定义能力,可以通过传递customBuilder组件的形式定义这些随视频滑动的自定义组件。
@Entry
@Component
struct Index {
sourceUrls: Array<MediaData> = [
{ url: 'https://xxx.mp4' },
{ url: 'https://xxx.mp4' },
{ url: 'https://xxx.mp4' }
];
playerData:VideoData[] = [];
// 1.定义控制器
swipePlayerController: SwipePlayerController = new SwipePlayerController();
dataSources: SwipePlayerIDataSource = new SwipePlayerIDataSource();
// 2.创建播放源
aboutToAppear(): void {
// 同上在此插入播放源
...
}
build() {
Column() {
// 3.创建入口组件
SwipePlayer({
datasource: this.dataSources,
swipePlayerController: this.swipePlayerController,
options: {
viewBuilder:(type: string, isFullScreen?: boolean) => {
if (type === 'video' && !isFullScreen) {
return ViewScreen;
}
}
}
})
}
.width('100%')
.height('100%');
}
}
@Builder
export function viewScreen(dataObj: ESObject, playerSession: PlayerSession | undefined,
swipePlayerController: SwipePlayerController): void {
VerticalScreenComponent({
verticalScreenData: dataObj as VideoData,
playerSession: playerSession,
swipePlayerController: swipePlayerController
});
}
// 竖屏页面样式
@Component
export struct VerticalScreenComponent {
build() {
RelativeContainer() {
...
}
.width('100%')
.height('100%')
}
}
自定义滑动切换组件(广告,图片轮播等)
短视频切换场景下除了视频播放场景外还可能存在广告页,图片轮播等场景和其他一些不可预期的页面,SwipePlayer提供自定义滑动切换组件的能力,通过传递customBuilder组件的形式实现。
@Entry
@Component
struct Index {
sourceUrls: Array<MediaData> = [
{ url: 'https://xxx.mp4' },
{ url: 'https://xxx.mp4' },
{ url: 'https://xxx.mp4' }
];
playerData:VideoData[] = [];
// 1.定义控制器
swipePlayerController: SwipePlayerController = new SwipePlayerController();
dataSources: SwipePlayerIDataSource = new SwipePlayerIDataSource();
// 2.创建播放源
aboutToAppear(): void {
// 同上在此插入播放源
...
}
build() {
Column() {
// 3.创建入口组件
SwipePlayer({
datasource: this.dataSources,
swipePlayerController: this.swipePlayerController,
options: {
viewBuilder: (type: string, isFullScreen?: boolean) => {
if (type === 'video' && isFullScreen) {
return fullScreen;
} else if (type === 'video' && !isFullScreen) {
return viewScreen;
} else {
return AdvertBuilder;
}
}
}
})
}
.width('100%')
.height('100%')
}
}
// 竖屏页面
@Builder
export function ViewScreen(dataObj: ESObject, playerSession: PlayerSession | undefined,
swipePlayerController: SwipePlayerController): void {
// 同上
...
}
// 横屏页面
@Builder
export function FullScreen(dataObj: ESObject, playerSession: PlayerSession | undefined,
swipePlayerController: SwipePlayerController): void {
// 同上
...
}
// 广告页面
@Builder
export function Advert(dataObj: ESObject, playerSession: PlayerSession | undefined,
swipePlayerController: SwipePlayerController): void {
Advert()
}
长按视频3s弹出倍速播放配置,选择倍速后弹出toast弹框提示并倍速播放视频
短视频滑动还涉及一些手势操作的业务场景,比如长按3s弹出倍速页面,可以通过组件提供的Gesture手势事件注册能力实现,使用SwipePlayerController提供的弹框能力可以弹出自定义配置页面,应用还可以通过AVPlayer创建回调注册AVPlayer回调监听speedDone事件并在回调中弹出toast提示。
// 竖屏页面样式
@Component
export struct VerticalScreenComponent {
build() {
RelativeContainer() {
...
}
.width('100%')
.height('100%')
// 页面长按弹出倍速弹窗
.gesture(
LongPressGesture()
.onAction(() => {
this.swipePlayerController.openDialog({
// FastSpeedBuilder倍速弹窗的样式
dialogBuilder: () => wrapBuilder(FastSpeedBuilder),
dataObj: this.swipeData
}, PushDirection.DOWN_TO_UP, 350, false);
})
);
}
}
// 倍速弹窗
@Component
export struct FastSpeed {
private swipeData: SwipeData = null!;
build() {
RelativeContainer() {
...
Flex() {
ForEach(CommonConstant.VIDEO_FAST_SPEED, (item: PlayerSpeed, commonIndex: number) => {
// 倍速的文字,例如1x,2x
Text(item + 'x')
.onClick(() => {
// 设置视频倍速
this.swipeData.swipePlayerController.setSpeed(item.playbackSpeed);
promptAction.openCustomDialog({
// toast提示窗样式
builder: () => this.clickSpeedBuilder(),
...
});
});
});
}
.width('100%');
...
}
}
}
}
@Builder
export function FastSpeedBuilder(swipeData: SwipeData, playerLayoutSize: PlayerLayoutSize, removeDialog: () => void,
closeDialog: () => void) {
FastSpeed({ swipeData: swipeData, closeDialog: closeDialog });
}
点击评论按钮弹出评论页面,包含视频(缩放)上移动画,跟手动画
SwipePlayerController会封装点击页面弹框及视频动画并对外提供调用方法供应用使用,使用该方法时会弹出空白弹框,用户可以自定义弹框内容。该弹框弹出式时会有默认视频动画(视频上移或缩放上移),收起弹框时默认有视频还原动画。
// 竖屏页面样式
@Component
export struct VerticalScreenComponent {
@State swipePlayerController: SwipePlayerController = null!;
build() {
RelativeContainer() {
...
// 评论图标、评论数,点击拉起评论弹窗
Column() {
Image(CommonConstant.CUSTOM_IMAGE[1])
.width('100%')
.height('100%');
Text(this.verticalScreenData.commentsNum.toString())
}
.onClick(() => {
let withAnimation = false;
// 获取屏幕宽度,判断直板机和折叠屏的状态
if (this.displayWidth < CommonConstant.PHONE_WIDTH) {
withAnimation = true;
}
// 直板机和折叠屏折叠态,弹窗会压缩视频,否则不会压缩视频
this.swipePlayerController.openDialog({
// CommentsBuilder为评论区样式
dialogBuilder: () => wrapBuilder(CommentsBuilder),
dataObj: this.dialogComponentData
}, PushDirection.DOWN_TO_UP, 350, withAnimation)
});
...
}
}
.width('100%')
.height('100%')
}
}
@Builder
export function CommentsBuilder(dialogComponentData: CommentsData, playerLayoutSize: PlayerLayoutSize,
removeDialog: () => void,
closeDialog: () => void) {
Comments({
playerLayoutSize: playerLayoutSize,
closeDialog: closeDialog,
removeDialog: removeDialog,
dialogComponentData: dialogComponentData
});
}
@Component
export struct Comments {
@State playerLayoutSize: PlayerLayoutSize = null!;
@Prop dialogComponentData: CommentsData = null!;
private closeDialog: () => void = null!;
private removeDialog: () => void = null!;
build() {
Column(){
...
}
.width('100%')
.height('70%');
}
}
横向视频全屏播放
播放横向适配时,会额外显示全屏播放按钮,点击后全屏播放,并显示返回键,标题,进度条等内容,使用SwipePlayerController提供的全屏和退出全屏能力结合自定义组件videoBuilder实现。
// 页面样式
@Component
export struct VerticalScreenComponent {
swipePlayerController: SwipePlayerController = null!;
build() {
RelativeContainer() {
...
// 全屏按钮
Text() {
Span(CommonConstant.REQUEST_FULL_SCREEN)
.fontWeight(CommonConstant.CUSTOM_FONT_WEIGHT)
.fontColor(Color.White);
Span(' ')
.letterSpacing(5);
ImageSpan(CommonConstant.CUSTOM_IMAGE[5])
.width(CommonConstant.FULL_SCREEN_TEXT_WIDTH);
}
.onClick(() => {
// 切换为横屏
this.swipePlayerController.requestFullScreen(false);
});
...
}
.width('100%')
.height('100%')
}
}
实现自定义进度条
某些情况下应用不想使用默认进度条组件,需要实现自定义的进度条,可以通过options配置禁用显示默认进度条,然后通过videoBuilder结合AVPlayer监听事件注册实现自定义进度条。
@Entry
@Component
struct Index {
...
build{
SwipePlayer({
datasource: this.dataSources,
swipePlayerController: this.swipePlayerController,
options: {
viewBuilder:(type: string, isFullScreen?: boolean) => {
if (type === 'video' && !isFullScreen) {
return ViewScreen;
}
}
}
})
}
}
// 页面样式
@Component
export struct VerticalScreenComponent {
@State isVerticalScreen: boolean = true;
@State isSliderMoving: boolean = false;
@State swipePlayerController: SwipePlayerController = null!;
playerSession: PlayerSession = null!;
build() {
RelativeContainer() {
...
// 自定义进度条
ProgressBar({
isShowTime: !this.isVerticalScreen,
isSliderMoving: this.isSliderMoving,
playerSession: this.playerSession,
trackColor: Color.Gray,
selectedColor: CommonConstant.SPEED_BUTTON_FONT_COLOR
})
...
}
.width('100%')
.height('100%')
}
}
@Builder
export function viewScreen(dataObj: ESObject, playerSession: PlayerSession | undefined,
swipePlayerController: SwipePlayerController): void {
VerticalScreenComponent({
verticalScreenData: dataObj as VideoData,
playerSession: playerSession,
swipePlayerController: swipePlayerController
});
}
接口和属性列表参考:SwipePlayer接口和属性列表。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。