05鸿蒙【动画】

动画

  • 属性动画
  • 显式动画
  • 组件转场动画

手翻书的原理:
手翻书的原理主要是基于帧动画(Frame Animation)的概念。它通过在连续的画面中绘制微小的变化,制造出连贯的动画效果。每个画面被称为一帧,通过快速连续播放帧动画,让静止的图像变得活跃起来。手翻书可以被看作是动画片的简化版,利用人的视觉停留原理,通过手绘动画的形式展现内容。

在这里插入图片描述

在这里插入图片描述
我们只需要关注,一个组件的开始状态和结束状态,Arkui帮我们填充变化的过程

属性动画

在这里插入图片描述

小鱼案例

在这里插入图片描述
在这里插入图片描述

显式动画

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//  小鱼动画案例按钮版

import router from '@ohos.router'
@Entry
@Component
struct AnimationPage {
  // 小鱼坐标
  @State fishX: number = 200
  @State fishY: number = 180
  // 小鱼角度
  @State angle: number = 0
  // 小鱼图片
  @State src: Resource = $r('app.media.fish')

  // 是否开始游戏
  @State isBegin: boolean = false

  build() {
    Row() {
      Stack(){
        // 返回按钮
        Button('返回')
          .position({x:0, y: 0})
          .backgroundColor('#20101010')
          .onClick(() => {
            // 返回上一页
            router.back()
          })

        if(!this.isBegin){
          // 开始按钮
          Button('开始游戏')
            .onClick(() => {
              animateTo(
                {duration: 1000},
                () => {
                  // 点击后显示小鱼
                  this.isBegin = true
                }
              )
            })
        }else{
          // 小鱼图片
          Image(this.src)
            .position({x: this.fishX - 20, y: this.fishY - 20})
            .rotate({angle:this.angle, centerX: '50%', centerY: '50%'})
            .width(40)
            .height(40)
              //.animation({duration: 500, curve: Curve.Smooth})
            .transition({
              type: TransitionType.Insert,
              opacity: 0,
              translate: {x: -250}
            })
        }
        // 操作按钮
        Row(){
          Button('←').backgroundColor('#20101010')
            .onClick(() => {
              animateTo(
                {duration: 500},
                () => {
                  this.fishX -= 20
                  this.src = $r('app.media.fish_rev')
                }
              )
            })
          Column({space: 40}){
            Button('↑').backgroundColor('#20101010')
              .onClick(() => {
                animateTo(
                  {duration: 500},
                  () => {
                    this.fishY -= 20
                  }
                )
              })
            Button('↓').backgroundColor('#20101010')
              .onClick(() => {
                animateTo(
                  {duration: 500},
                  () => {
                    this.fishY += 20
                  }
                )
              })
          }
          Button('→').backgroundColor('#20101010')
            .onClick(() => {
              animateTo(
                {duration: 500},
                () => {
                  this.fishX += 20
                  this.src = $r('app.media.fish')
                }
              )
            })
        }
        .height(240)
        .width(240)
        .justifyContent(FlexAlign.Center)
        .position({x:0,y:120})
      }
      .height('100%').width('100%')
    }
    .height('100%')
    .width('100%')
    .backgroundImage($r('app.media.sea'))
    .backgroundImageSize({height: '105%', width: '100%'})
  }
}

组件转场动画

在这里插入图片描述
在这里插入图片描述
注意入场出场动画要结合animateTo使用
显示与否的控制要放在animateTo里

在这里插入图片描述

//  小鱼动画案例按钮版

import router from '@ohos.router'
@Entry
@Component
struct AnimationPage {
  // 小鱼坐标
  @State fishX: number = 200
  @State fishY: number = 180
  // 小鱼角度
  @State angle: number = 0
  // 小鱼图片
  @State src: Resource = $r('app.media.fish')

  // 是否开始游戏
  @State isBegin: boolean = false

