HarmonyOS 鸿蒙Next中有没有手势处理的框架
HarmonyOS 鸿蒙Next中有没有手势处理的框架

类似这样的效果,大佬们救救
更多关于HarmonyOS 鸿蒙Next中有没有手势处理的框架的实战教程也可以访问 https://www.itying.com/category-93-b0.html
3 回复
开发者你好:
暂时并没有直接的实现希望效果的框架,可以参考下面的组合手势案例实现所希望的对图片平移、缩放、矩阵变换等操作。
【背景知识】
- GestureGroup(组合手势):手势识别组合,即多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。
- 图形变换:该通用属性能用于对组件进行旋转、平移、缩放、矩阵变换等操作。
- 位置设置:该通用属性能设置组件的对齐方式、布局方向和显示位置。
【解决方案】
- 创建图片预览页面,实现在点击图片时,进入图片预览页面,进行大图展示及移动缩放操作。
- 通过在图片预览页面设置组合手势GestureMode控制Image组件的scale属性从而控制图片缩放,以及控制offset/position属性从而控制Image组件的位置。
代码实现方式,参考如下:
- 创建评论浏览主页面。
import { ImagePreviewParam } from './ImagePreview';
@Entry
@Component
struct Index {
@State imageList: (string | Resource)[] = [
$r('app.media.startIcon'),
$r('app.media.startIcon'),
$r('app.media.startIcon')
]
@Provide pathStack: NavPathStack = new NavPathStack()
build() {
Navigation(this.pathStack) {
Column() {
List() {
ForEach(this.imageList, (item: string | Resource, index: number) => {
ListItem() {
Image(item)
.width('100%')
.aspectRatio(1)
.onClick(() => {
this.pathStack.pushPath({
name: 'ImagePreview',
param: { imageList: this.imageList, active: index } as ImagePreviewParam
})
})
}
.width('50%')
.aspectRatio(1)
})
}
.layoutWeight(1)
.width('100%')
}
.padding({
top: AppStorage.get<number>('topRectHeight') || 0
})
}
.height('100%')
.width('100%')
.hideTitleBar(true)
}
}
- 创建图片预览页面。 内部用到的接口:
export interface ImagePreviewParam {
imageList: (string | Resource)[]
active: number
}
export interface imageSize {
width: number
height: number
}
export interface ImageSizeType {
width: number
height: number
scale: number
offsetX: number
offsetY: number
offsetStartX: number
offsetStartY: number
dragOffsetX: number
dragOffsetY: number
}
核心逻辑实现代码:
@Component
struct ImagePreview {
private imageListSize: imageSize[] = []
private defaultScale: number = 1
@Consume pathStack: NavPathStack
@Prop imageList: (string | Resource)[]
@Prop active: number
@State containerHeight: number = 0
@State containerWidth: number = 0
@State disabledSwipe: boolean = false
@State activeImage: ImageSizeType = {
width: 0,
height: 0,
scale: 1,
offsetX: 0,
offsetY: 0,
offsetStartX: 0,
offsetStartY: 0,
dragOffsetX: 0,
dragOffsetY: 0,
}
build() {
NavDestination() {
Swiper() {
ForEach(this.imageList, (item: string, i: number) => {
Image(item)
.width(`calc(100% * ${this.activeImage.scale})`)
.height(`calc(100% * ${this.activeImage.scale})`)
.objectFit(ImageFit.Contain)
.draggable(false)
.scale(this.active === i ? { x: this.activeImage.scale, y: this.activeImage.scale } : null)
.offset(this.active === i ? { x: this.activeImage.offsetX, y: this.activeImage.offsetY } : null)
.onComplete(e => {
if (e?.loadingStatus) {
this.imageListSize[i] = {
width: px2vp(Number(e.contentWidth)),
height: px2vp(Number(e.contentHeight))
}
if (this.active === i) {
this.activeImage.width = this.imageListSize[i].width
this.activeImage.height = this.imageListSize[i].height
}
}
})
}, (item: string, i) => i.toString())
}
.width('100%')
.height('100%')
.index(this.active)
.backgroundColor('#000')
.parallelGesture(
GestureGroup(GestureMode.Exclusive,
PinchGesture({ fingers: 2 })
.onActionStart((event) => {
this.defaultScale = this.activeImage.scale
})
.onActionUpdate((event) => {
let scale = event.scale * this.defaultScale
if (scale <= 4 && scale >= 1) {
this.activeImage.offsetX = this.activeImage.offsetX / (this.activeImage.scale - 1) * (scale - 1) || 0
this.activeImage.offsetY = this.activeImage.offsetY / (this.activeImage.scale - 1) * (scale - 1) || 0
this.activeImage.scale = scale
}
this.disabledSwipe = this.activeImage.scale > 1
})
.onActionEnd((event) => {
this.disabledSwipe = this.activeImage.scale > 1
})
.onActionCancel(() => {
this.disabledSwipe = this.activeImage.scale > 1
}),
PanGesture()
.onActionStart(event => {
this.activeImage.dragOffsetX = event.fingerList[0].globalX
this.activeImage.dragOffsetY = event.fingerList[0].globalY
})
.onActionUpdate((event) => {
if (this.activeImage.scale === 1) {
return
}
let offsetX = event.fingerList[0].globalX - this.activeImage.dragOffsetX +
this.activeImage.offsetStartX
let offsetY = event.fingerList[0].globalY - this.activeImage.dragOffsetY +
this.activeImage.offsetStartY
if (this.activeImage.width * this.activeImage.scale > this.containerWidth &&
(this.activeImage.width * this.activeImage.scale - this.containerWidth) / 2 >=
Math.abs(offsetX)) {
this.activeImage.offsetX = offsetX
}
if (this.activeImage.height * this.activeImage.scale >
this.containerHeight &&
(this.activeImage.height * this.activeImage.scale - this.containerHeight) / 2 >=
Math.abs(offsetY)) {
this.activeImage.offsetY = offsetY
}
if ((this.activeImage.width * this.activeImage.scale - this.containerWidth) / 2 < Math.abs(offsetX)) {
this.disabledSwipe = false
}
})
.onActionEnd((event) => {
this.activeImage.offsetStartX = this.activeImage.offsetX
this.activeImage.offsetStartY = this.activeImage.offsetY
})
.onActionCancel(() => {
this.activeImage.offsetStartX = this.activeImage.offsetX
this.activeImage.offsetStartY = this.activeImage.offsetY
}),
TapGesture({ count: 2 }) // 双击手势
.onAction(() => {
if (this.activeImage.scale > 1) {
this.activeImage.scale = 1
this.activeImage.offsetX = 0
this.activeImage.offsetY = 0
this.activeImage.offsetStartX = 0
this.activeImage.offsetStartY = 0
this.disabledSwipe = false
} else {
this.activeImage.scale = 2
this.disabledSwipe = true
}
}),
TapGesture({ count: 1 }) // 单击手势
.onAction(() => {
this.pathStack?.pop()
}),
)
)
.disableSwipe(this.disabledSwipe)
.onAnimationEnd((index) => {
if (index !== this.active) {
this.activeImage = {
width: 0,
height: 0,
scale: 1,
offsetX: 0,
offsetY: 0,
offsetStartX: 0,
offsetStartY: 0,
dragOffsetX: 0,
dragOffsetY: 0,
}
}
this.active = index
this.disabledSwipe = this.activeImage.scale > 1
this.activeImage.width = this.imageListSize[index].width
this.activeImage.height = this.imageListSize[index].height
})
.itemSpace(50)
.onAreaChange((_, n) => {
this.containerWidth = Number(n.width)
this.containerHeight = Number(n.height)
})
}
.hideTitleBar(true)
.padding({
top: AppStorage.get<number>('topRectHeight') || 0,
bottom: AppStorage.get<number>('bottomRectHeight') || 0,
})
}
}
@Builder
export function ImagePreviewBuilder(_: string, params: ImagePreviewParam) {
ImagePreview({ imageList: params.imageList, active: params.active })
}
更多关于HarmonyOS 鸿蒙Next中有没有手势处理的框架的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
HarmonyOS Next提供了专门的手势处理框架。该框架支持多种手势识别,包括点击、长按、拖拽、缩放和旋转等。开发者可通过Gesture组件和相关API直接实现手势交互功能,无需依赖其他语言或外部库。手势事件处理基于ArkTS/TypeScript实现,与鸿蒙UI组件无缝集成。
HarmonyOS Next提供了完善的手势处理框架,支持多种手势识别和自定义手势开发。通过Gesture、PanGesture、PinchGesture等组件可以实现拖拽、缩放、旋转等交互效果。开发者可以使用@ohos.gesture模块进行手势事件监听和处理,结合@ohos.arkui的UI组件实现流畅的交互体验。具体可参考官方文档中的手势处理章节。

