HarmonyOS鸿蒙Next中如何获取卡片和按钮的中心位置,使卡片的消失动画效果为缩小并移动到按钮位置?
HarmonyOS鸿蒙Next中如何获取卡片和按钮的中心位置,使卡片的消失动画效果为缩小并移动到按钮位置?
@Entry
@Component
struct CardComponent {
@State @Watch('onAnimationChange') isShow: boolean = false;
@State widthSize: number = 116;
@State heightSize: number = 184;
@State scaleCard: number = 0;
@State opacityCard: number = 0;
@State centerX: string = '0%';
@State centerY: string = '0%';
@State screenOffset: string = '';
@State X: number = 0;
@State Y: number = 0;
targetBtnX: number = 0;
targetBtnY: number = 0;
targetCardX: number = 0;
targetCardY: number = 0;
aboutToAppear(): void {
this.init()
}
onAnimationChange() {
if (this.isShow) {
this.showAnimation()
} else {
this.hideAnimation()
}
}
showAnimation() {
this.X = 0;
this.Y = 0;
this.centerX = '0%'
this.centerY = '100%'
// 创建动画
this.getUIContext()?.animateTo({
duration: 300,
curve: Curve.EaseInOut
}, () => {
// 放大1倍
this.scaleCard = 1
// 透明度
this.opacityCard = 1
})
}
hideAnimation() {
this.centerX = '50%'
this.centerY = '50%'
// 创建动画
this.getUIContext()?.animateTo({
duration: 300,
curve: Curve.EaseInOut
}, () => {
// 缩小
this.scaleCard = 0
// 透明度
this.opacityCard = 0
// 计算需要移动的偏移量,使卡片中心移动到按钮中心
this.X = this.targetBtnX - this.targetCardX;
this.Y = this.targetBtnY - this.targetCardY;
})
}
// 初始化方法:获取初始位置
init() {
// 绑定指定组件,返回对应的监听句柄
let observer = this.getUIContext().getUIInspector().createComponentObserver('targetBtn');
let observer2 = this.getUIContext().getUIInspector().createComponentObserver('targetCard');
observer.on('layout', () => {
let nodeBtn = this.getUIContext().getComponentUtils().getRectangleById('targetBtn');
if (nodeBtn) {
// 存储按钮的中心位置
this.targetBtnX = nodeBtn.screenOffset.x + nodeBtn.size.width / 2;
this.targetBtnY = nodeBtn.screenOffset.y + nodeBtn.size.height / 2;
console.log(`targetBtnX: ${this.targetBtnX}, targetBtnY: ${this.targetBtnY}`);
}
console.log(`targetCardX: ${this.targetCardX}, targetCardY: ${this.targetCardY}`);
}
// 通过句柄向对应的查询条件取消注册回调,当组件布局完成时不再触发指定的回调
observer.off('layout');
})
observer2.on('layout', () => {
let nodeCard = this.getUIContext().getComponentUtils().getRectangleById('targetCard');
if (nodeCard) {
this.screenOffset = JSON.stringify(nodeCard.screenOffset)
// 存储按钮的卡片中心位置
this.targetCardX = nodeCard.screenOffset.x + nodeCard.size.width / 2;
this.targetCardY = nodeCard.screenOffset.y + nodeCard.size.height / 2;
}
// 通过句柄向对应的查询条件取消注册回调,当组件布局完成时不再触发指定的回调
observer2.off('layout');
})
}
build() {
Column() {
Column() {}
.width(this.widthSize)
.height(this.heightSize)
.scale({
x: this.scaleCard,
y: this.scaleCard,
centerX: this.centerX,
centerY: this.centerY
})
.visibility(this.opacityCard ? Visibility.Visible : Visibility.None)
.translate({ x: this.X, y: this.Y })
.borderRadius(6)
.backgroundColor(Color.Yellow)
.justifyContent(FlexAlign.Start)
.id('targetCard')
Button('按钮')
.width(100)
.height(50)
.position({ 'bottom': 0, 'right': 0})
.id('targetBtn')
.onClick(() => {
this.isShow = !this.isShow
})
}
.width('100%')
.height('100%')
}
}
现在想实现的效果是出现的时候从左下角展开,收起的时候会收到按钮所在的位置。目前通过计算组件位置获取移动距离,但是获得的位置不太对?
更多关于HarmonyOS鸿蒙Next中如何获取卡片和按钮的中心位置,使卡片的消失动画效果为缩小并移动到按钮位置?的实战教程也可以访问 https://www.itying.com/category-93-b0.html
楼主好aboutToAppear中直接调用init可能导致布局未完成前获取位置信息,造成坐标计算错误。
getRectangleById返回的是屏幕绝对坐标,而动画平移使用的是组件相对坐标。所以这要改一下。监听layout事件后立即注销,无法响应动态布局变化。
试一哈通过组件ID获取相对位置
getTargetPosition(id: string) {
const node = this.getUIContext().getComponentUtils().getGeometryById(id);
if (node) {
return {
x: node.position.x + node.size.width / 2,
y: node.position.y + node.size.height / 2
}
}
return { x: 0, y: 0 };
}
调整动画参数设置
hideAnimation() {
const btnPos = this.getTargetPosition('targetBtn');
const cardPos = this.getTargetPosition('targetCard');
this.getUIContext().animateTo({
duration: 300,
curve: Curve.EaseInOut
}, () => {
this.scaleCard = 0;
this.opacityCard = 0;
// 计算相对父容器的偏移量
this.X = btnPos.x - cardPos.x;
this.Y = btnPos.y - cardPos.y;
});
}
优化一下布局监听机制
init() {
const observer = this.getUIContext().getUIInspector().createComponentObserver('targetBtn');
observer.on('layout', () => {
this.targetBtnPos = this.getTargetPosition('targetBtn');
console.log(`Button中心: ${JSON.stringify(this.targetBtnPos)}`);
});
const cardObserver = this.getUIContext().getUIInspector().createComponentObserver('targetCard');
cardObserver.on('layout', () => {
this.targetCardPos = this.getTargetPosition('targetCard');
console.log(`卡片中心: ${JSON.stringify(this.targetCardPos)}`);
});
}
更多关于HarmonyOS鸿蒙Next中如何获取卡片和按钮的中心位置,使卡片的消失动画效果为缩小并移动到按钮位置?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
通过onAreaChange事件获取组件的布局区域信息,记录中心点坐标
// 按钮位置状态
@State btnCenterX: number = 0
@State btnCenterY: number = 0
// 卡片位置状态
@State cardCenterX: number = 0
@State cardCenterY: number = 0
Button('目标按钮')
.onAreaChange((oldArea, newArea) => {
this.btnCenterX = newArea.globalPosition.x + newArea.width / 2
this.btnCenterY = newArea.globalPosition.y + newArea.height / 2
})
// 卡片组件同理
可以试试
在HarmonyOS Next中实现卡片动画效果时,获取组件位置需要注意以下几点:
-
使用getRectangleById获取的是组件在屏幕中的绝对位置,包含状态栏等系统UI的高度。建议改用getBoundingClientRect获取相对父组件的坐标。
-
当前代码在observer回调中立即调用了off(),这会导致只触发一次位置更新。应该保留监听以便在窗口尺寸变化时更新位置。
-
动画中心点计算可以简化为:
// 获取按钮中心点
const btnRect = this.getUIContext().getComponentUtils().getBoundingClientRect('targetBtn');
this.targetBtnX = btnRect.left + btnRect.width / 2;
this.targetBtnY = btnRect.top + btnRect.height / 2;
// 获取卡片中心点
const cardRect = this.getUIContext().getComponentUtils().getBoundingClientRect('targetCard');
this.targetCardX = cardRect.left + cardRect.width / 2;
this.targetCardY = cardRect.top + cardRect.height / 2;
- 动画实现时,建议使用transform-origin设置缩放中心点,而不是通过centerX/centerY:
.scale({
x: this.scaleCard,
y: this.scaleCard
})
.transformOrigin({ x: '50%', y: '100%' }) // 设置缩放中心为底部中点
- 对于收起动画,直接计算两个中心点的差值作为translate的偏移量即可:
this.X = this.targetBtnX - this.targetCardX;
this.Y = this.targetBtnY - this.targetCardY;
注意检查组件是否已正确设置id,并且确保在组件挂载完成后再获取位置信息。