  build() {
    Row() {
      Stack(){
        // 返回按钮
        Button('返回')
          .position({x:0, y: 0})
          .backgroundColor('#20101010')
          .onClick(() => {
            // 返回上一页
            router.back()
          })

        if(!this.isBegin){
          // 开始按钮
          Button('开始游戏')
            .onClick(() => {
              animateTo(
                {duration: 1000},
                () => {
                  // 点击后显示小鱼
                  this.isBegin = true
                }
              )
            })
        }else{
          // 小鱼图片
          Image(this.src)
            .position({x: this.fishX - 20, y: this.fishY - 20})
            .rotate({angle:this.angle, centerX: '50%', centerY: '50%'})
            .width(40)
            .height(40)
              //.animation({duration: 500, curve: Curve.Smooth})
            .transition({
              type: TransitionType.Insert,
              opacity: 0,
              translate: {x: -250} // 起始位置
            })
        }
        // 操作按钮
        Row(){
          Button('←').backgroundColor('#20101010')
            .onClick(() => {
              animateTo(
                {duration: 500},
                () => {
                  this.fishX -= 20
                  this.src = $r('app.media.fish_rev')
                }
              )
            })
          Column({space: 40}){
            Button('↑').backgroundColor('#20101010')
              .onClick(() => {
                animateTo(
                  {duration: 500},
                  () => {
                    this.fishY -= 20
                  }
                )
              })
            Button('↓').backgroundColor('#20101010')
              .onClick(() => {
                animateTo(
                  {duration: 500},
                  () => {
                    this.fishY += 20
                  }
                )
              })
          }
          Button('→').backgroundColor('#20101010')
            .onClick(() => {
              animateTo(
                {duration: 500},
                () => {
                  this.fishX += 20
                  this.src = $r('app.media.fish')
                }
              )
            })
        }
        .height(240)
        .width(240)
        .justifyContent(FlexAlign.Center)
        .position({x:0,y:120})
      }
      .height('100%').width('100%')
    }
    .height('100%')
    .width('100%')
    .backgroundImage($r('app.media.sea'))
    .backgroundImageSize({height: '105%', width: '100%'})
  }
}

小鱼案例

计算方法,三角函数
在这里插入图片描述

请添加图片描述

Math.atan2()方法计算二维坐标系中任意一个点(x, y)和原点(0, 0)的连线与X轴正半轴的夹角大小。

// 所以Math.atan2(vy, vx)就可以计算出,角BAC的夹角,
// 3.计算手指与中心点连线和x轴正半轴的夹角,单位是弧度
        let angle = Math.atan2(vy, vx)
 // 5.计算摇杆小球的坐标
            this.positionX = this.centerX + distance * this.cos
            this.positionY = this.centerY + distance * this.sin

请添加图片描述
小鱼的角度
在这里插入图片描述

   // 6.修改小鱼的角度
            if(Math.abs(angle * 2) < Math.PI){  // 注意这里angle是弧度, 90度=π/2
              this.src = $r('app.media.fish')
            }else{
              this.src = $r('app.media.fish_rev')
              angle = angle < 0 ? angle + Math.PI : angle - Math.PI
            }
            this.angle = angle * 180 / Math.PI  // 弧度转角度  弧度和角度的关系:弧度数/π=角度值/180°。

            this.speed = 5

小鱼全部代码

import router from '@ohos.router'
import curves from '@ohos.curves'

@Entry
@Component
struct AnimationPage2 {
  // 小鱼坐标
  @State fishX: number = 200
  @State fishY: number = 120
  // 小鱼角度
  @State angle: number = 0
  // 小鱼图片
  @State src: Resource = $r('app.media.fish')

  // 是否开始游戏
  @State isBegin: boolean = false

  // 摇杆中心区域坐标
  private centerX: number = 120
  private centerY: number = 120

  // 大、小圆半径
  private maxRadius: number = 100
  private radius: number = 20

  // 摇杆小圆球初始位置
  @State positionX: number = this.centerX;
  @State positionY: number = this.centerY;

  // 角度正弦和余弦
  sin: number = 0
  cos: number = 0

  // 小鱼移动速度
  speed: number = 0
  // 任务id
  taskId: number = -1


