首页 > 其他分享 >如何实现逐帧动画(OpenHarmony)

如何实现逐帧动画(OpenHarmony)

时间:2023-05-11 20:32:13浏览次数:53  
标签:OpenHarmony 动画 number width 逐帧 火柴 组件 背景图片 Row

如何实现逐帧动画

场景说明

逐帧动画是常见的一种动画呈现形式,本例就为大家介绍如何通过OpenHarmony实现逐帧动画。

效果呈现

本例最终效果如下:

  • 点击“run”按钮,火柴人开始走动。
  • 点击“stop”按钮,火柴人停止走动。

如何实现逐帧动画(OpenHarmony)_clearAllInterval

运行环境

  • IDE:DevEco Studio 3.1 Beta1
  • SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release)

实现思路

本例的实现有两个关键点:

  • 将连续走动的火柴人拆分为多帧静态图像,在固定的时间间隔内逐帧将图像移动到动画窗口,间隔时间要小于肉眼可察觉的时间。循环上述动作,就可以实现火柴人的走动动画。 火柴人静态图像如下:

如何实现逐帧动画(OpenHarmony)_逐帧动画_02

  • 将背景图片以固定速度相对于火柴人走动方向反方向移动,从而实现火柴人向前走动的效果。

     背景图如下:

如何实现逐帧动画(OpenHarmony)_setInterval_03

本例使用translate()控制火柴人的移动,用backgroundImagePosition()控制背景图的移动。另外,通过setInterval()设置火柴人移动的时间间隔,通过clearAllInterval()清除移动。

开发步骤

  1. 搭建UI框架。
    使用两个Row组件分别呈现背景图和火柴人,第二个Row组件作为第一个Row组件的子组件,父Row组件的背景设置为背景图,子Row组件中添加Image组件用来呈现火柴人单帧图像。
@Entry
@Component
export default struct frameAnimation {
  build() {
    Column() {
      // 父Row组件
      Row() {
        // 子Row组件
        Row() {
          // 通过Image组件显示火柴人图像
          Image($r("app.media.man")).height(60).width(545.16)
        }.width(100)
        .justifyContent(FlexAlign.Start)
        .alignItems(VerticalAlign.Top)
        // 截取显示与背景同等大小的区域,控制单个火柴人显示在画面中
        .clip(true)
      }
      // 添加背景图像
      .backgroundImage($r("app.media.background"))
      // 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
      .backgroundImageSize(ImageSize.Cover)
      .width('100%')
      .height(130)
      .justifyContent(FlexAlign.Center)
      .alignItems(VerticalAlign.Bottom)
      Row() {
        // 添加跑动按钮
        Button('run')
          .margin({ right: 10 })
          .type(ButtonType.Normal)
          .width(75)
          .borderRadius(5)
        // 添加停止按钮
        Button('stop')
          .type(ButtonType.Normal)
          .borderRadius(5)
          .width(75)
          .backgroundColor('#ff0000')
      }.margin({ top: 30, bottom: 10 })
    }.width('100%').width('100%').padding({ top: 30 })
  }
}

2.添加火柴人和背景图片的移动逻辑。
通过状态变量设定火柴人和背景图片的位置,位置变化时可以实时刷新UI界面。

// 火柴人位置变量
@State manPostion: {
  x: number,
  y: number
} = { x: 0, y: 0 }
// 背景图位置变量
@State treePosition: {
  x: number,
  y: number
} = { x: 0, y: 0 }

给火柴人和背景图片添加位置属性。

Row() {
  Row() {
    Image($r("app.media.man"))
      .height(60)
      .width(545.16)
      // 通过translate实现火柴人的位移。绑定manPosition,用来改变火柴人位置。
      .translate(this.manPostion)
  }
  ...
}
.backgroundImage($r("app.media.background"))
.backgroundImageSize(ImageSize.Cover)
// 通过backgroundImagePosition实现背景图片的位移。绑定treePosition,用来改变背景图片的位置。
.backgroundImagePosition(this.treePosition)
...

