实现思路
本例的实现有两个关键点:
● 将连续走动的火柴人拆分为多帧静态图像,在固定的时间间隔内逐帧将图像移动到动画窗口,间隔时间要小于肉眼可察觉的时间。循环上述动作,就可以实现火柴人的走动动画。 火柴人静态图像如下:
● 将背景图片以固定速度相对于火柴人走动方向反方向移动,从而实现火柴人向前走动的效果。 背景图如下:
本例使用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()
- })
复制代码
完整代码本例完整代码如下: