【入门到精通】鸿蒙next开发:基于SwipePlayer三方库的短视频流畅点播切换场景解决方案

往期鸿蒙5.0全套实战文章必看:(文中附带全栈鸿蒙5.0学习资料)


基于SwipePlayer三方库的短视频流畅点播切换场景解决方案

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%') 
  } 
}

16.png

自定义滑动切换组件(广告,图片轮播等)

短视频切换场景下除了视频播放场景外还可能存在广告页,图片轮播等场景和其他一些不可预期的页面,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() 
}

17.png

长按视频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 
  }); 
}

21.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值