0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

分享之前使用HarmonyOS NEXT Canvas做的动态GIF视频的一个案例,没有感情,全是技术。

陈姚丰 来源:jf_83680738 作者:jf_83680738 2025-03-16 15:56 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

theme: fancy

hello,大家好,我是莓创-陈杨。最近忙着改图表组件的BUG,还有定制化开发一些图表。没啥时间写新东西,草稿里面放了十几个要实现的案例分享,欠的实在太多了,后面再慢慢还吧。这次分享一下之前使用HarmonyOS NEXT Canvas做的动态视频的一个案例,没有感情,全是技术。

开发准备

开发流程与进度

这次整体开发流程主要如下:

  1. 获取图片素材列表数据,初始化视频的帧数以及canvas画布
  2. 绘画视频控制器,编写视频按帧数播放的功能
  3. 动态切换帧数进行播放
  4. 支持播放词条进行控制播放
  5. 添加音乐
  6. 导出视频

目前已经开发完第三步了,后面会继续开发,而且也会继续分享出来。感兴趣的开发可以关注一下。

代码讲解

接下来我简单讲解一下代码,也是需要注意点

1、获取图片素材列表,大家想要生成什么GIF或者视频就去找什么素材。可以用第三方的链接,可以用base64,可以用本地项目图片。最后都通过ImageBitmap方法将图片存储为canvas渲染的像素数据。非常方面,在H5还要担心跨域之类的问题。

let playTimer: number = 0
@Entry
@Component
export struct VideoEditing {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  private scroller: Scroller = new Scroller()
  @State w: number = 400; // 编程画板的宽度
  @State h: number = 760; // 编程画板的尺寸
  @State levelList: any[] = [
    new ImageBitmap('common/images/icon0.png'),
    new ImageBitmap('common/images/icon1.png'),
    new ImageBitmap('common/images/icon2.png'),
    new ImageBitmap('common/images/icon3.png'),
    new ImageBitmap('common/images/icon4.png'),
    new ImageBitmap('common/images/icon5.png'),
    new ImageBitmap('common/images/icon6.png'),
    new ImageBitmap('common/images/icon7.png')
    ....
  ];

  build() {
    Flex({direction: FlexDirection.Column, alignItems: ItemAlign.Start}) {
      Column() {
        Canvas(this.context)
          .onReady(() = > {
          })
          .width(this.w)
          .height(this.h)
          .backgroundColor('#fff')
      }.justifyContent(FlexAlign.Center).clip(true).width('100%').flexGrow(1).backgroundColor('#f8f8f8')
    }
  }
}

2、绘画视频控制器主体,这里主要控制变量就是视频的帧数fps,再结合循环计时器形成一个小型播放器,循环器的时间规则就是1000 / fps,代表着每秒几帧,想快就放大fps,想慢就缩小fps,是不是很简单。

let playTimer: number = 0
@Entry
@Component
export struct VideoEditing {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  private scroller: Scroller = new Scroller()
  @State w: number = 400; // 编程画板的宽度
  @State h: number = 760; // 编程画板的尺寸
  @State levelList: any[] = [
    ....
  ]; // 图层list
  @State ispause: boolean = true // 是否是暂停状态
  @State plusNum: number = 0 // 帧总量
  @State plusCount: number = 0 // 帧总量计数器(判断循环次数)
  @State count: number = 0 // 当前帧
  @State fps: number = 25 // 25帧/秒
  @State fpsNumber: number = 2 // 25帧/秒
  @State recordFrom: number = 0 // 记录起始帧
  @State recordTo: number = 0 // 记录结束帧
  @State imgsLen: number = 0 // 记录帧长度

