在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。
1. 基础点赞功能实现
1.1 创建数据模型
// VideoModel.ets
export class VideoModel {id: string = "";title: string = "";author: string = "";coverUrl: string = "";videoUrl: string = "";likes: number = 0;isLiked: boolean = false;comments: number = 0;shares: number = 0;
}
1.2 点赞按钮组件
// LikeButton.ets
@Component
export struct LikeButton {@Prop video: VideoModel;@State private isAnimating: boolean = false;build() {Column() {// 点赞按钮Image(this.video.isLiked ? $r('app.media.ic_liked') : $r('app.media.ic_like')).width(40).height(40).scale({ x: this.isAnimating ? 1.2 : 1, y: this.isAnimating ? 1.2 : 1 }).animation({ duration: 200, curve: Curve.EaseInOut }).onClick(() => {this.handleLike();})// 点赞数Text(this.video.likes.toString()).fontSize(14).fontColor(Color.White).margin({ top: 4 })}.alignItems(HorizontalAlign.Center)}private handleLike() {// 触发动画this.isAnimating = true;setTimeout(() => {this.isAnimating = false;}, 200);// 更新点赞状态this.video.isLiked = !this.video.isLiked;this.video.likes += this.video.isLiked ? 1 : -1;// 这里可以添加网络请求,同步点赞状态到服务器this.syncLikeStatus();}private syncLikeStatus() {// 实际项目中这里应该调用API同步点赞状态console.log(`视频${this.video.id}点赞状态: ${this.video.isLiked}`);}
}
2. 双击点赞功能实现
2.1 视频组件封装
// VideoPlayer.ets
@Component
export struct VideoPlayer {@Prop video: VideoModel;@State private showLikeAnimation: boolean = false;private lastTapTime: number = 0;build() {Stack() {// 视频播放器Video({src: this.video.videoUrl,controller: new VideoController()}).width('100%').height('100%').objectFit(ImageFit.Cover).onTouch((event: TouchEvent) => {if (event.type === TouchType.Down) {this.handleTap();}})// 点赞动画if (this.showLikeAnimation) {Image($r('app.media.ic_liked')).width(80).height(80).position({ x: '50%', y: '50%' }).scale({ x: 0, y: 0 }).opacity(1).animation({duration: 1000,curve: Curve.EaseOut,onFinish: () => {this.showLikeAnimation = false;}}).scale({ x: 1.5, y: 1.5 }).opacity(0)}// 右侧互动栏Column() {LikeButton({ video: this.video }).margin({ bottom: 20 })// 其他互动按钮(评论、分享等)...}.position({ x: '85%', y: '50%' })}.width('100%').height('100%')}private handleTap() {const currentTime = new Date().getTime();const timeDiff = currentTime - this.lastTapTime;if (timeDiff < 300) { // 双击判定if (!this.video.isLiked) {this.video.isLiked = true;this.video.likes += 1;this.showLikeAnimation = true;this.syncLikeStatus();}}this.lastTapTime = currentTime;}private syncLikeStatus() {// 同步点赞状态到服务器console.log(`视频${this.video.id}通过双击点赞`);}
}
3. 点赞动画优化
3.1 粒子爆炸效果
// ParticleLikeAnimation.ets
@Component
export struct ParticleLikeAnimation {@State private particles: { id: number, x: number, y: number, scale: number, opacity: number }[] = [];private particleCount: number = 12;build() {Stack() {ForEach(this.particles, (particle) => {Image($r('app.media.ic_liked')).width(20).height(20).position({ x: `${particle.x}%`, y: `${particle.y}%` }).scale({ x: particle.scale, y: particle.scale }).opacity(particle.opacity)})}}public startAnimation() {this.particles = [];// 生成粒子for (let i = 0; i < this.particleCount; i++) {this.particles.push({id: i,x: 50,y: 50,scale: 0,opacity: 1});}// 动画效果this.particles.forEach((particle, index) => {const angle = (index * (360 / this.particleCount)) * (Math.PI / 180);const distance = 15 + Math.random() * 10;setTimeout(() => {particle.x = 50 + Math.cos(angle) * distance;particle.y = 50 + Math.sin(angle) * distance;particle.scale = 0.5 + Math.random() * 0.5;particle.opacity = 0;}, index * 50);});setTimeout(() => {this.particles = [];}, 1000);}
}
3.2 在VideoPlayer中使用粒子动画
// 修改VideoPlayer.ets
@Component
export struct VideoPlayer {@State private particleAnimRef: ParticleLikeAnimation | null = null;build() {Stack() {// ...其他组件// 替换原来的点赞动画ParticleLikeAnimation({ ref: this.particleAnimRef })// ...其他组件}}private handleTap() {const currentTime = new Date().getTime();const timeDiff = currentTime - this.lastTapTime;if (timeDiff < 300) { // 双击判定if (!this.video.isLiked) {this.video.isLiked = true;this.video.likes += 1;this.particleAnimRef?.startAnimation();this.syncLikeStatus();}}this.lastTapTime = currentTime;}
}
4. 数据持久化与同步
4.1 使用分布式数据服务
// LikeService.ets
import distributedData from '@ohos.data.distributedData';export class LikeService {private kvManager: distributedData.KVManager;private kvStore: distributedData.KVStore;async init() {const config = {bundleName: 'com.example.douyin',userInfo: {userId: 'currentUser',userType: distributedData.UserType.SAME_USER_ID}};this.kvManager = distributedData.createKVManager(config);const options = {createIfMissing: true,encrypt: false,backup: false,autoSync: true,kvStoreType: distributedData.KVStoreType.SINGLE_VERSION};this.kvStore = await this.kvManager.getKVStore('likes_store', options);}async syncLike(videoId: string, isLiked: boolean) {if (!this.kvStore) {await this.init();}try {await this.kvStore.put(videoId, isLiked);await this.kvStore.sync({deviceIds: ['all'],mode: distributedData.SyncMode.PUSH});} catch (error) {console.error('同步点赞状态失败:', JSON.stringify(error));}}async getLikeStatus(videoId: string): Promise<boolean> {if (!this.kvStore) {await this.init();}try {const result = await this.kvStore.get(videoId);return !!result;} catch (error) {console.error('获取点赞状态失败:', JSON.stringify(error));return false;}}
}
4.2 在LikeButton中集成数据服务
// 修改LikeButton.ets
@Component
export struct LikeButton {private likeService: LikeService = new LikeService();aboutToAppear() {this.loadLikeStatus();}private async loadLikeStatus() {const isLiked = await this.likeService.getLikeStatus(this.video.id);this.video.isLiked = isLiked;}private async syncLikeStatus() {await this.likeService.syncLike(this.video.id, this.video.isLiked);}
}
5. 完整视频页面实现
// DouyinPage.ets
@Entry
@Component
struct DouyinPage {@State currentVideoIndex: number = 0;@State videos: VideoModel[] = [{id: "1",title: "第一个视频",author: "创作者1",coverUrl: "resources/cover1.jpg",videoUrl: "resources/video1.mp4",likes: 1234,isLiked: false,comments: 56,shares: 12},// 更多视频...];build() {Stack() {// 视频滑动容器Swiper() {ForEach(this.videos, (video: VideoModel) => {SwiperItem() {VideoPlayer({ video: video })}})}.index(this.currentVideoIndex).autoPlay(false).indicator(false).loop(false).vertical(true).edgeEffect(EdgeEffect.Spring).onChange((index: number) => {this.currentVideoIndex = index;})// 顶部导航Row() {Text("推荐").fontSize(18).fontColor(Color.White).margin({ left: 20 })// 其他导航项...}.width('100%').height(50).position({ x: 0, y: 30 })}.width('100%').height('100%').backgroundColor(Color.Black)}
}
6. 实际项目注意事项
-
性能优化:
- 使用LazyForEach加载视频列表
- 视频预加载机制
- 动画使用硬件加速
-
网络请求:
- 实现点赞API接口
- 添加请求重试机制
- 处理网络异常情况
-
用户体验:
- 添加加载状态指示器
- 实现点赞操作的防抖处理
- 离线状态下的点赞处理
-
安全考虑:
- 点赞操作的身份验证
- 防止重复点赞
- 数据加密传输
-
测试要点:
- 双击手势的灵敏度测试
- 动画性能测试
- 多设备同步测试
-
扩展功能:
- 点赞列表查看
- 点赞通知功能
- 热门点赞视频推荐
通过以上实现,你可以在HarmonyOS 5应用中创建类似抖音的点赞功能,包括基础点赞、双击点赞、动画效果和数据同步等核心功能。
相关文章:
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...