...
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
2684
assets/main.scene
2684
assets/main.scene
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user