  // 跳到某一帧
  goto (n: number) {
    this.count = n
    this.drawImg(this.levelList[n])
  }
  drawImg(img) {
    // const image = offCanvas.transferToImageBitmap()
    // this.context.transferFromImageBitmap(image)
    this.context.drawImage(img,0,0,this.w, this.h)
  }
  fromTo(from: number, to: number) {
    const self = this
    const fps = this.fps
    // 先清除上次未执行完的动画
    clearInterval(playTimer)
    const timeFn = (): undefined = > {
      if (self.ispause) {
        return
      }
      // 当总量计数器达到帧总量的时候退出
      if (self.plusNum <= self.plusCount) {
        self.resetData()
        // clearInterval(playTimer)
        return
      } else {
        // 未达到,继续循环
        // 帧计数器
        self.count++
        // 一次循环结束,重置keyCount为from
        if (self.count > to) {
          self.count = from
        }
        this.scroller.scrollTo({ xOffset: self.count * 150, yOffset: 0 })
        self.goto(self.count)
        // 总量计数器
        self.plusCount++
        return
      }
    }
    // 总量计数器
    this.plusCount = 0

    // 帧总量 帧数*循环次数first
    this.plusNum = to - from + 1
    this.ispause = false

    this.recordFrom = from
    this.recordTo = to

    timeFn()
    playTimer = setInterval(timeFn, 1000 / fps)
  }
  // 重置数据 停止并回到第一帧或cover帧
  resetData() {
    this.ispause = true
    clearInterval(playTimer)
    this.plusNum = 0
    this.plusCount = 0
    this.scroller.scrollTo({ xOffset: 0, yOffset: 0 })
    // 重置记录
    this.recordFrom = 0
    this.recordTo = this.imgsLen - 1
    this.count = 0
  }
  build() {
    Flex({direction: FlexDirection.Column, alignItems: ItemAlign.Start}) {
      Column() {
        Canvas(this.context)
          .onReady(() = > {
            this.goto(0)
          })
          .width(this.w)
          .height(this.h)
          .backgroundColor('#fff')
      }.justifyContent(FlexAlign.Center).clip(true).width('100%').flexGrow(1).backgroundColor('#f8f8f8')
      Column() {
        Flex({justifyContent: FlexAlign.SpaceBetween}) {
          Row() {
            Text(String(this.fps)).fontSize(15).margin({right: 4})
            Text('帧/秒').fontSize(12)
            Image($r('app.media.ic_public_spinner_small')).width(20)
          }.onClick(() = > {
            // this.showSex = true
            TextPickerDialog.show({
              range:  ['1', '12', '25', '30', '50', '60'],
              selected: this.fpsNumber,
              // selectedTextStyle: {color:'rgba(255, 80, 121, 1)'},
              onAccept: (value: TextPickerResult) = > {
                this.fpsNumber = Number(value.index)
                this.fps = Number(value.value)
              }
            })
          })
          Row() {
            Image($r('app.media.ic_public_play')).width(20)
              .onClick(() = > {
                this.imgsLen = this.levelList.length
                this.recordFrom = 0
                this.recordTo = this.imgsLen - 1
                this.fromTo(this.recordFrom, this.recordTo)
              })
            Image($r('app.media.ic_public_pause')).width(20)
          }
          Row() {
            Image($r('app.media.ic_public_music_filled')).width(20)
          }
        }.padding({top: 20, bottom: 20, left: 10, right: 10})
      }
    }.position({x: 0, y: 0}).width('100%').height('100%').backgroundColor('#fff').transition({ type: TransitionType.Insert, translate: { x: 0, y: '100%' } }).transition({ type: TransitionType.Delete, translate: { x: 0, y: '100%' } })
  }
}

