This commit is contained in:
yuanfengxin
2022-07-14 17:42:02 +08:00
parent 5f06585aca
commit 1b1aacfc9a
28 changed files with 44189 additions and 1566 deletions

View File

@@ -61,7 +61,11 @@ export class BezierCurveAnimation extends Component {
tweenSwitchEvent = new cc.EventHandler();
/** 更新事件 */
@property({ displayName: '更新事件', tooltip: '(曲线Y_yN)', type: cc.EventHandler })
@property({
displayName: '更新事件',
tooltip: '(总曲线Y_yN, 当前缓动下标_indexN, 当前缓动曲线Y_yN)',
type: cc.EventHandler
})
updateEvent = new cc.EventHandler();
/** 结束事件 */
@@ -69,25 +73,36 @@ export class BezierCurveAnimation extends Component {
endEvent = new cc.EventHandler();
/* --------------- private --------------- */
/* ------------------------------- segmentation ------------------------------- */
/** 开始缓动 */
startTween(): cc.Tween<any> {
/**
* 开始缓动
* @param startIndexN_ 缓动开始下标
* @param endIndexN_ 缓动结束下标
* @returns
*/
startTween(startIndexN_?: number, endIndexN_ = (startIndexN_ ?? 0) + 1): cc.Tween<any> {
let tweenUnitAs = this.tweenUnitAs;
if (startIndexN_ !== undefined) {
tweenUnitAs = tweenUnitAs.slice(startIndexN_, endIndexN_);
}
/** 总时间(秒) */
let totalTimeSN = this.tweenUnitAs.reduce((preValue, currValue) => preValue + currValue.timeSN, 0);
let totalTimeSN = tweenUnitAs.reduce((preValue, currValue) => preValue + currValue.timeSN, 0);
/** 时间占比 */
let timeRatioNs: number[] = [];
{
let currN = 0;
this.tweenUnitAs.forEach((v, kN) => {
tweenUnitAs.forEach((v, kN) => {
let ratioN = v.timeSN / totalTimeSN;
currN += ratioN;
timeRatioNs.push(currN);
});
}
/** 曲线函数 */
let curveFS = this.tweenUnitAs.map((v) => {
let curveFS = tweenUnitAs.map((v) => {
if (v.customCurveB) {
let curve = new BezierCurve(v.controlPointV3S);
return curve.point.bind(curve) as (kN: number) => number;
return (kN: number) => {
return curve.point(kN).y;
};
} else {
return cc.easing[easingEnum[v.easing]].bind(cc.easing) as (kN: number) => number;
}
@@ -118,13 +133,17 @@ export class BezierCurveAnimation extends Component {
/** 曲线位置 */
let posN = (ratioN - lastTimeRatioN) / timeRangeN;
/** 曲线位置 */
let yN = curveFS[tweenIndexN](posN) * timeRangeN + lastTimeRatioN;
let yN = curveFS[tweenIndexN](posN);
let y2N = yN * timeRangeN + lastTimeRatioN;
// 缓动切换事件触发
if (lastTweenIndexN !== tweenIndexN) {
this.tweenSwitchEvent?.emit([lastTweenIndexN]);
}
if (y2N === undefined) {
debugger;
}
// 更新事件触发
this.updateEvent?.emit([yN]);
this.updateEvent?.emit([y2N, tweenIndexN, yN]);
// 更新缓动下标
lastTweenIndexN = tweenIndexN;
}

View File

@@ -1,7 +1,8 @@
import { _decorator, Component, Node } from 'cc';
import * as cc from 'cc';
import { BezierCurveAnimation } from './BezierCurveAnimation';
const { ccclass, property, requireComponent } = _decorator;
import { EDITOR } from 'cc/env';
const { ccclass, property, requireComponent, executeInEditMode } = _decorator;
/** 旋转抽奖方向 */
export enum RollingLotteryDirection {
@@ -30,19 +31,27 @@ export class RollingLottery extends Component {
/** transform 组件 */
private _uiTransform: cc.UITransform;
/** 周长 */
private _perimeterN: number;
/** 当前距离 */
private _currDistN = 0;
private _perimeterV3 = cc.v3();
/** 总距离 */
private _totalDistN: number;
private _totalDistV3: cc.Vec3;
/** 当前下标 */
private _currIndexN: number;
private _currIndexN = 0;
/** 子节点大小 */
private _ItemSize: cc.Size;
/** 动状态 */
/** 动状态 */
private _scrollB = false;
/** 循环状态 */
private _loopScrollB = false;
/** 循环缓动 */
private _loopTween: cc.Tween<any>;
/** 自己矩形 */
private _selfRect = cc.rect();
/** 上次曲线 Y */
private _lastCurveYN = 0;
/** 当前目标 */
private _currTargetN: number;
/** 当前缓动下标 */
private _currTweenN = 0;
/* --------------- 临时变量 --------------- */
private _tempM4 = cc.mat4();
private _temp2M4 = cc.mat4();
@@ -53,92 +62,175 @@ export class RollingLottery extends Component {
this._initEvent();
}
/* ------------------------------- 功能 ------------------------------- */
/**
* 获取格子移动后距离
* @param currIndexN_ 当前格子下标
* @param targetIndexN_ 目标格子下标
* @returns
*/
private _getItemMovePos(currIndexN_: number, targetIndexN_: number): void {
/** 当前节点 */
let currNode = this.node.getChildByName(currIndexN_ + '');
/** 节点矩形 */
let currRect = this._getBoundingBoxToWorld(currNode);
/** 移动距离 */
let moveDistV3 = cc.v3(this._ItemSize.x, this._ItemSize.y).multiplyScalar(targetIndexN_ - currIndexN_);
/** 移动距离 */
let moveDistN: number;
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
moveDistV3.y = 0;
moveDistN = moveDistV3.x;
} else {
moveDistV3.x = 0;
moveDistN = moveDistV3.y;
}
currRect.x += moveDistV3.x;
currRect.y += moveDistV3.y;
/** 终点坐标 */
let endPosV3 = this.node
.getChildByName(currIndexN_ + '')
.worldPosition.clone()
.add(moveDistV3);
/** 圈数 */
let circleN = Math.floor(moveDistN / this._perimeterN);
// /** 额外移动距离 */
// let extraMoveDistN = moveDistN - circleN * this._perimeterN;
}
/** 获取在世界坐标系下的节点包围盒(不包含自身激活的子节点范围) */
private _getBoundingBoxToWorld(node_: cc.Node): cc.Rect {
node_.getWorldMatrix(this._temp2M4);
cc.Mat4.fromRTS(this._tempM4, this.node.getRotation(), this.node.getPosition(), this.node.getScale());
let width = this._uiTransform.contentSize.width;
let height = this._uiTransform.contentSize.height;
let rect = new cc.Rect(
-this._uiTransform.anchorPoint.x * width,
-this._uiTransform.anchorPoint.y * height,
width,
height
);
let uiTransform = node_.getComponent(cc.UITransform);
cc.Mat4.fromRTS(this._tempM4, node_.getRotation(), node_.getPosition(), node_.getScale());
const width = uiTransform.contentSize.width;
const height = uiTransform.contentSize.height;
const rect = cc.rect(-uiTransform.anchorPoint.x * width, -uiTransform.anchorPoint.y * height, width, height);
node_.parent.getWorldMatrix(this._temp2M4);
cc.Mat4.multiply(this._temp2M4, this._temp2M4, this._tempM4);
rect.transformMat4(this._temp2M4);
return rect;
}
/** 检测参数节点是否与当前节点碰撞 */
private _checkCollision(node_: cc.Node): boolean {
let rect = this._getBoundingBoxToWorld(this.node);
let rect2 = this._getBoundingBoxToWorld(node_);
// 增加保险范围
rect.width += rect.width * 0.5;
rect.height += rect.height * 0.5;
rect.x -= rect.width * 0.25;
rect.y -= rect.height * 0.25;
return rect.intersects(rect2);
/**
* 滚动子节点
* @param distV3_ 距离
*/
private _scrollChild(distV3_: cc.Vec3): void {
/** 当前节点矩形 */
let currNodeRect: cc.Rect;
/** 更新节点坐标 */
let updatePosB = false;
/** 当前节点 UITransform */
let currTransform: cc.UITransform;
/** 头节点 */
let headNode = this.node.children[0];
/** 尾节点 */
let tailNode = this.node.children[this.node.children.length - 1];
/** 头节点矩形 */
let headNodeRect: cc.Rect;
/** 尾节点矩形 */
let tailNodeRect: cc.Rect;
// 左右滚动
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
}
// 上下滚动
else {
// 从上往下滚动
if (distV3_.y < 0) {
this.node.children.forEach((v, kN) => {
currTransform = v.getComponent(cc.UITransform);
updatePosB = false;
currNodeRect = this._getBoundingBoxToWorld(v);
// 移动坐标
currNodeRect.y += distV3_.y;
// 相交则更新节点坐标
if (currNodeRect.intersects(this._selfRect)) {
updatePosB = true;
}
// 若不相交则超出范围
else {
// 若节点在上方则跳过更新
if (currNodeRect.yMin > this._selfRect.yMax) {
updatePosB = true;
} else {
// setTimeout 防止获取节点坐标错误、防止 setSiblingIndex 后遍历错误
setTimeout(() => {
// 切换位置到头节点上方
headNodeRect = this._getBoundingBoxToWorld(headNode);
v.worldPosition = cc.v3(
v.worldPosition.x,
headNodeRect.yMax + currNodeRect.height * currTransform.anchorY
);
// 更新 item
let indexN = Number(headNode.name) - 1;
v.name = indexN + '';
this.itemUpdateEvent.emit([v, indexN]);
// 更新 item 下标
headNode = v;
v.setSiblingIndex(0);
}, 0);
}
}
// 更新节点坐标
if (updatePosB) {
v.worldPosition = cc.v3(
v.worldPosition.x,
currNodeRect.y + currNodeRect.height * currTransform.anchorY
);
}
});
}
// 从下往上滚动
else {
this.node.children.forEach((v, kN) => {
currTransform = v.getComponent(cc.UITransform);
updatePosB = false;
currNodeRect = this._getBoundingBoxToWorld(v);
// 移动坐标
currNodeRect.y += distV3_.y;
// 相交则更新节点坐标
if (currNodeRect.intersects(this._selfRect)) {
updatePosB = true;
}
// 若不相交则超出范围
else {
// 若节点在下方则跳过更新
if (this._selfRect.yMin > currNodeRect.yMax) {
updatePosB = true;
} else {
// setTimeout 防止获取节点坐标错误、防止 setSiblingIndex 后遍历错误
setTimeout(() => {
// 切换位置到尾节点下方
tailNodeRect = this._getBoundingBoxToWorld(tailNode);
v.worldPosition = cc.v3(
v.worldPosition.x,
tailNodeRect.yMin - currNodeRect.height * (1 - currTransform.anchorY)
);
// 更新 item
let indexN = Number(tailNode.name) + 1;
v.name = indexN + '';
this.itemUpdateEvent.emit([v, indexN]);
// 更新 item 下标
tailNode = v;
v.setSiblingIndex(this.node.children.length - 1);
}, 0);
}
}
// 更新节点坐标
if (updatePosB) {
v.worldPosition = cc.v3(
v.worldPosition.x,
currNodeRect.y + currNodeRect.height * currTransform.anchorY
);
}
});
}
}
}
/** 更新运动距离 */
private _updateMoveDist(indexN_: number): void {
/** 当前节点 */
let currNode = this.node.getChildByName(this._currIndexN + '');
/** 间隔格子 */
let intervalN = indexN_ - this._currIndexN;
/** 格子距离 */
let boxDistN = this.dire === RollingLotteryDirection.HORIZONTAL ? this._ItemSize.width : this._ItemSize.height;
/** 超出当前格子距 */
let overDistN = this._currDistN - boxDistN * this._currIndexN;
/** 当前格子距父节点(0, 0)的偏移坐标 */
let offsetDistV3 = this.node.worldPosition.clone().subtract(currNode.worldPosition);
// 设置总距离
this._totalDistN = intervalN * boxDistN - overDistN;
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
this._totalDistV3 = cc.v3(intervalN * boxDistN + offsetDistV3.x);
} else {
this._totalDistV3 = cc.v3(0, intervalN * boxDistN + offsetDistV3.y);
}
}
/** 更新数据 */
private _updateData(): void {
// 单圈长度
this._perimeterN = 0;
this._perimeterV3.set(cc.Vec3.ZERO);
let size: cc.Size;
this.node.children.forEach((v1) => {
this._perimeterN += v1.getComponent(cc.UITransform).height;
size = v1.getComponent(cc.UITransform).contentSize;
this._perimeterV3.add3f(size.x, size.y, 0);
});
// 重置距离
this._currDistN = 0;
}
/** 初始化数据 */
@@ -147,29 +239,31 @@ export class RollingLottery extends Component {
this._uiTransform = this.node.getComponent(cc.UITransform);
this._ItemSize = this.node.children[0].getComponent(cc.UITransform).contentSize.clone();
// 自己矩形
this._selfRect = this._getBoundingBoxToWorld(this.node);
// 设置更新事件
this._curveComp.updateEvent.component = cc.js.getClassName(this);
this._curveComp.updateEvent.handler = 'updateEvent';
this._curveComp.updateEvent.target = this.node;
// 设置结束事件
this._curveComp.endEvent.component = cc.js.getClassName(this);
this._curveComp.endEvent.handler = 'updateEvent';
// 更新当前距离
{
let distV3 = this.node.worldPosition.clone().subtract(this.node.children[0].worldPosition);
this._currDistN = this.dire === RollingLotteryDirection.HORIZONTAL ? distV3.x : distV3.y;
}
// 自己矩形
this._selfRect = this._getBoundingBoxToWorld(this.node);
this._curveComp.endEvent.handler = 'endEvent';
this._curveComp.endEvent.target = this.node;
this._updateData();
}
/** 初始化视图 */
private _initView(): void {
this.scroll(0);
// 重置子节点
this.node.children.forEach((v, kN) => {
v.name = kN + this._currIndexN + '';
this.itemUpdateEvent.emit([v, kN + this._currIndexN]);
});
this.jump(this._currIndexN);
}
/** 初始化事件 */
@@ -179,41 +273,105 @@ export class RollingLottery extends Component {
/**
* 循环滚动
* @param speedN_ 速度
* @param speedN_ 速度/秒
* @param timeSN_ 时间(秒),不填则一直滚动
*/
loop(speedN_: number, timeSN_?: number): void {}
/** 滚动到指定下标 */
scroll(indexN_: number, timeSN_?: number): void {
loop(speedN_: number, timeSN_?: number): void {
if (this._scrollB) {
return;
}
this._scrollB = true;
this._updateMoveDist(indexN_);
/** 移动距离 */
let moveDistN = this._totalDistN - this._currDistN;
// 直接跳转
if (!timeSN_) {
/** 圈数 */
let circleN = Math.floor(moveDistN / this._perimeterN);
/** 额外移动距离 */
let extraMoveDistN = moveDistN - circleN * this._perimeterN;
this._loopScrollB = true;
let distV3 = cc.v3();
this._loopTween = cc
.tween({ valueN: 0, lastValueN: 0 })
.repeatForever(
cc.tween().by(
1,
{
valueN: 1
},
{
onUpdate: (target?: any, ratioN?: number) => {
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
distV3.x = (target.valueN - target.lastValueN) * speedN_;
} else {
distV3.y = (target.valueN - target.lastValueN) * speedN_;
}
this._scrollChild(distV3);
target.lastValueN = target.valueN;
}
}
)
)
.start();
if (timeSN_ !== undefined) {
this.scheduleOnce(() => {
this._loopTween.stop();
this._loopTween = null;
this._loopScrollB = false;
}, timeSN_);
}
}
/* ------------------------------- 自定义事件 ------------------------------- */
tweenSwitchEvent(indexN_: number): void {
// cc.log('缓动切换', indexN_);
}
updateEvent(yN_: number): void {
// cc.log('缓动更新', yN_);
}
endEvent(): void {
// cc.log('缓动结束');
/** 跳转到指定下标 */
jump(indexN_: number): void {
if (this._scrollB) {
return;
}
this._scrollB = true;
// 更新移动距离
this._updateMoveDist(indexN_);
// 开始滚动
this._scrollChild(this._totalDistV3);
// 更新状态
this._currIndexN = indexN_;
this._scrollB = false;
}
/** 子节点更新 */
childUpdate(node_: cc.Node, indexN_: number): void {
node_.getComponentInChildren(cc.Label).string = indexN_ + '';
/** 滚动到指定下标 */
scroll(indexN_: number): void {
if (this._scrollB && !this._loopScrollB) {
return;
}
this._scrollB = true;
// 停止循环滚动
if (this._loopScrollB) {
this._loopTween.stop();
this._loopTween = null;
this._loopScrollB = false;
}
this._currTargetN = indexN_;
this._lastCurveYN = 0;
// 更新移动距离
this._updateMoveDist(indexN_);
// 开始滚动
this._currTweenN = 0;
this._curveComp.startTween(this._currTweenN);
}
/* ------------------------------- 自定义事件 ------------------------------- */
updateEvent(yN_: number, indexN_: number, y2N_: number): void {
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
} else {
this._scrollChild(cc.v3(0, (yN_ - this._lastCurveYN) * this._totalDistV3.y));
}
this._lastCurveYN = yN_;
if (yN_ === undefined) {
debugger;
}
// cc.log('缓动更新', yN_, indexN_, y2N_, yN_ - this._lastCurveYN);
}
endEvent(): void {
this._scrollB = false;
// 继续缓动
this._currTweenN++;
if (this._currTweenN < this._curveComp.tweenUnitAs.length) {
this._curveComp.startTween(this._currTweenN);
}
}
/* ------------------------------- 节点事件 ------------------------------- */
private _nodeSiblingOrderChanged(): void {

File diff suppressed because it is too large Load Diff