逐帧动画是常见的一种动画呈现形式,本例就为大家介绍如何通过 translate(),setInterval(),clearAllInterval() 等方法实现逐帧动画。
效果呈现

本例最终效果如上图:
点击“run”按钮,火柴人开始走动。
点击“stop”按钮,火柴人停止走动。
运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
IDE:DevEco Studio 3.1 Release
SDK:Ohos_sdk_public 3.2.12.5(API Version 9 Release)
实现思路
本例的实现有两个关键点: ①将连续走动的火柴人拆分为多帧静态图像,在固定的时间间隔内逐帧将图像移动到动画窗口,间隔时间要小于肉眼可察觉的时间。循环上述动作,就可以实现火柴人的走动动画。
火柴人静态图像如下:
将背景图片以固定速度相对于火柴人走动方向反方向移动,从而实现火柴人向前走动的效果。
背景图如下:

本例使用 translate() 控制火柴人的移动,用 backgroundImagePosition() 控制背景图的移动。 另外,通过 setInterval() 设置火柴人移动的时间间隔,通过 clearAllInterval() 清除移动。
开发步骤
①搭建 UI 框架
使用两个 Row 组件分别呈现背景图和火柴人,第二个 Row 组件作为第一个 Row 组件的子组件,父 Row 组件的背景设置为背景图,子 Row 组件中添加 Image 组件用来呈现火柴人单帧图像。
@Entry @Component exportdefaultstructframeAnimation{ 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}) } }
②添加火柴人和背景图片的移动逻辑
通过状态变量设定火柴人和背景图片的位置,位置变化时可以实时刷新 UI 界面。
//火柴人位置变量
@StatemanPostion:{
x:number,
y:number
}={x:0,y:0}
//背景图位置变量
@StatetreePosition:{
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)
...
③为’‘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方法,启动动画
lettimer=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
exportdefaultstructframeAnimation{
//火柴人位置变量
@StatemanPostion:{
x:number,
y:number
}={x:0,y:0}
//背景图位置变量
@StatetreePosition:{
x:number,
y:number
}={x:0,y:0}
//定时器列表,当列表清空时,动画停止
privatetimerList: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()
lettimer=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
+关注
关注
31文章
3927浏览量
20725
原文标题:OpenHarmony上实现逐帧动画
文章出处:【微信号:gh_834c4b3d87fe,微信公众号:OpenHarmony技术社区】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
SGTools--动画控件--屏幕实现动画显示 就是这么简单
【M-K1HSE开发板免费体验】相关源码之阅读和分析1-使用XComponent + Vsync 实现自定义动画
分享---储能UI界面能量流动动画实现方法
ElfBoard技术贴|如何在【RK3588】ELF 2开发板中实现自定义开机动画
如何在deepin 25上使用Blender 4.5.0
【EASY EAI Orin Nano开发板试用体验】使用Linux posix文件读取接口和LVGL的animimg(图像动画)控件实现动画播放以及
基于小凌派RK2206开发板:OpenHarmony如何使用IoT接口控制FLASH外设
基于小凌派RK2206开发板:OpenHarmony如何使用IoT接口控制UART外设
基于小凌派RK2206开发板:OpenHarmony如何使用IoT接口控制GPIO外设
S32K118EVB-Q064如何在firs帧后停止tx?
如何在RAKsmart服务器上实现企业AI模型部署
如何在SJA1110中配置IEEE802.1CB帧复制和冗余消除 (FRER)?
用DeepSeek-R1实现自动生成Manim动画

如何在OpenHarmony上实现逐帧动画?
评论