HarmonyOS 6商城开发学习:收藏/加购图标按压弹性缩放与关键帧动效实战
在 HarmonyOS 6 电商或购物比价类应用中,"❤️收藏"和"🛒加入购物车"图标若只有静态点击反馈,会显得生硬且缺乏质感。理想的交互是:手指按下时图标微微缩小(蓄力感),抬起时弹回并伴随环形进度闪烁(确认感)。
官方购物比价行业实践指出,可通过 onTouch监听 TouchType.Down/Up结合 UIContext.keyframeAnimateTo关键帧动画实现该效果。本文将抛开冗余代码,从零构建一个可复用的商城图标点击动效组件。
一、现象:点击无反馈或动画僵硬
1. 常见错误写法
// ❌ 只用 onClick + animateTo,无法感知"按下/松开",无按压蓄力感
Image($r('app.media.ic_favorite'))
.onClick(() => {
animateTo({ duration: 200 }, () => { /* 只改变状态 */ })
})
-
onClick无法区分按下和松开,scale变化突兀 -
没有"按下去缩小→松开弹回"的连贯帧
2. 根因揭秘
组件需同时监听:
-
TouchType.Down:触发缩小(scale ~0.85)+ 计数/状态变更
-
TouchType.Up:恢复原始尺寸 + 关键帧环闪提示
-
使用
getUIContext().keyframeAnimateTo()代替旧版animateTo以获得更精确的关键帧控制(ArkUI 推荐做法)
二、完整实现——商城收藏图标按压动效
以下代码可直接放入 pages/FavIconAnimPage.ets运行:
// pages/FavIconAnimPage.ets
@Entry
@Component
struct FavIconAnimPage {
// 图标容器宽高(控制热区)
@State boxSize: number = 56
// 缩放比例:按下缩小,松开复原
@State scaleVal: number = 1.0
// 环形进度(用于 Up 时的闪烁反馈)
@State ringVal: number = 0
// 是否已收藏
@State isFav: boolean = false
// 点击次数示意
@State clickCount: number = 0
private uiCtx: UIContext | undefined = undefined
aboutToAppear(): void {
this.uiCtx = this.getUIContext()
}
build() {
Column({ space: 30 }) {
Text('HarmonyOS 6 商城图标点击动效')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 40 })
Text(`已收藏次数:${this.clickCount}`)
.fontSize(14)
.fontColor('#666')
// ===== 核心:Stack 叠放图标 + 环形进度 =====
Stack() {
// ① 底层:触摸反馈环形进度(Up时闪一下)
Progress({
value: this.ringVal,
total: 100,
type: ProgressType.Ring
})
.width(this.boxSize + 10)
.height(this.boxSize + 10)
.color('#FF5722')
.style({ strokeWidth: 2.5, enableSmoothEffect: true })
// ② 上层:收藏图标(带缩放动画)
Image(this.isFav ? $r('app.media.ic_favorite_fill') : $r('app.media.ic_favorite'))
.width(28)
.height(28)
.fillColor(this.isFav ? '#FF5722' : '#888')
.scale({ x: this.scaleVal, y: this.scaleVal })
// ★ 核心触摸事件
.onTouch((e: TouchEvent) => {
if (e.type === TouchType.Down) {
// 按下:缩小蓄力
this.uiCtx?.keyframeAnimateTo({ iterations: 1 }, [
{
duration: 80,
event: () => {
this.scaleVal = 0.82
}
}
])
}
if (e.type === TouchType.Up) {
// 切换收藏状态
this.isFav = !this.isFav
this.clickCount++
// 松开:弹回原尺寸
this.uiCtx?.keyframeAnimateTo({ iterations: 1 }, [
{
duration: 120,
curve: Curve.Friction,
event: () => {
this.scaleVal = 1.0
}
}
])
// 环形进度快闪(0→100→0)
this.ringVal = 0
this.uiCtx?.keyframeAnimateTo({ iterations: 1 }, [
{
duration: 150,
event: () => { this.ringVal = 100 }
},
{
duration: 100,
event: () => { this.ringVal = 0 }
}
])
}
})
}
.width(this.boxSize)
.height(this.boxSize)
.justifyContent(FlexAlign.Center)
Text('👆 按下缩小 / 松开弹回 + 环闪')
.fontSize(12)
.fontColor('#999')
}
.width('100%')
.height('100%')
.backgroundColor('#F6F7FB')
.justifyContent(FlexAlign.Center)
}
}
三、关键结构解读
Stack
├── Progress (Ring) ← 环形触摸反馈,Up时 0→100→0
└── Image (收藏图标) ← scale 绑定 @State scaleVal
└── onTouch(Down → 缩到0.82 / Up → 弹回1.0 + 切换状态 + 环闪)
为什么要这样写?
|
要素 |
说明 |
|---|---|
|
|
需捕获 Down/Up 两阶段, |
|
|
官方推荐替代 |
|
|
弹回时用摩擦曲线,比线性更有"弹性"质感 |
|
|
不额外增加布局层级,环形进度默认透明背景,平时不可见 |
|
热区 |
Stack 设固定 |
四、避坑指南
|
问题 |
原因 |
修复 |
|---|---|---|
|
动画不执行或无关键帧效果 |
未通过 |
必须在 |
|
快速连点缩放错乱 |
Down/Up 帧相互覆盖 |
可加防抖标志 |
|
环闪看不到 |
|
Up 时先 |
|
图标被 Progress 挡住 |
子组件顺序反了 |
Stack 先 Progress 再 Image(后渲染在上层) |
|
想用于加购 Cart 图标 |
同样逻辑,去掉 Ring 或改用 Badge 数字跳动 |
复用相同 |
五、总结:商城图标点击动效 SOP
-
用
onTouch而非onClick 区分 Down(缩小)和 Up(弹回) -
@State scaleVal驱动 Image.scale,Down→0.8x,Up→1.0 -
UIContext.keyframeAnimateTo 做两段关键帧(缩小帧 / 弹回帧),弹回加Curve.Friction -
可选 Ring Progress 或 Badge 数字 作为 Up 时的视觉确认反馈
-
Stack 包 Icon + Feedback 层,保证热区≥44dp
核心法则:HarmonyOS 6 商城交互质感 =
onTouch(Down/Up)+keyframeAnimateTo缩放关键帧 + 微反馈层(Ring/Badge)。
更多推荐
所有评论(0)