当前位置: 首页 > news >正文

【Harmony】轮播图特效,持续更新中。。。。

效果预览
swiper官网例子
请添加图片描述
Swiper 高度可变化
请添加图片描述
两边等长露出,跟随手指滑动
请添加图片描述
Swiper 指示器导航点位于 Swiper 下方
请添加图片描述
卡片楼层层叠一
在这里插入图片描述

一、官网 例子

请添加图片描述
参考代码:

// xxx.ets
class MyDataSource implements IDataSource {private list: number[] = []constructor(list: number[]) {this.list = list}totalCount(): number {return this.list.length}getData(index: number): number {return this.list[index]}registerDataChangeListener(listener: DataChangeListener): void {}unregisterDataChangeListener() {}
}@Entry
@Component
struct SwiperExample {private swiperController: SwiperController = new SwiperController()private data: MyDataSource = new MyDataSource([])aboutToAppear(): void {let list: number[] = []for (let i = 1; i <= 10; i++) {list.push(i);}this.data = new MyDataSource(list)}build() {Column({ space: 5 }) {Swiper(this.swiperController) {LazyForEach(this.data, (item: string) => {Text(item.toString()).width('90%').height(160).backgroundColor(0xAFEEEE).textAlign(TextAlign.Center).fontSize(30)}, (item: string) => item)}.cachedCount(2).index(1).autoPlay(true).interval(4000).indicator(Indicator.digit() // 设置数字导航点样式.right("43%").top(200).fontColor(Color.Gray).selectedFontColor(Color.Gray).digitFont({ size: 20, weight: FontWeight.Bold }).selectedDigitFont({ size: 20, weight: FontWeight.Normal })).loop(true).duration(1000).itemSpace(0).displayArrow(true, false)Row({ space: 12 }) {Button('showNext').onClick(() => {this.swiperController.showNext()})Button('showPrevious').onClick(() => {this.swiperController.showPrevious()})}.margin(5)}.width('100%').margin({ top: 5 })}
}

二、Swiper 高度可变化

请添加图片描述
主要逻辑代码:

// TODO: 知识点: Swiper组件绑定onGestureSwipe事件,在页面跟手滑动过程中,逐帧触发该回调// 性能知识点: onGestureSwipe属于频繁回调,不建议在onGestureSwipe做耗时和冗余操作.onGestureSwipe((index:number,extraInfo:SwiperAnimationEvent)=>{animateTo({duration: Constants.DURATION_SWIPER,curve: Curve.EaseOut,playMode: PlayMode.Normal,onFinish: () => {// logger.info('play end');}}, () => { // 通过左右滑动的距离来计算对应的上下位置的变化if (index === 0 && extraInfo.currentOffset < 0) {this.swiperDistance = extraInfo.currentOffset / Constants.SCROLL_WIDTH * Constants.SMALL_FONT_SIZE;} else if (index === 1 && extraInfo.currentOffset > 0) {this.swiperDistance = extraInfo.currentOffset / Constants.SCROLL_WIDTH * Constants.SMALL_FONT_SIZE - Constants.SMALL_FONT_SIZE;} else if (index === 2 && extraInfo.currentOffset < 0) {this.swiperDistance = extraInfo.currentOffset / Constants.SCROLL_WIDTH * Constants.GRID_SINGLE_HEIGHT - Constants.SMALL_FONT_SIZE;} else if (index === 3 && extraInfo.currentOffset > 0) {this.swiperDistance = extraInfo.currentOffset / Constants.SCROLL_WIDTH * Constants.GRID_SINGLE_HEIGHT - Constants.SMALL_FONT_SIZE - Constants.GRID_SINGLE_HEIGHT;}})}).onAnimationStart((_: number, targetIndex: number)=>{animateTo({duration: Constants.DURATION_DOWN_PAGE,curve: Curve.EaseOut,playMode: PlayMode.Normal,onFinish: () => {// logger.info('play end');}}, () => {if (targetIndex === 0) {this.swiperDistance = 0;} else if (targetIndex === 1 || targetIndex === 2) {this.swiperDistance = -Constants.SMALL_FONT_SIZE;} else {this.swiperDistance = -Constants.SMALL_FONT_SIZE - Constants.GRID_SINGLE_HEIGHT;}})}).indicator(new DotIndicator()// .selectedItemWidth($r('app.float.swipersmoothvariation_select_item_width')).selectedItemWidth('18fp')// .selectedItemHeight($r('app.float.swipersmoothvariation_select_item_height')).selectedItemHeight('3vp')// .itemWidth($r('app.float.swipersmoothvariation_default_item_width')).itemWidth('5vp')// .itemHeight($r('app.float.swipersmoothvariation_default_item_height')).itemHeight('-3vp')// .selectedColor($r('app.color.swipersmoothvariation_swiper_selected_color')).selectedColor(Color.Yellow)// .color($r('app.color.swipersmoothvariation_swiper_unselected_color'))).color('#FFFF8662'))

逻辑结构相对复杂,请查看下面 demo 开源地址

三、Swiper 指示器导航点位于 Swiper 下方

请添加图片描述
主要是分离内容区域和空白区域给指示器留白蛤

