鴻蒙UI開發——基于onTouch事件實現表情選擇膠囊

1、背 景

有朋友留言說,抖音APP中,長按評論按鈕觸發的快捷表情選擇膠囊動畫比較好(效果如下圖),希望使用鴻蒙ArkTs也實現一個類似的。

圖片

本文在鴻蒙ArkTs下也實現一個類似的效果,如下:

圖片

首先,核心交互流程與抖音APP保持一致,即:

    1. 長按一個按鈕,我們可以在按鈕旁邊生成一個快捷表情選擇膠囊;

    2. 手指可以快捷的在候選表情上滑動,在選中的表情上停留時,該表情會稍微放大;

動畫細節上做了一些個人效果,例如:

  • 膠囊展示到界面時,候選表情做了一個類似IOS解鎖時的類飛入效果;

  • 我們在表情膠囊上滑動選擇時,整個表情膠囊不會隨著動畫效果的改變發生寬度抖動。

下面開始介紹如何實現,文末有源代碼,有需要的同學自取。

2、問題分析

上述的交互效果其實難點并不在于動畫效果,在于onTouch事件的管理,因為,我們在長按評論按鈕時,此時系統將會分配事件流給評論按鈕消費。

如果我們想在用戶不松手移動到其他位置,讓其他組件也生效,實現過程將稍微復雜一些。

本文的實現思路是:監聽評論按鈕的onTouch事件,在事件move過程中,實時獲取該事件的發生坐標,基于坐標去判斷坐落的表情包位置,從而控制焦點放大效果。

📢📢注意

onTouch事件的調試千萬要在真機或者模擬器中執行,在預覽器中執行可能會出現非預期的問題。

?我們怎么獲取指定組件的坐標呢?

雖然我們通過onTouch事件可以知道用戶手指的位置,那我們還有一個問題沒解決,就是怎么知道各個表情包的坐標呢?

ArkTs為我們提供了一個API,根據組件ID獲取組件實例對象, 通過組件實例對象將獲取的坐標位置和大小同步返回給調用方,接口如下:

import?{ componentUtils }?from?'@kit.ArkUI';// 調用方式let modePosition:componentUtils.ComponentInfo = componentUtils.getRectangleById("id");

3、布 局

布局比較簡單,在本文中,將整個表情膠囊用一個Row包裹,另外,評論圖標與表情膠囊整體屬于在一個Row中。示意圖如下:

圖片

為了方便動態插拔,我們將圖標資源集合使用一個數組來動態維護,資源類型定義與數組定義如下:???????

interface CommentIconInfo {??source: Resource,??id: string;}const commentIcons: Array<CommentIconInfo> =? [? ? {? ? ??id:?'page_main_icon1',? ? ??source:?$r('app.media.send_you_a_little_red_flower'),? ? },? ? {? ? ??id:?'page_main_icon2',? ? ??source:?$r('app.media.powerful'),? ? },? ? {? ? ??id:?'page_main_icon3',? ? ??source:?$r('app.media.send_heart'),? ? }, {? ??id:?'page_main_icon4',? ??source:?$r('app.media.surprise'),? },? ]

布局代碼如下:???????