3.为''run''按钮和"stop"按钮绑定控制逻辑。
构建火柴人和背景图片移动的方法,用来设定火柴人和背景图片每次移动的距离。这里要注意火柴人每次移动的距离等于两个火柴人之间的间隔距离(像素值)。

// 火柴人移动方法
manWalk() {
  if (this.manPostion.x <= -517.902) {
    this.manPostion.x = 0
  } else {
    // 每次移动的距离为火柴人静态图像之间的间隔距离
    this.manPostion.x -= 129.69
  }
}
// 背景移动方法
treesMove() {
  if (this.treePosition.x <= -1215) {
    this.treePosition.x = 0
  } else {
    this.treePosition.x -= 20
  }
}

创建doAnimation()方法调用上述两个方法,以便在后续的定时器中使用。

doAnimation() {
  this.manWalk()
  this.treesMove()
  }

通过setInterval为“run”按钮绑定走动逻辑。

Button('run')
  .margin({ right: 10 })
  .type(ButtonType.Normal)
  .width(75)
  .borderRadius(5)
  .onClick(() => {
    this.clearAllInterval()
    // 创建定时器,调用doAnimation方法,启动动画
    let timer = setInterval(this.doAnimation.bind(this), 100)
    this.timerList.push(timer)
  })

通过clearAllInterval为“stop”按钮绑定停止逻辑。

Button('stop')
  .type(ButtonType.Normal)
  .borderRadius(5)
  .width(75)
  .backgroundColor('#ff0000')
  .onClick(() => {
    // 清理定时器,停止动画
    this.clearAllInterval()
})

完整代码

本例完整代码如下:

@Entry
@Component
export default struct frameAnimation {
  // 火柴人位置变量
  @State manPostion: {
    x: number,
    y: number
  } = { x: 0, y: 0 }
  // 背景图位置变量
  @State treePosition: {
    x: number,
    y: number
  } = { x: 0, y: 0 }
  // 定时器列表,当列表清空时,动画停止
  private timerList: number[] = []

  // 火柴人移动方法
  manWalk() {
    if (this.manPostion.x <= -517.902) {
      this.manPostion.x = 0
    } else {
      this.manPostion.x -= 129.69
    }
  }
  // 背景移动方法
  treesMove() {
    if (this.treePosition.x <= -1215) {
      this.treePosition.x = 0
    } else {
      this.treePosition.x -= 20
    }
  }

  // 销毁所有定时器
  clearAllInterval() {
    this.timerList.forEach((timer: number) => {
      clearInterval(timer)
    })
    this.timerList = []
  }

  doAnimation() {
    this.manWalk()
    this.treesMove()
  }

  build() {
    Column() {
      // 父Row组件
      Row() {
        // 子Row组件
        Row() {
          // 通过Image组件显示火柴人图像
          Image($r("app.media.man"))
            .height(60)
            .width(545.16)
            // 通过translate实现火柴人的位移。绑定manPosition变量,用来改变火柴人位置。
            .translate(this.manPostion)
        }
        .width(100)
        .justifyContent(FlexAlign.Start)
        .alignItems(VerticalAlign.Top)
        // 截取显示与背景同等大小的区域,控制单个火柴人显示在画面中
        .clip(true)
      }
      // 添加背景图像
      .backgroundImage($r("app.media.background"))
      // 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
      .backgroundImageSize(ImageSize.Cover)
      // 通过backgroundImagePosition实现背景图片的位移。绑定treePosition,用来改变背景图片的位置。
      .backgroundImagePosition(this.treePosition)
      .width('100%')
      .height(130)
      .justifyContent(FlexAlign.Center)
      .alignItems(VerticalAlign.Bottom)

      Row() {
        // 添加跑动按钮
        Button('run')
          .margin({ right: 10 })
          .type(ButtonType.Normal)
          .width(75)
          .borderRadius(5)
          .onClick(() => {
            this.clearAllInterval()
            let timer = setInterval(this.doAnimation.bind(this), 100)
            this.timerList.push(timer)
          })
        // 添加停止按钮
        Button('stop')
          .type(ButtonType.Normal)
          .borderRadius(5)
          .width(75)
          .backgroundColor('#ff0000')
          .onClick(() => {
            this.clearAllInterval()
          })
      }.margin({ top: 30, bottom: 10 })
    }.width('100%').width('100%').padding({ top: 30 })
  }
}