  build() {
    Row() {
      Stack(){
        // 返回按钮
        Button('返回')
          .position({x:0, y: 0})
          .backgroundColor('#20101010')
          .onClick(() => {
            // 返回上一页
            router.back()
          })

        // 开始按钮和小鱼
        if(!this.isBegin){
          // 开始按钮
          Button('开始游戏')
            .onClick(() => {
              animateTo(
                {duration: 1000},
                () => {
                  // 点击后显示小鱼
                  this.isBegin = true
                }
              )
            })
        }else{
          // 小鱼图片
          Image(this.src)
            .position({x: this.fishX - 20, y: this.fishY - 20})
            .rotate({angle:this.angle, centerX: '50%', centerY: '50%'})
            .width(40)
            .height(40)
            .transition({
              type: TransitionType.Insert,
              opacity: 0,
              translate: {x: -250}
            })
        }

        // 摇杆
        Row(){
          Circle({width: this.maxRadius * 2, height: this.maxRadius * 2})
            .fill('#20101010')
            .position({ x: this.centerX-this.maxRadius, y: this.centerY-this.maxRadius })
          Circle({ width: this.radius * 2, height: this.radius * 2 })
            .fill('#403A3A3A')
            .position({ x: this.positionX - this.radius, y: this.positionY - this.radius})
        }
        .height(240)
        .width(240)
        .justifyContent(FlexAlign.Center)
        .position({x:0,y:120})
        .onTouch(this.handleTouchEvent.bind(this))  // 手指在移动过程中,会移除大圆,所以我们把,监听放到大容器上
      }
      .height('100%').width('100%')
    }
    .height('100%')
    .width('100%')
    .backgroundImage($r('app.media.sea'))
    .backgroundImageSize({height: '105%', width: '100%'})
  }
  // 处理手指移动的事件
  handleTouchEvent(event: TouchEvent){
    switch (event.type){   // 对手指移动类型做判断,离开停下小鱼,摇杆还原
      case TouchType.Up:
      // 还原小鱼速度
        this.speed = 0
      // 取消定时任务
        clearInterval(this.taskId)
      // 还原摇杆小球的坐标
        animateTo(
          {curve: curves.springMotion()},  // 还原动画
          () => {
            this.positionX = this.centerX
            this.positionY = this.centerY

            this.angle = 0
          }
        )
        break
      case TouchType.Down:
      // 开始定时任务
        this.taskId = setInterval(() => {
          this.fishX += this.speed * this.cos
          this.fishY += this.speed * this.sin
        }, 40)
        break

      case TouchType.Move:
      // 1.获取手指位置坐标
        let x = event.touches[0].x
        let y = event.touches[0].y
      // 2.计算手指与中心点的坐标差值
        let vx = x - this.centerX
        let vy = y - this.centerY
      // 3.计算手指与中心点连线和x轴正半轴的夹角,单位是弧度
        let angle = Math.atan2(vy, vx)
      // 4.计算手指与中心点的距离
        let distance = this.getDistance(vx, vy)

        this.sin = Math.sin(angle)
        this.cos = Math.cos(angle)
        animateTo(
          {curve: curves.responsiveSpringMotion()},   // 跟守动画,持续的动画曲线
          () => {
            // 5.计算摇杆小球的坐标
            this.positionX = this.centerX + distance * this.cos
            this.positionY = this.centerY + distance * this.sin

            // 6.修改小鱼的角度
            if(Math.abs(angle * 2) < Math.PI){  // 注意这里angle是弧度, 90度=π/2
              this.src = $r('app.media.fish')
            }else{
              this.src = $r('app.media.fish_rev')
              angle = angle < 0 ? angle + Math.PI : angle - Math.PI
            }
            this.angle = angle * 180 / Math.PI  // 弧度转角度  弧度和角度的关系:弧度数/π=角度值/180°。

            this.speed = 5
          }
        )
        break
    }
  }
  getDistance(x: number, y: number){
    let d = Math.sqrt(x * x + y * y)
    return Math.min(d, this.maxRadius)
  }
}
  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值