build() {? Column() {? ? Row() {? ? ? Row() {? ? ? ? ForEach(this.commentIcons, (item: CommentIconInfo) => {? ? ? ? ? Image(item.source)? ? ? ? ? ? .id(item.id)? ? ? ? ? ? .width(this.selectedId === item.id ??this.selectedSize :?this.normalSize)? ? ? ? ? ? .height(this.selectedId === item.id ??this.selectedSize :?this.normalSize)? ? ? ? ? ? .padding({ left:?this.iconMarginLeft })? ? ? ? ? ? .animation({ duration:?this.animDuration, curve: curves.springMotion() })? ? ? ? ? ? .visibility(this.showCommentIconPanel ? Visibility.Visible : Visibility.None)? ? ? ? }, (item: CommentIconInfo) => {? ? ? ? ??return?`${item.id}`?// 復寫id生成器? ? ? ? })? ? ? }? ? ? .visibility(this.showCommentIconPanel ? Visibility.Visible : Visibility.None)? ? ? .backgroundColor(Color.Pink)? ? ? .width(this.showCommentIconPanel ? this.getRowWidth() : 10)? ? ? .borderRadius(this.selectedId ? 40 : 20)? ? ? .padding({ left: this.paddingHoriz, right: this.paddingHoriz })? ? ? .animation({ duration: this.animDuration, curve: curves.springMotion() })? ? ? SymbolGlyph($r('sys.symbol.ellipsis_message_fill'))? ? ? ? .fontSize(60)? ? ? ? .renderingStrategy(SymbolRenderingStrategy.MULTIPLE_COLOR)? ? ? ? .fontColor([Color.Green])? ? ? ? .margin({ left: 10 })? ? ? ? .onTouch(this.onTouchEvent)? ? }? ? .justifyContent(FlexAlign.End)? ? .width('100%')? }? .padding(20)? .height('100%')}

4、onTouch事件處理

我們在onTouch事件中需要做兩個核心事情:

  1. 控制表情膠囊的顯示/隱藏

  2. 控制用戶手指指向的表情包,并聚焦放大顯示

代碼如下:???????

private onTouchEvent = (event: TouchEvent) => {??if?(!event) {? ??return;? }? // 手指按下0.5s后彈出表情選擇面板??if?(event.type === TouchType.Down) {? ? this.lastTouchDown = Date.now();? }?else?if?(event.type === TouchType.Up || event.type === TouchType.Cancel) {? ? this.showCommentIconPanel = false;?//?松手后,取消顯示表情面板(具體消失邏輯可以根據業務需求調整)? }?else?if?(!this.showCommentIconPanel) {? ? this.showCommentIconPanel = (Date.now() - this.lastTouchDown) >?500;? }? this.checkCommentIcons(event);}// 判斷用戶手指是否選中了某個表情包private checkCommentIcons = (event: TouchEvent) => {? const touchInfo = event.touches[0];? this.selectedId =?'';??if?(!touchInfo) {? ??return;? }? const windowX = Math.ceil(touchInfo.windowX);?//?獲取用戶手指x坐標? const context = this.getUIContext();? // 檢測用戶手指x坐標可以命中的表情? this.commentIcons.forEach(iconInfo =>?{? ??if?(event.type === TouchType.Up) {? ? ??return;? ? }? ? const compInfo = componentUtils.getRectangleById(iconInfo.id);? ? const?x?= Math.ceil(context.px2vp(compInfo.windowOffset.x));? ? const width = Math.ceil(context.px2vp(compInfo.size.width));? ??if?(windowX >=?x?&& windowX <=?x?+ width) {? ? ? this.selectedId = iconInfo.id;?//?范圍中有表情被選中? ? }? });? this.commentIcons = [...this.commentIcons];?//?低成本刷新數組(淺拷貝)}

5、源代碼

示例效果如下:

圖片

下方的源代碼替換了11、15、19、22行的圖片資源后,可以正常運行。代碼如下:???????

import { componentUtils } from?'@kit.ArkUI';import { curves } from?'@kit.ArkUI';interface CommentIconInfo {??source: Resource,??id: string;}const commentIcons: Array<CommentIconInfo> =? [? ? {? ? ??id:?'page_main_icon1',? ? ??source:?$r('app.media.send_you_a_little_red_flower'),? ? },? ? {? ? ??id:?'page_main_icon2',? ? ??source:?$r('app.media.powerful'),? ? },? ? {? ? ??id:?'page_main_icon3',? ? ??source:?$r('app.media.send_heart'),? ? }, {? ??id:?'page_main_icon4',? ??source:?$r('app.media.surprise'),? },? ]@Entry@Componentstruct Index {? @State selectedSize: number = 60;? @State normalSize: number = 35;? @State showCommentIconPanel: boolean =?false;? @State commentIcons: Array<CommentIconInfo> = commentIcons;? @State selectedId: string =?'';? // 一些本地使用的量? lastTouchDown: number = 0;? iconMarginLeft: number = 5;? paddingHoriz: number = 10; // 左右padding? animDuration: number = 500;? private onTouchEvent = (event: TouchEvent) => {? ??if?(!event) {? ? ??return;? ? }? ? // 手指按下0.5s后彈出表情選擇面板? ??if?(event.type === TouchType.Down) {? ? ? this.lastTouchDown = Date.now();? ? }?else?if?(event.type === TouchType.Up || event.type === TouchType.Cancel) {? ? ? this.showCommentIconPanel =?false; // 松手后,取消顯示表情面板(具體消失邏輯可以根據業務需求調整)? ? }?else?if?(!this.showCommentIconPanel) {? ? ? this.showCommentIconPanel = (Date.now() - this.lastTouchDown) > 500;? ? }? ? this.checkCommentIcons(event);? }? private checkCommentIcons = (event: TouchEvent) => {? ? const touchInfo = event.touches[0];? ? this.selectedId =?'';? ??if?(!touchInfo) {? ? ??return;? ? }? ? const windowX = Math.ceil(touchInfo.windowX); // 獲取用戶手指x坐標? ? const context = this.getUIContext();? ? // 檢測用戶手指x坐標可以命中的表情? ? this.commentIcons.forEach(iconInfo => {? ? ??if?(event.type === TouchType.Up) {? ? ? ??return;? ? ? }? ? ? const compInfo = componentUtils.getRectangleById(iconInfo.id);? ? ? const x = Math.ceil(context.px2vp(compInfo.windowOffset.x));? ? ? const width = Math.ceil(context.px2vp(compInfo.size.width));? ? ??if?(windowX >= x && windowX <= x + width) {? ? ? ? this.selectedId = iconInfo.id; // 范圍中有表情被選中? ? ? }? ? });? ? this.commentIcons = [...this.commentIcons]; // 低成本刷新數組(淺拷貝)? }??build() {? ??Column() {? ? ??Row() {? ? ? ??Row() {? ? ? ? ? ForEach(this.commentIcons, (item: CommentIconInfo) => {? ? ? ? ? ? Image(item.source)? ? ? ? ? ? ? .id(item.id)? ? ? ? ? ? ? .width(this.selectedId === item.id ? this.selectedSize : this.normalSize)? ? ? ? ? ? ? .height(this.selectedId === item.id ? this.selectedSize : this.normalSize)? ? ? ? ? ? ? .padding({ left: this.iconMarginLeft })? ? ? ? ? ? ? .animation({ duration: this.animDuration, curve: curves.springMotion() })? ? ? ? ? ? ? .visibility(this.showCommentIconPanel ? Visibility.Visible : Visibility.None)? ? ? ? ? }, (item: CommentIconInfo) => {? ? ? ? ? ??return?`${item.id}` // 復寫id生成器? ? ? ? ? })? ? ? ? }? ? ? ? .visibility(this.showCommentIconPanel ? Visibility.Visible : Visibility.None)? ? ? ? .backgroundColor(Color.Pink)? ? ? ? .width(this.showCommentIconPanel ? this.getRowWidth() : 10)? ? ? ? .borderRadius(this.selectedId ? 40 : 20)? ? ? ? .padding({ left: this.paddingHoriz, right: this.paddingHoriz })? ? ? ? .animation({ duration: this.animDuration, curve: curves.springMotion() })? ? ? ? SymbolGlyph($r('sys.symbol.ellipsis_message_fill'))? ? ? ? ? .fontSize(60)? ? ? ? ? .renderingStrategy(SymbolRenderingStrategy.MULTIPLE_COLOR)? ? ? ? ? .fontColor([Color.Green])? ? ? ? ? .margin({ left: 10 })? ? ? ? ? .onTouch(this.onTouchEvent)? ? ? }? ? ? .justifyContent(FlexAlign.End)? ? ? .width('100%')? ? }? ? .padding(20)? ? .height('100%')? }? private?getRowWidth() { // 防止抖動? ? const baseWidth =? ? ? this.paddingHoriz + this.paddingHoriz + this.commentIcons.length * this.normalSize +? ? ? ? (this.commentIcons.length - 1) * this.iconMarginLeft;? ??if?(this.selectedId) {? ? ??return?baseWidth + this.selectedSize - this.normalSize;? ? }? ??return?baseWidth;? }}

項目源代碼地址:

https://gitee.com/lantingshuxu/harmony-class-room-demos/tree/feat%2FmagicComment/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/65908.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65908.shtml
英文地址,請注明出處:http://en.pswp.cn/web/65908.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Node.js——http 模塊(二)

個人簡介 &#x1f440;個人主頁&#xff1a; 前端雜貨鋪 &#x1f64b;?♂?學習方向&#xff1a; 主攻前端方向&#xff0c;正逐漸往全干發展 &#x1f4c3;個人狀態&#xff1a; 研發工程師&#xff0c;現效力于中國工業軟件事業 &#x1f680;人生格言&#xff1a; 積跬步…

研華 PCI-1751 驅動更新導LabVIEW致程序異常

問題描述&#xff1a; 某 LabVIEW 程序長期運行正常&#xff0c;但在使用研華 PCI-1751 數據采集卡運行一段時間后&#xff0c;程序開始出現不正常的行為。具體過程如下&#xff1a; 初始問題&#xff1a; 更換新的 PCI-1751 板卡后&#xff0c;驅動程序被更新&#xff0c;但程…

接上篇基于Alertmanager 配置釘釘告警

Alertmanager 是一個用于處理和管理 Prometheus 警報的開源工具。它負責接收來自 Prometheus 服務器的警報&#xff0c;進行去重、分組、靜默、抑制等操作&#xff0c;并通過電子郵件、PagerDuty、Slack 等多種渠道發送通知。 主要功能 去重&#xff1a;合并相同或相似的警報&…

網絡原理(三)—— 傳輸層 之 UDP 和 TCP協議

傳輸層 在傳輸層兩大關鍵的協議就是UDP和TCP協議了&#xff0c;除此之外&#xff0c;還有別的傳輸層協議&#xff0c;本文章將介紹UDP和TCP協議&#xff0c;重點介紹TCP協議。 首先回顧TCP和UDP 的特點&#xff1a; UDP&#xff1a;不可靠傳輸&#xff0c;面向數據包&#xf…

針對服務器磁盤爆滿,MySql數據庫始終無法啟動,怎么解決

&#xff08;點擊即可進入聊天助手&#xff09; 很多站長在運營網站的過程當中都會遇到一個問題,就是網站突然無法打開,數據一直無法啟動 無論是強制重啟還是,刪除網站內的所有應用,數據庫一直無法啟動 這個時候,就需要常見的運維手段了,需要對服務器后臺各個資源,進行逐一排查…

高性能現代PHP全棧框架 Spiral

概述 Spiral Framework 誕生于現實世界的軟件開發項目是一個現代 PHP 框架&#xff0c;旨在為更快、更清潔、更卓越的軟件開發提供動力。 特性 高性能 由于其設計以及復雜精密的應用服務器&#xff0c;Spiral Framework框架在不影響代碼質量以及與常用庫的兼容性的情況下&a…

【面試題】Spring/SpringBoot部分[2025/1/6 ~ 2025/1/12]

Spring/SpringBoot部分[2025/1/6 ~ 2025/1/12] 1. 說說 Spring 啟動過程&#xff1f;2. 說說 Springboot 的啟動流程&#xff1f;3. 你了解的 Spring 都用到哪些設計模式&#xff1f;4. Spring 有哪幾種事務傳播行為?5. SpringBoot 是如何實現自動配置的&#xff1f;6. Spring…

【機器學習:十八、更高級的神經網絡概念】

1. 梯度下降法的改進&#xff1a;Adam算法 1.1 Adam算法簡介 Adam&#xff08;Adaptive Moment Estimation&#xff09;是一種優化算法&#xff0c;結合了動量梯度下降和 RMSProp 的優點&#xff0c;在處理稀疏梯度和高維空間優化時表現尤為出色。其核心在于動態調整每個參數…

計算機網絡之---VPN與隧道協議

VPN與隧道協議 VPN&#xff08;虛擬專用網絡&#xff09;和隧道協議是現代網絡安全技術的重要組成部分&#xff0c;它們主要用于在不安全的公共網絡&#xff08;如互聯網&#xff09;上建立一個安全的私密網絡連接。VPN通過加密通信和認證機制&#xff0c;確保數據的隱私性和完…

【STM32-學習筆記-6-】DMA

文章目錄 DMAⅠ、DMA框圖Ⅱ、DMA基本結構Ⅲ、不同外設的DMA請求Ⅳ、DMA函數Ⅴ、DMA_InitTypeDef結構體參數①、DMA_PeripheralBaseAddr②、DMA_PeripheralDataSize③、DMA_PeripheralInc④、DMA_MemoryBaseAddr⑤、DMA_MemoryDataSize⑥、DMA_MemoryInc⑦、DMA_DIR⑧、DMA_Buff…

SQL Server中可以通過擴展事件來自動抓取阻塞

在SQL Server中可以通過擴展事件來自動抓取阻塞&#xff0c;以下是詳細流程&#xff1a; 開啟阻塞跟蹤配置&#xff1a; ? 執行以下SQL語句來啟用相關配置&#xff1a; EXEC sp_configureshow advanced options, 1; RECONFIGURE; EXEC sp_configure blocked process thresh…

DNS解析域名簡記

域名通常是由: 權威域名.頂級域名.根域名組成的。 從左往右&#xff0c;級別依次升高&#xff0c;這和外國人從小范圍到大范圍的說話習慣相關。&#xff08;我們自己是更習慣先說大范圍再說小范圍&#xff0c;如XX省XX市XX區XX路&#xff09; DNS解析域名時&#xff0c;會先查…

【爬蟲】單個網站鏈接爬取文獻數據:標題、摘要、作者等信息

源碼鏈接&#xff1a; https://github.com/Niceeggplant/Single—Site-Crawler.git 一、項目概述 從指定網頁中提取文章關鍵信息的工具。通過輸入文章的 URL&#xff0c;程序將自動抓取網頁內容 二、技術選型與原理 requests 庫&#xff1a;這是 Python 中用于發送 HTTP 請求…

關于掃描模型 拓撲 和 傳遞貼圖工作流筆記

關于MAYA拓撲和傳遞貼圖的操作筆記 一、拓撲低模: 1、拓撲工作區位置: 1、準備出 目標 高模。 (高模的狀態如上 ↑ )。 2、打開頂點吸附,和建模工具區,選擇四邊形繪制. 2、拓撲快捷鍵使…

解決無法遠程管理Windows Server服務器核心安裝

問題 有時&#xff0c;人們會為了節省運算資源&#xff0c;例如運行Hyper-V虛擬機&#xff0c;而選擇Windows Server核心安裝&#xff0c;即無圖形化界面。這時&#xff0c;我們就只能通過Powershell命令對其進行操控&#xff0c;或為了獲得圖形化界面而使用遠程服務器管理工具…

SQL HAVING 子句深入解析

SQL HAVING 子句深入解析 介紹 SQL&#xff08;Structured Query Language&#xff09;是一種用于管理關系數據庫管理系統的標準編程語言。在SQL中&#xff0c;HAVING子句是與GROUP BY子句一起使用的&#xff0c;用于篩選分組后的數據。它根據聚合函數的結果對組進行條件過濾…

【計算機網絡】lab7 TCP協議

&#x1f308; 個人主頁&#xff1a;十二月的貓-CSDN博客 &#x1f525; 系列專欄&#xff1a; &#x1f3c0;計算機網絡_十二月的貓的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻擋不了春天的腳步&#xff0c;十二點的黑夜遮蔽不住黎明的曙光 目錄 1. 實驗目的…

JavaRestClient 客戶端初始化+索引庫操作

1. 介紹 ES官方提供了各種不同語言的客戶端&#xff0c;用來操作ES。這些客戶端的本質就是組裝DSL語句&#xff0c;通過http請求發送給ES。 Elasticsearch目前最新版本是8.0&#xff0c;其java客戶端有很大變化。不過大多數企業使用的還是8以下版本 2. 客戶端初始化 在elastic…

【JVM-2.2】使用JConsole監控和管理Java應用程序:從入門到精通

在Java應用程序的開發和運維過程中&#xff0c;監控和管理應用程序的性能和資源使用情況是非常重要的。JConsole是Java Development Kit&#xff08;JDK&#xff09;自帶的一款圖形化監控工具&#xff0c;它可以幫助開發者實時監控Java應用程序的內存、線程、類加載以及垃圾回收…

基于html5實現音樂錄音播放動畫源碼

源碼介紹 基于html5實現音樂錄音播放動畫源碼是一款類似Shazam的UI&#xff0c;點擊按鈕后&#xff0c;會變成為一個監聽按鈕。旁邊會有音符飛入這個監聽按鈕&#xff0c;最后轉換成一個音樂播放器。 效果預覽 源碼獲取 基于html5實現音樂錄音播放動畫源碼