参考

标签:OpenHarmony,动画,number,width,逐帧,火柴,组件,背景图片,Row
From: https://blog.51cto.com/u_14452884/6267661

相关文章

  • vue+cesium实现卫星在轨绕行动画
    1、初始化蓝星首先要实现这个功能,一定要开启时间轴timeline:true,//是否显示时间线控件this.viewer=newCesium.Viewer('cesiumContainer',{    baseLayerPicker:false, //影像切换    animation:false, //是否显示动画控件......
  • 成品直播源码推荐,js点击让窗口抖动动画效果
    成品直播源码推荐,js点击让窗口抖动动画效果比如说用户的未输入密码就点击登录按钮,则输入框会晃动一下提示用户需要输入,实现这种效果很简单,只需要给元素添加一个类,然后做一个关键帧动画即可css代码 .shake{   animation:shake800msease-in-out; }@keyframesshake{......
  • web前端动画网址
    很实用的web前端动画网址1.https://lhammer.cn/You-need-to-know-css/#/zh-cn/2.https://chokcoco.github.io/CSS-Inspiration/#/3.https://qishaoxuan.github.io/css_tricks/4.https://tobiasahlin.com/spinkit/5.https://animista.net/​————————————————原文......
  • Vue路由跳转时的动画效果
    1.写一个layout组件,降<router-view/>包裹在transition标签里,实现路由跳转时的动画 2.在router/index.js里面引入该组件,并放在component:layout这里,功能完成 3.transition是vue的封装组件,具体可参考官网 https://cn.vuejs.org/guide/built-ins/transition.html#css-based-......
  • flutter系列之:做一个修改组件属性的动画
    目录简介flutter中的动画widgetAnimatedContainers使用举例总结简介什么是动画呢?动画实际上就是不同的图片连续起来形成的。flutter为我们提供了一个AnimationController来对动画进行详尽的控制,不过直接是用AnimationController是比较复杂的,如果只是对一个widget的属性进行修改,......
  • 如何利用Spine制作简单的2D骨骼动画(附软件下载)
    在2D游戏中,我们经常看到各种各样的角色动画。动画能给游戏带来生机和灵气。创作一段美妙的动画,不仅需要强大的软件工具,更需要一套完善的工作流程。Spine就是一款针对游戏开发的2D骨骼动画编辑工具。Spine可以提供更高效和简洁的工作流程,以创建游戏所需的动画。下载Spine3.8.7......
  • 开机动画生成可烧写bin文件
    1、进入LVGL的官网Onlineimageconverter-BMP,JPGorPNGtoCarrayorbinary|LVGL得到原图片的bin文件但是它生成的bin文件会有4个字节的文件头,所以在使用的时候记得跳过这4字节2、合并大bin将如下代码贴到空白的bat文件中,双击运行即可得到output.bin1@echoof......
  • 第六节:受控 、高阶组件、portals、fragment、严格模式、动画
    一.受控组件       二.高阶组件       三.portals      四.fragment        五.严格模式        六.动画         !作       者:Yaopengfei(姚鹏飞)博客地......
  • 108中超轻量级的加载动画!
    大家好,我是【程序视点】小二哥!今天要上的菜不是Animate.js,也不是Move.js,而是能提供108种加载动画的库:Whirl.最省力的加载动画话不多说,直接来看例子。以上只是冰山一角。whirl的CSS加载动画集合中有108种选项供你挑选。选中喜欢的动画后,点击“GrabtheCSSonGithub......
  • 使用UE做动画编辑-VRAnimEditor
    这个编辑不同于blender,和UE中的动画编辑。我的动画编辑基于VR中姿态矫正,以及补帧插值方式做处理。好处: 传统的动画编辑 需要花费大量时间,比较好的动作捕捉使用起来有局限,大厂用的AI模拟生物应该没到实用阶段,而这个使用VR矫正姿态,可以缩短时间,并且实时预览,效果好(不管是编辑还......