以上就是前三步的实现代码,这三个步骤整体并不难。在页面能够实现简单的播放之后,后面就是生成视频或者GIF了。其实还有一个功能也很重要,就是导入视频,解析视频,然后就可以做视频编辑器了,这个也是一个大工程,想玩的可以去尝试尝试

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • HarmonyOS
    +关注

    关注

    80

    文章

    2146

    浏览量

    35583
  • Harmony
    +关注

    关注

    0

    文章

    108

    浏览量

    3493
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    HarmonyOS Next MQTT演示

    HarmonyOS Next MQTT演示
    发表于 11-07 16:54

    HarmonyOS NEXT 应用开发练习:智能视频推荐

    、整体思路 本DEMO展示了如何在HarmonyOS NEXT平台上开发智能视频推荐应用。
    发表于 01-02 16:26

    HarmonyOS next】ArkUI-X休闲益智消消乐【进阶】

    先看下运行效果吧 HarmonyOS H5与原生融合的多端开发实践 技术亮点:通过ArkUI-X的Web组件将H5游戏无缝嵌入原生应用,实现次开发、多端运行,覆盖
    发表于 06-28 21:59

    人类与机器人有感情产生吗?

    `  之前看到周润发的部电影里面的机器人,让我对机器人有了新的看法。里面的那个机器人表情丰富、机智幽默。  现在人们疑惑的是人与人工智能的感情
    发表于 11-10 10:18

    Canvas怎么合成自定义Gif

    Canvas合成自定义Gif
    发表于 06-03 13:36

    HarmonyOS NEXT新能力,站式高效开发HarmonyOS应用

    与调优新场景,测试新体验,以及让元服务开发更简单。 HarmonyOS NEXT开发者预览版的发布,将为HarmonyOS应用生态发展开启
    发表于 08-14 15:08

    如何将MP4视频转换为GIF

    GIF。因此,在上述所有情况下,您都需要好的MP4到GIF转换器。阅读以下内容,详细了解如何使用不同的解决方案将 MP4转换为GIF 
    的头像 发表于 12-17 16:44 5231次阅读

    HarmonyOS NEXT新能力,站式高效开发HarmonyOS应用

    ,并分享了围绕“次开发,多端部署” “可分可合,自由流转” “统生态,原生智能”三大HarmonyOS应用开发理念的实践经验。 而在8月5日,开发者主题演讲上,华为对HarmonyOS
    的头像 发表于 08-09 17:16 1803次阅读

    HarmonyOS NEXT新能力,站式高效开发HarmonyOS应用

    分享了围绕 “次开发,多端部署” “可分可合,自由流转” “统生态,原生智能” 三大HarmonyOS应用开发理念的实践经验。 而在8月5日,开发者主题演讲上,华为对HarmonyOS
    的头像 发表于 08-11 12:10 1715次阅读

    淘宝与华为合作将基于HarmonyOS NEXT启动鸿蒙原生应用开发

    1月25日,淘宝与华为举办鸿蒙合作签约仪式,宣布将基于HarmonyOS NEXT启动鸿蒙原生应用开发。
    的头像 发表于 01-26 16:14 1793次阅读

    HDC 2024上,HarmonyOS NEXT有哪些精彩亮点值得期待?

    6月21日至6月23日,备受瞩目的HDC2024华为开发者大会将在松山湖盛大举办。近日,官方对外发出了亮点日程海报,围绕HarmonyOS NEXT,大会都将带来哪些精彩内容呢?让我们
    的头像 发表于 06-19 17:02 1120次阅读
    HDC 2024上,<b class='flag-5'>HarmonyOS</b> <b class='flag-5'>NEXT</b>有哪些精彩亮点值得期待?

    华为“纯血”鸿蒙系统 HarmonyOS NEXT 将于9月底推出正式版

    HarmonyOS NEXT 将于今年 9 月底推出正式版本。 “从发布第一个版本到今年的 9 月份,这个(9 月)月底我们会正式发布 HarmonyOS
    的头像 发表于 09-14 14:27 3024次阅读

    华为HarmonyOS NEXT 10月8日开启公测

    华为宣布,万众瞩目的HarmonyOS NEXT操作系统将于10月8日正式开启公测,标志着这创新力作即将与广大用户见面。HarmonyOS NEX
    的头像 发表于 09-24 15:41 1718次阅读
    华为<b class='flag-5'>HarmonyOS</b> <b class='flag-5'>NEXT</b> 10月8日开启公测

    AWTK 最新动态:支持鸿蒙系统(HarmonyOS Next)

    为ToolkitAnyWhere,是ZLG倾心打造的套基于C语言开发的GUI框架。旨在为用户提供功能强大、高效可靠、简单易用、可轻松做出炫酷效果的GUI引擎,支
    的头像 发表于 11-06 08:03 1141次阅读
    AWTK 最新<b class='flag-5'>动态</b>:支持鸿蒙系统(<b class='flag-5'>HarmonyOS</b> <b class='flag-5'>Next</b>)

    HarmonyOS Next V2 @Event

    HarmonyOS Next V2 @Event 背景 在上节中,我们针对父子组件,讲了关于传递数据的知识。我们了解到 @Local 是管理自己内部的数据的, @Param 是负责接收父组件的数据
    的头像 发表于 03-31 09:42 615次阅读