下图是一个普通的侧边栏,点击打开按钮播放打开动效,点击关闭按钮播放关闭动效。
普通侧边栏侧边栏的结构是这样
侧边栏节点树其中Panel
节点是不动的,移动的是Root
节点。Root
节点的锚点设置成了(0, 0.5)(这个不重要,只是为了计算方便)。
下面是挂载到Panel节点上自定义组件内的打开关闭方法(本文所有代码都写在此组件内),分别计算了开始位置和结束位置,利用缓动完成动画效果。
/**
* 打开面板
*/
open() {
const nodeWidth: number = this.node.getComponent(cc.UITransform).width;
const rootNodeWidth: number = this.rootNode.getComponent(cc.UITransform).width;
// 开始位置
const startPos: cc.Vec3 = cc.v3(-nodeWidth / 2 - rootNodeWidth, this.rootNode.position.y);
// 结束位置
const endPos: cc.Vec3 = cc.v3(-nodeWidth / 2, this.rootNode.position.y);
cc.Tween.stopAllByTarget(this.rootNode);
cc.tween(this.rootNode)
.call(() => {
this.node.active = true;
})
.set({position: startPos})
.to(0.3, {position: endPos}, {easing: cc.easing.quadOut})
.start();
}
/**
* 关闭面板
*/
close() {
const nodeWidth: number = this.node.getComponent(cc.UITransform).width;
const rootNodeWidth: number = this.rootNode.getComponent(cc.UITransform).width;
// 开始位置
const startPos: cc.Vec3 = cc.v3(-nodeWidth / 2, this.rootNode.position.y);
// 结束位置
const endPos: cc.Vec3 = cc.v3(-nodeWidth / 2 - rootNodeWidth, this.rootNode.position.y);
cc.Tween.stopAllByTarget(this.rootNode);
cc.tween(this.rootNode)
.set({position: startPos})
.to(0.3, {position: endPos}, {easing: cc.easing.quadIn})
.call(() => {
this.node.active = false;
})
.start();
}
乍一看好像没啥问题是不是,但是将项目设置成适配屏幕高度,并且在动画过程中改变屏幕比例后就会发现,侧边栏不能完全贴合左边了!就算是动画结束后再改变屏幕比例也是如此。
侧边栏位置偏移我们回过去看代码,open
方法中只获取了一次开始位置和结束位置,然而屏幕比例变化后,结束位置实际上也需要改变。
// 开始位置
const startPos: cc.Vec3 = cc.v3(-nodeWidth / 2 - rootNodeWidth, this.rootNode.position.y);
// 结束位置
const endPos: cc.Vec3 = cc.v3(-nodeWidth / 2, this.rootNode.position.y);
我们可以动态地根据屏幕比例设置侧边栏的位置么?当然是可以的,仅用Cocos Creator的缓动系统就可以做到。
我们知道缓动系统可以根据节点的某个属性的当前值和结束值做出插值,但其实它也可以对其他对象的属性做出插值。比如你可以这样:
const obj = {a: 0};
cc.tween(obj)
.to(1, {a: 100})
.start();
如果我们把obj
对象的属性a
换成getter/setter的形式,我们就可以知道属性被修改了(用Proxy、Object.defineProperty等也都可以做到)。
const obj = {
_a: 0,
set a(a: number) {
this._a = a;
cc.log('set a', a);
},
get a() {
return this._a;
},
};
cc.tween(obj)
.to(1, {a: 100})
.start();
打印
我们再回到侧边栏定位问题上,我们可以发现,尽管不同屏幕比例下侧边栏开始时和结束时的坐标不同,但是在相同时间段内的位移是相同的。把左侧开始位置定义成0%,右侧结束位置定义成100%,我们可以得到这样一个公式:侧边栏坐标=父节点坐标系下屏幕坐标的坐标-(侧边栏宽度×(100-侧边栏移动百分比))/100。
结合前面提到的getter/setter,我们可以写出这样的代码:
// X方向上移动的百分比,0表示侧边栏完全在屏幕外,100表示侧边栏完全在屏幕内
private _movePercentX: number = 0;
set movePercentX(movePercentX: number) {
this._movePercentX = movePercentX;
const nodeWidth: number = this.node.getComponent(cc.UITransform).width;
const rootNodeWidth: number = this.rootNode.getComponent(cc.UITransform).width;
// -nodeWidth / 2 - rootNodeWidth * (100 - this._movePercentX) / 100 就是前面提到的公式
this.rootNode.position = cc.v3(-nodeWidth / 2 - rootNodeWidth * (100 - this._movePercentX) / 100, this.rootNode.position.y);
}
get movePercentX(): number {
return this._movePercentX;
}
接着,我们只需要重写侧边栏的打开关闭动效方法即可。
/**
* 打开面板V2
*/
openV2() {
cc.Tween.stopAllByTarget(this);
cc.tween(this as Panel) // Panel是当前类的名称,如果不进行强制转换,IDE内可能会报错
.call(() => {
this.node.active = true;
})
.set({movePercentX: 0})
.to(0.3, {movePercentX: 100}, {easing: cc.easing.quadOut})
.start();
}
/**
* 关闭面板V2
*/
closeV2() {
cc.Tween.stopAllByTarget(this);
cc.tween(this as Panel)
.set({movePercentX: 100})
.to(0.3, {movePercentX: 0}, {easing: cc.easing.quadIn})
.call(() => {
this.node.active = false;
})
.start();
}
效果展示:
效果展示看起来好多了,但是还有一步,如果在没有播放动效的时候改变了屏幕比例,依然会出现错位问题,所以我们需要监听一下屏幕尺寸变化。由于我们的Panel
节点尺寸跟随了屏幕大小,我们也可以监听Panel
节点尺寸是否改变(监听屏幕大小变化的方式不在本文探讨范围之内,如果你有更好的办法也可以使用)。
start() {
this.node.on(cc.Node.EventType.SIZE_CHANGED, () => {
// 重新赋值刷新侧边栏坐标
this.movePercentX = this.movePercentX;
});
}
效果展示
针不戳!
说明
以上基于Cocos Creator3.6.2版本编写。
网友评论