鸿蒙应用开发学习:改进小鱼动画实现按键一直按下时控制小鱼移动和限制小鱼移出屏幕

news2024/11/19 2:24:54

一、前言

近期我在学习鸿蒙应用开发,跟着B站UP主黑马程序员的视频教程做了一个小鱼动画应用,UP主提供的小鱼动画源代码仅仅实现了移动组件的功能,还存在一些问题,如默认进入页面是竖屏而页面适合横屏显示;真机测试发现手机的状态栏影响到了返回键对按键事件的响应;方向键不能响应一直按着的操作;还有小鱼会移出屏幕范围。

之前已经解决了强制横屏和隐藏手机状态栏,这次则是通过一番研究,实现了按键一直按下时控制小鱼移动和限制小鱼移出屏幕这两个功能。

二、实现方法

1. 一直按下方向键时控制小鱼移动

实现这一功能是在方向键下添加onTouch方法,对按键一直按下事件进行响应。在onTouch方法中还需要判断TouchType.Down事件和TouchType.Up事件。在TouchType.Down事件时,添加animateTo方法,实现按键时一直控制小鱼移动(需要通过setInterval方法设置定时任务让animateTo方法定期执行)。在TouchType.Up事件时,通过clearInterval清除定时任务,小鱼就不会一直移动了。以向右按键为例,改造后的代码如下:

Button('→').backgroundColor('#20101010')
            .onClick(() => {              
                animateTo(
                 { duration: 500 },
                 () => {
                   this.src = $r('app.media.fish')
                   this.fishX += this.speed
                 }
               )
            })
            .onTouch((event: TouchEvent) => {
              if (event.type === TouchType.Down) {
                this.taskId = setInterval(() => {
                  animateTo(
                    { duration: 500 },
                    () => {                      
                    this.src = $r('app.media.fish')
                    this.fishX += this.speed
                    }
                  )
                }, 200)
              }
              if (event.type === TouchType.Up) {
                clearInterval(this.taskId)
                this.taskId = -1
              }
            })

        }
        .height(240)
        .width(240)
        .justifyContent(FlexAlign.Center)
        .position({ x: 0, y: 120 })

2.限制小鱼移出屏幕

实现了上面的代码后,一直按下方向键,小鱼终于可以一直移动了,但往一个方向一直移动就会移出屏幕,为让小鱼不移出屏幕,还需要对按键操作事件进行判断,检查当前小鱼的位置,只有小鱼在限制的范围内才能执行animateTo方法移动小鱼。

按下向左方向键:对小鱼的X坐标(this.fishX)进行判断。屏幕左侧边界的X值为0,小鱼的大小为40(this.fishSize)。this.fishX是小鱼图片中心点的坐标,则当小鱼接触到屏幕左侧边界时,小鱼的中心点X坐标值为20。本软件中设置的小鱼的移动速度为20(this.speed),因此,我设置的判断条件是当this.fishX >= this.fishSize时,才能执行animateTo方法。当this.fishX == 40时,再移动一次this.fishX就变成了20,此时小鱼图片的左侧边缘正好接触到屏幕左边界。

按下向上方向键:对小鱼的Y坐标(this.fishY)进行判断。屏幕上边界Y值为0,小鱼大小为40。原理和按下向左方向机一样。我设置的判断条件是当this.fishY >= this.fishSize时,才能执行animateTo方法。

对于屏幕下方的边界和屏幕右侧的边界判断需要导入模块 import display from '@ohos.display' ,并在页面的onPageShow方法获取屏幕的尺寸信息。

onPageShow() {
    // 获取旋转的方向,具体可以查看对应文档
    let orientation = window.Orientation.LANDSCAPE;

    // 获取屏幕尺寸信息
    let promise = display.getAllDisplays()
    promise.then((data) => {
      console.info('设备屏幕信息:' + JSON.stringify(data));
      console.info('testTag', '屏幕宽度px:' + JSON.stringify(data[0].width));
      console.info('testTag', '屏幕高度px:' + JSON.stringify(data[0].height));
      this.screenWidth = px2vp(data[0].width)
      this.screenHeight = px2vp(data[0].height)
      console.info('testTag', '屏幕宽度vp:' + JSON.stringify(this.screenWidth));
      console.info('testTag', '屏幕高度vp:' + JSON.stringify(this.screenHeight));
    }).catch((err) => {
      console.error('错误信息:' + JSON.stringify(err));
    })
  }

按下向右方向机:对小鱼的X坐标(this.fishX)进行判断。屏幕右侧边界的X值为变量this.screenHeight, 则判定语句为 this.fishX <= this.screenHeight - this.fishSize 。只有符合该条件是才执行animateTo方法。