  Column() {Swiper(this.swiperController){// TODO 高性能知识点:此处为了演示场景,列表数量只有3个,使用ForEach,列表数量较多的场景,推荐使用LazyForEach+组件复用+缓存列表项实现ForEach(this.swiperData,(item:Resource)=>{Column(){// TODO 知识点:将swiper区域分割成内容区和空白区Image(item).width('100%').height('22%').borderRadius(10)Column().width('100%').height(50).backgroundColor(Color.Gray)}})}.width('95%').loop(true).autoPlay(true)// TODO 知识点:通过indicator属性,将导航点放置到空白区域,实现指示器导航点位于swiper下方的效果.indicator(new DotIndicator().bottom(15))}.height('100%').width('100%').justifyContent(FlexAlign.Center)

四、Swiper组件实现容器视图居中完全展示,两边等长露出,跟随手指滑动

请添加图片描述
逻辑简约描述:
难点在于偏移的计算
要特别注意宽度和高度值设置,保持统一单位。
在实际的开发过程中,因为单位的马虎导致即使代码是一样的,也出现过多次错位等问题
建议先完整参考代码写一遍之后再按实际需求进行偏移算法修改
比较烧脑,准备两罐红牛缓解疲劳蛤!

偏移计算:

/*** 计算卡片偏移量,并维护偏移量列表。* @param targetIndex { number } swiper target card's index.*/calculateOffset(target: number) {let left = target - 1;let right = target + 1;// 计算上一张卡片的偏移值if (this.isIndexValid(left)) {this.cardsOffset[left] = this.getMaxOffset(left);}// 计算当前卡片的偏移值if (this.isIndexValid(target)) {this.cardsOffset[target] = this.getMaxOffset(target) / 2;}// 下一张片的偏移值if (this.isIndexValid(right)) {this.cardsOffset[right] = 0;}}

滑动触发偏移计算:

.onChange((index) => {// logger.info(TAG, `Target index: ${index}`);this.calculateOffset(index);}).onGestureSwipe((index, event) => {const currentOffset = event.currentOffset;// 获取当前卡片(居中)的原始偏移量const maxOffset = this.getMaxOffset(index) / 2;// 实时维护卡片的偏移量列表,做到跟手效果if (currentOffset < 0) {// 向左偏移/** 此处计算原理为:按照比例设置卡片的偏移量。* 当前卡片居中,向左滑动后将在左边,此时卡片偏移量即为 maxOffset * 2(因为向右对齐)。* 所以手指能够滑动的最大距离(this.displayWidth)所带来的偏移量即为 maxOffset。* 易得公式:卡片实时偏移量 = (手指滑动长度 / 屏幕宽度) * 卡片最大可偏移量 + 当前偏移量。* 之后的计算原理相同,将不再赘述。*/this.cardsOffset[index] = (-currentOffset / this.displayWidth) * maxOffset + maxOffset;if (this.isIndexValid(index + 1)) {// 下一个卡片的偏移量const maxOffset = this.getMaxOffset(index + 1) / 2;this.cardsOffset[index + 1] = (-currentOffset / this.displayWidth) * maxOffset;}if (this.isIndexValid(index - 1)) {// 上一个卡片的偏移量const maxOffset = this.getMaxOffset(index - 1) / 2;this.cardsOffset[index - 1] = (currentOffset / this.displayWidth) * maxOffset + 2 * maxOffset;}} else if (currentOffset > 0) {// 向右滑动this.cardsOffset[index] = maxOffset - (currentOffset / this.displayWidth) * maxOffset;if (this.isIndexValid(index + 1)) {const maxOffset = this.getMaxOffset(index + 1) / 2;this.cardsOffset[index + 1] = (currentOffset / this.displayWidth) * maxOffset;}if (this.isIndexValid(index - 1)) {const maxOffset = this.getMaxOffset(index - 1) / 2;this.cardsOffset[index - 1] = 2 * maxOffset - (currentOffset / this.displayWidth) * maxOffset;}}}).onAnimationStart((index, targetIndex) => {this.calculateOffset(targetIndex);})

五、收尾两侧都有等长偏移的露出

六、卡片叠加楼层效果一:初步实现楼层效果,交互流程度和丝滑待改善

在这里插入图片描述

build() {Column() {Stack() {ForEach(this.data, (item: Resource, index) => {Stack({ alignContent: Alignment.Start }){Image(item).width('80%').height('100%').alignSelf(ItemAlign.Center)}.width('100%').offset({ x: this.calculateOffset(index), y: 0 }).zIndex(index !== this.currentIndex && this.getImgOffset(index) === 0 ? 0 : 2 - Math.abs(this.getImgOffset(index))).height(index === this.currentIndex ? 202 : ((index === this.currentIndex - 1 || index === this.currentIndex + 1) ? 192 : 182)).translate({ x: this.translateList[index] }).gesture(PanGesture({ direction: PanDirection.Horizontal}).onActionStart((event: GestureEvent) => {if (event.offsetX < 0) {if(this.currentIndex === index && this.currentIndex != this.data.length -1){this.setAnim(true)}}else {if (this.currentIndex === index && this.currentIndex != 0) {this.setAnim(false)}}})).onClick(() => {})})}.height('50%').width('100%').alignContent(Alignment.Center).clip(true) //裁剪超出 banner 左侧的层叠部分.backgroundColor(Color.Gray).padding({left: 10,right: 10,top: 16,bottom: 16})}.height('100%').width('100%').justifyContent(FlexAlign.Start)}/*** 计算偏移量* @param index:索引值* @returns*/calculateOffset(index: number): number {const offsetIndex: number = this.getImgOffset(index);const tempOffset: number = Math.abs(offsetIndex);let offsetX: number = 0;if (tempOffset === 1) {// 根据图片层级系数来决定左右偏移量offsetX = - 8 * offsetIndex;}if (tempOffset === 2) {// 根据图片层级系数来决定左右偏移量offsetX = -this.offsetXValue * offsetIndex;}return offsetX;}/*** 获取图片系数* @param index:索引值* @returns*/getImgOffset(index: number): number {const coefficient: number = this.currentIndex - index; // 计算图片左右位置const tempCoefficient: number = Math.abs(coefficient);if (tempCoefficient <= this.halfCount) {return coefficient;}const dataLength: number = this.data.length;let tempOffset: number = dataLength - tempCoefficient; // 判断图片位于左右层级位置if (tempOffset <= this.halfCount) { //如果在左侧if (coefficient > 0) {return -tempOffset;}return tempOffset;}return 0;}/*** 设置动画* @param duration:动画持续时间**/setAnim(isLeft:boolean){let dataLength: number = this.data.length;let tempIndex: number = 0animateTo({ duration: 1000}, () => {if (isLeft) {this.translateList[this.currentIndex] = -350tempIndex = this.currentIndex + 1this.currentIndex = tempIndex % dataLength} else {tempIndex = this.currentIndex - 1 + dataLengththis.currentIndex = tempIndex % dataLengththis.translateList[this.currentIndex] = 0}})}

~~~~~~~~~~~持续更新中

开源 Demo 工程地址

Demo 工程

相关文章:

【Harmony】轮播图特效,持续更新中。。。。

效果预览 swiper官网例子 Swiper 高度可变化 两边等长露出&#xff0c;跟随手指滑动 Swiper 指示器导航点位于 Swiper 下方 卡片楼层层叠一 一、官网 例子 参考代码&#xff1a; // xxx.ets class MyDataSource implements IDataSource {private list: number[] []cons…...

Go 并发模式:管道的妙用

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在编写程序时,我们通常不会一口气写出一个冗长的函数。相反,我们通过构建函数、结构体和方法等抽象来简化代码。这不仅有助于隐藏不重要的细节,还使我们能够专注于某一部分代码,而不必担心影响其他部分。然而…...

CAN通信详解

1、CAN介绍 1.1、什么是CAN? CAN&#xff08;Controller Area Network&#xff09; 即控制器局域网&#xff0c;是ISO国际标准化的串行通信协议。 开发目的&#xff1a;为了满足汽车产业的“减少线束的数量”、“通过多个LAN&#xff0c;进行大量数据的高速通信”…...

52 文本预处理_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录一、理论部分二、代码读取数据集词元化词表整合所有功能小结练习 一、理论部分 对于序列数据处理问题&#xff0c;我们在序列处理中评估了所需的统计工具和预测时面临的挑战。 …...

【python】字符串扩展-格式化的精度控制

字符串扩展 字符串的三种定义方式字符串拼接字符串格式化格式化的精度控制字符串格式化方式2对表达式进行格式化 学习目标 掌握格式化字符串的过程中做数字的精度控制 字符串格式化 name "小明" set_up_year 2006 stock_price 19.99 message "我是&…...

C++第一次练习

题目1 class Solution { public:bool isletter(char s){if(s<z&&s>a)return true;if(s>A&&s<Z)return true;return false;}string reverseOnlyLetters(string s) {if(s.empty()){return s;}int left,right;left0;rights.size()-1;while(left<ri…...

计算机毕业设计 基于Python的医疗预约与诊断系统 Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…...

JAVA基础:正则表达式,String的intern方法,StringBuilder可变字符串特点与应用,+连接字符串特点

1 String中的常用方法2 1.1 split方法 将字符串按照指定的内容进行分割&#xff0c;将分割成的每一个子部分组成一个数组 分割内容不会出现在数组中 实际上该方法不是按照指定的简单的符号进行分割的&#xff0c;而是按照正则表达式进行分割 1.2 正则表达式 用简单的符号组合…...

前端接口报错302 [已解决]

前端接口报错302 [已解决] 在前端开发中&#xff0c;与后端接口的交互是项目成功的关键。然而&#xff0c;遇到如302这样的状态码报错时&#xff0c;可能会让开发者感到困惑。本文将通过详细解析和多个代码案例&#xff0c;帮助你深入理解前端接口报错302&#xff0c;并提供有效…...

【网络安全】利用未授权API接口实现创建Support Ticket

未经许可,不得转载。 文章目录 正文目标为一个技术平台,客户可以通过该平台预订不同类型的服务。 正文 redacted.com 是主域,但所有流量都通过 api.redacted.com。我过去曾使用该公司预订了一些服务,因此我的帐户中有预订历史。 我对我的订单开具了 Support Ticket,此时…...

气压高度加误差的两种方法(直接添加 vs 换算到气压误差),附MATLAB程序

在已知高度真实值时,如果需要计算此高度下的气压计误差,可考虑本文所述的两种方法 气压高度 气压与高度之间的关系可以用大气压的垂直变化来描述。随着高度的增加,气压通常会下降。这是因为空气的密度在高度增加时减少,导致上方空气柱对下方空气施加的压力减小。 主要关系…...

Word 制作会议名牌教程

文章目录 Part.I IntroductionPart.II 制作步骤 Part.I Introduction 本文详细介绍了如何用 Word 制作会议名牌&#xff0c;附有笔者制作好的一个成品&#xff08;戳我下载~&#xff09;。 下面是一些常识 会议名牌尺寸&#xff1a;100mm 180mm Part.II 制作步骤 1、新建文…...

浮动静态路由

浮动静态路由 首先我们知道静态路由的默认优先级是60&#xff0c;然后手动添加一条静态路由优先级为80的路由作为备份路由。当主路由失效的备份路由就会启动。 一、拓扑图 二、基本配置 1.R1: <Huawei>system-view [Huawei]sysname R1 [R1]interface GigabitEthernet…...

JavaWeb初阶 day1

目录 tomcat目录结构 tomcat:web服务器软件 项目部署的方式 直接将项目放到webapps下 配置conf/server.xml文件 在conf\Catalina\localhost创建任意名称的xml文件。在文件中编写 静态项目和动态项目 Servlet Servlet执行原理 Servlet方法&#xff08;生命周期&#x…...

OpenAPI鉴权(二)jwt鉴权

一、思路 前端调用后端可以使用jwt鉴权&#xff1b;调用三方接口也可以使用jwt鉴权。对接多个三方则与每个third parth都约定一套token规则&#xff0c;因为如果使用同一套token&#xff0c;token串用可能造成权限越界问题&#xff0c;且payload交叉业务不够清晰。下面的demo包…...

【Rust练习】16.模式

文章题目来自&#xff1a;https://practice-zh.course.rs/pattern-match/patterns.html 1 &#x1f31f;&#x1f31f; 使用 | 可以匹配多个值, 而使用 … 可以匹配一个闭区间的数值序列 fn main() {} fn match_number(n: i32) {match n {// 匹配一个单独的值1 > println!(…...

深度学习(4):torch.nn.Module

文章目录 一、是什么二、nn.Module 的核心功能三、nn.Module 的基本用法1. 定义自定义模型2. 初始化模型3. 模型的使用 四、nn.Module 的关键特性1. 自动注册子模块和参数2. forward 方法3. 不需要定义反向传播 五、常用的内置模块六、示例&#xff1a;创建一个简单的神经网络1…...

(14)关于docker如何通过防火墙做策略限制

关于docker如何通过防火墙做策略限制 1、iptables相关问题 在Iptables防火墙中包含四种常见的表&#xff0c;分别是filter、nat、mangle、raw。 filter&#xff1a;负责过滤数据包。 filter表可以管理INPUT、OUTPUT、FORWARD链。 nat&#xff1a;用于网络地址转换。 nat表…...

新React开发人员应该如何思考

React是一个用于构建用户界面的流行JavaScript库&#xff0c;通过使开发人员能够创建可重用组件并有效管理复杂的UI&#xff0c;彻底改变了前端开发。然而&#xff0c;采用正确的心态对于新开发人员驾驭React独特的范式至关重要。让我们来探索塑造“React思维模式”的基本原则和…...

解密.bixi、.baxia勒索病毒:如何安全恢复被加密数据

导言 在数字化时代&#xff0c;数据安全已成为个人和企业面临的重大挑战之一。随着网络攻击手段的不断演进&#xff0c;勒索病毒的出现尤为引人关注。其中&#xff0c;.bixi、.baxia勒索病毒是一种新型的恶意软件&#xff0c;它通过加密用户的重要文件&#xff0c;迫使受害者支…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...