在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

news2025/6/13 1:42:12

下面我将详细介绍如何使用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. 实际项目注意事项

  1. ​性能优化​​:

    • 使用LazyForEach加载视频列表
    • 视频预加载机制
    • 动画使用硬件加速
  2. ​网络请求​​:

    • 实现点赞API接口
    • 添加请求重试机制
    • 处理网络异常情况
  3. ​用户体验​​:

    • 添加加载状态指示器
    • 实现点赞操作的防抖处理
    • 离线状态下的点赞处理
  4. ​安全考虑​​:

    • 点赞操作的身份验证
    • 防止重复点赞
    • 数据加密传输
  5. ​测试要点​​:

    • 双击手势的灵敏度测试
    • 动画性能测试
    • 多设备同步测试
  6. ​扩展功能​​:

    • 点赞列表查看
    • 点赞通知功能
    • 热门点赞视频推荐

通过以上实现,你可以在HarmonyOS 5应用中创建类似抖音的点赞功能,包括基础点赞、双击点赞、动画效果和数据同步等核心功能。

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

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

相关文章

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

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

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…

阿里云ACP云计算备考笔记 (5)——弹性伸缩

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

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

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

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…

边缘计算医疗风险自查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 适用场…

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…

Python:操作 Excel 折叠

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

MongoDB学习和应用(高效的非关系型数据库)

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

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…