按下向下方向机:对小鱼的Y坐标(this.fishY)进行判断。屏幕右侧边界的Y值为变量this.screenWidth, 则判定语句为 this.fishY <= this.screenWidth - this.fishSize 。只有符合该条件是才执行animateTo方法。

这些判断语句都要添加到方向键的onClick方法和onTouch方法。

三、完整源代码

最后上这个文件的完整源代码:

import router from '@ohos.router';
import window from '@ohos.window'; // 用于强制设为横屏
import display from '@ohos.display'

@Entry
@Component
struct Aquarium1Page {
  onPageShow() {
    // 获取旋转的方向,具体可以查看对应文档
    let orientation = window.Orientation.LANDSCAPE;
    try {
      // 设置屏幕旋转
      globalThis.windowClass.setPreferredOrientation(orientation, (err) => {
        console.log('testTag', `onPageShow函数中setPreferredOrientation方法错误码为${err}`)
      });
    } catch (exception) {
      console.error('设置失败: ' + JSON.stringify(exception));
    }
    // 获取屏幕尺寸信息
    let promise = display.getAllDisplays()
    promise.then((data) => {
      console.info('设备屏幕信息:' + JSON.stringify(data));
      console.info('testTag', '屏幕宽度px:' + JSON.stringify(data[0].width));
      console.info('testTag', '屏幕高度px:' + JSON.stringify(data[0].height));
      this.screenWidth = px2vp(data[0].width)
      this.screenHeight = px2vp(data[0].height)
      console.info('testTag', '屏幕宽度vp:' + JSON.stringify(this.screenWidth));
      console.info('testTag', '屏幕高度vp:' + JSON.stringify(this.screenHeight));
    }).catch((err) => {
      console.error('错误信息:' + JSON.stringify(err));
    })
  }

  onPageHide() {
    // 获取旋转的方向,具体可以查看对应文档
    let orientation = window.Orientation.PORTRAIT;
    try {
      // 设置屏幕旋转
      globalThis.windowClass.setPreferredOrientation(orientation, (err) => {
        console.log('testTag', `onPageHide函数中setPreferredOrientation方法错误码为${err}`)
      });
    } catch (exception) {
      console.error('设置失败: ' + JSON.stringify(exception));
    }
  }

  // 小鱼的位置
  @State fishX: number = 200
  @State fishY: number = 180
  // 小鱼的大小
  fishSize: number = 40
  // 小鱼角度
  @State angle: number = 0
  // 小鱼图片
  @State src: Resource = $r('app.media.fish')
  // 是否开始游戏
  @State isBegin: boolean = false
  // 小鱼的速度
  speed: number = 20
  // 用于控制Interval的id
  taskId: number = -1
  // 屏幕尺寸
  screenWidth: number = px2vp(2000)
  screenHeight: number = px2vp(1080)

  build() {
    Row() {
      Stack() {
        Button('返回')
          .position({ x: 20, y: 20 })
          .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(this.fishSize)
            .height(this.fishSize)
              //.animation({duration: 500, curve: Curve.Smooth})
            .transition({
              type: TransitionType.Insert,
              opacity: 0,
              translate: { x: -250 }
            })
        }
        // 操作按钮
        Row() {
          // 向左移动,小鱼身体不能超出屏幕范围
          Button('←').backgroundColor('#20101010')
            .onClick(() => {
              if (this.fishX >= this.fishSize) {
                animateTo(
                  { duration: 500 },
                  () => {
                    this.src = $r('app.media.fish_rev')
                    this.fishX -= this.speed
                  }
                )
              }
            })
            .onTouch((event: TouchEvent) => {
              if (event.type === TouchType.Down) {
                this.taskId = setInterval(() => {
                  animateTo(
                    { duration: 500 },
                    () => {
                      if (this.fishX >= this.fishSize) {
                        this.src = $r('app.media.fish_rev')
                        this.fishX -= this.speed
                      }
                    }
                  )
                }, 200)
              }
              if (event.type === TouchType.Up) {
                clearInterval(this.taskId)
                this.taskId = -1
              }
            })

          Column({ space: 40 }) {
            // 向上和向下移动,小鱼的身体均不能超出屏幕范围
            Button('↑').backgroundColor('#20101010')
              .onClick(() => {
                if (this.fishY >= this.fishSize) {
                  animateTo(
                    { duration: 500 },
                    () => {
                      this.fishY -= this.speed
                    }
                  )
                }
              })
              .onTouch((event: TouchEvent) => {
                if (event.type === TouchType.Down) {
                  this.taskId = setInterval(() => {
                    animateTo(
                      { duration: 500 },
                      () => {
                        if (this.fishY >= this.fishSize) {
                          this.fishY -= this.speed
                        }
                      }
                    )
                  }, 200)
                }
                if (event.type === TouchType.Up) {
                  console.log("testTag", `停止向上,当前fishY为:${this.fishY}`)
                  clearInterval(this.taskId)
                  this.taskId = -1
                }
              })
            Button('↓').backgroundColor('#20101010')
              .onClick(() => {
                if (this.fishY <= this.screenWidth - this.fishSize) {
                  animateTo(
                    { duration: 500 },
                    () => {
                      this.fishY += this.speed
                    }
                  )
                }
              })
              .onTouch((event: TouchEvent) => {
                if (event.type === TouchType.Down) {
                  this.taskId = setInterval(() => {
                    animateTo(
                      { duration: 500 },
                      () => {
                        if (this.fishY <= this.screenWidth - this.fishSize) {
                          this.fishY += this.speed
                        }
                      }
                    )
                  }, 200)
                }
                if (event.type === TouchType.Up) {
                  console.log("testTag", `停止向下,当前fishY为:${this.fishY}`)
                  clearInterval(this.taskId)
                  this.taskId = -1
                }
              })
          }

          Button('→').backgroundColor('#20101010')
            .onClick(() => {
              if (this.fishX <= this.screenHeight - this.fishSize) {
                animateTo(
                  { duration: 500 },
                  () => {
                    this.src = $r('app.media.fish')
                    this.fishX += this.speed
                  }
                )
              }
            })
            .onTouch((event: TouchEvent) => {
              if (event.type === TouchType.Down) {
                this.taskId = setInterval(() => {
                  animateTo(
                    { duration: 500 },
                    () => {
                      if (this.fishX <= this.screenHeight - this.fishSize) {
                        this.src = $r('app.media.fish')
                        this.fishX += this.speed
                      }
                    }
                  )
                }, 200)
              }
              if (event.type === TouchType.Up) {
                clearInterval(this.taskId)
                this.taskId = -1
              }
            })

        }
        .height(240)
        .width(240)
        .justifyContent(FlexAlign.Center)
        .position({ x: 0, y: 120 })
      }
      .height('100%').width('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundImage($r('app.media.underwater_cartoon'))
    .backgroundImageSize({ height: '100%', width: '100%' })

  }
}

四、B站视频链接:

鸿蒙应用开发学习:改进小鱼动画实现按键一直按下时控制小鱼移动和限制小鱼移出屏幕-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1384729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeeCode前端算法基础100题(20)找出字符串中第一个匹配项的下标

一、问题详情: 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。 示例 1: 输入:haystack = "sadbutsad", needle = "s…

MySQL面试题 | 07.精选MySQL面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Pushmall智能AI数字名片— —SCRM客户资源管理系统

推贴数字AI名片说明&#xff1a; **Pushmall智能AI数字名片&#xff1a;**基于靠谱人脉的地理位置服务&#xff0c;资源查询&#xff0c;数字名片定制服务、企业名片&#xff1a;相互引荐、提供需求&#xff1b;建人脉群、客户群&#xff0c;及简介、短视频、宣传文档、电子图…

Flutter使用stack来实现悬浮UI

文章目录 stack特性示例 stack特性 在Flutter中&#xff0c;你可以使用Stack和Positioned来创建悬浮 UI。Stack允许你将多个小部件叠放在一起&#xff0c;而Positioned则用于定位小部件在Stack中的位置。 示例 以下是一个简单的示例&#xff0c;演示如何创建一个悬浮按钮&am…

力扣每日一练(24-1-14)

做过类似的题&#xff0c;一眼就是双指针&#xff0c;刚好也就是题解。 if not nums:return 0p1 0 for p2 in range(1, len(nums)):if nums[p2] ! nums[p1]:p1 1nums[p1] nums[p2]return p1 1 根据规律&#xff0c;重复的数字必定相连&#xff0c;那么只要下一个数字与上一…

Codeforces Round 779 (Div. 2) D2. 388535(思维题 二进制性质/trie树上最大最小异或)

题目 t(t<1e5)组样例&#xff0c;每次给定l,r(0<l<r<2^17) 和r-l1个数ai&#xff0c;新序列是被[l,r]这些数异或上同一个x得到的&#xff0c; 求出x&#xff0c;有多个输出任意一个即可 思路来源 官方题解 洛谷题解 Educational Codeforces Round 157 (Rated…

redis中的string相关的部分命令

redis命令手册 redis中文官网查看文档 挨个进行输出调试 Redis Setnx 命令 Redis Getrange 命令 Redis Mset 命令 redis 127.0.0.1:6379> MSET key1 "Hello" key2 "World" OK redis 127.0.0.1:6379> GET key1 "Hello" redis 127.0.0.1:…

(菜鸟自学)搭建虚拟渗透实验室——安装Kali Linux

安装Kali Linux Kali Linux 是一种基于 Debian 的专为渗透测试和网络安全应用而设计的开源操作系统。它提供了广泛的渗透测试工具和安全审计工具&#xff0c;使安全专业人员和黑客可以评估和增强网络的安全性。 安装KaliLinux可参考我的另一篇文章《Kali Linux的下载安装以及基…

大模型实战05——LMDeploy大模型量化部署实践

大模型实战05——LMDeploy大模型量化部署实践 1、大模型部署背景 2、LMDeploy简介 3、动手实践环节——安装、部署、量化 注 笔记内容均为截图 笔记课程视频地址&#xff1a;https://www.bilibili.com/video/BV1iW4y1A77P/?spm_id_from333.788&vd_source2882acf8c823ce…

等保测评流程是什么样的?测评周期多久?

等保测评流程是什么样的&#xff1f;测评周期多久&#xff1f; 等保测评一般分成五个阶段&#xff0c;定级、备案、测评、整改、监督检查。 1.定级阶段 针对用户的信息系统有等保专家进行定级&#xff0c;一般常见的系统就是三级系统或者是二级系统。在这里有一个小的区分&am…

新年福利大放送-UDS学习宝典

新年福利大放送-UDS学习宝典 小T年末总结 新年伊始&#xff0c;小T首先给大家送上真挚的祝福&#xff1a; 祝大家新年快乐&#xff0c;心想事成&#xff0c;财源广进&#xff0c;永远活出自己最舒服自在的状态&#xff01; 在过去的一年中感谢小伙伴对小T作品的认可与支持&…

通过Wireshark抓包分析谈谈DNS域名解析的那些事儿

原创/朱季谦 本文主要想通过动手实际分析一下是如何通过DNS服务器来解析域名获取对应IP地址的&#xff0c;毕竟&#xff0c;纸上得来终觉浅&#xff0c;绝知此事要躬行。 一、域名与IP地址 当在浏览器上敲下“www.baidu.com”时&#xff0c;一键回车&#xff0c;很快&#x…

财务管理软件,用表格导出账目明细

不论是工资收入&#xff0c;还是日常花销&#xff0c;每一笔钱都需要我们认真对待。然而&#xff0c;许多人在财务管理上仍然采用传统的纸质记账方式&#xff0c;这不仅容易丢失数据&#xff0c;还难以实现财务的统一管理。为此&#xff0c;我为大家推荐一款简单好用的记账软件…

基于深度学习的多类别电表读数识别方案详解

基于深度学习的多类别电表读数识别方案详解 多类别电表读数识别方案详解项目背景项目难点最终项目方案系列项目全集&#xff1a; 安装说明环境要求 数据集简介数据标注模型选型明确目标&#xff0c;开始下一步的操作 检测模型训练模型评估与推理番外篇&#xff1a;基于目标检测…

linux Tcp总结

Tcp连接建立时的影响因素 在Client发出SYN后&#xff0c;如果过了1秒 &#xff0c;还没有收到Server的响应&#xff0c;那么就会进行第一次重传&#xff1b;如果经过2s的时间还没有收到Server的响应&#xff0c;就会进行第二次重传&#xff1b;一直重传tcp_syn_retries次。 对…

无公网ip如何随时随地远程查看本地群晖NAS存储的文件资源

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…

python -- pyQt5中 样式设置

一、父控件设置样式表后对子控件产生影响&#xff0c;控制styleSheet的作用范围 https://blog.csdn.net/qq_31073871/article/details/90288625 QFrame 作为容器&#xff0c;放入其他多种部件&#xff0c;里面的边框都生效 在类名后面用 #号串接变量名&#xff0c;子控件不…

浅学Linux之旅 day1 学习路线及计算机入门知识介绍

我不要做静等被掀起的轻波&#xff0c;我要生起翠绿的斑驳 偶尔过季的遭遭人事化长风拂过 思绪撕碎点燃了火 ——24.1.14 一、Linux学习路线 ①计算机入门知识介绍 ②Linux系统概述 ③Linux系统的安装和体验 ④Linux的网络配置和连接工具 ⑤Linux的目录结构 ⑥Linux的常用命令 …

Qt/QML编程学习之心得:小键盘keyboard(36)

小键盘对于qml应用是经常用到的,在qml里面,就如一个fileDialog也要自己画一样,小键盘keyboard也是要自己画的,对于相应的每个按键的clicked都要一一实现的。 这里有一个示例: 代码如下: import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Window 2.0 im…

五种嵌入式经典通信总线协议

一.先前知识 1.并行与串行 并行通信和串行通信是两种不同的数据传输方式&#xff1a; 并行通信&#xff1a;并行通信是指在同一时间使用多条并行传输的线路传输多个比特的数据。每个比特使用独立的线路进行传输&#xff0c;同时进行。这样可以在一个时钟周期内传输多个比特&…