直接先上代码吧
//MOUSE_MOVE 当鼠标在目标节点在目标节点区域中移动时,不论是否按下。
//MOUSE_DOWN 当鼠标按下时触发一次。
//MOUSE_UP MOUSE_ENTER
//MOUSE_LEAVE 当鼠标移出目标节点区域时,不论是否按下。
//TOUCH_CANCEL 当手指在目标节点区域外离开屏幕时。
//TOUCH_END 当手指在目标节点区域内离开屏幕时。
//TOUCH_MOVE 当手指在屏幕上移动时。
//TOUCH_START 当手指触摸到屏幕时。
//cc.Node.EventType
//convertTouchToNodeSpaceAR 先将空间坐标转到其父节点的 这样实现在父节点区域内可移动
//矩阵位置坐标 x,y为矩阵左上角的坐标值
let Rect = function (x, y, height, width) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
}
/**
* @brief 判断两个轴对齐的矩形是否重叠
* @param rc1 第一个矩阵的位置
* @param rc2 第二个矩阵的位置
* @return 两个矩阵是否重叠(边沿重叠,也认为是重叠)
*/
Rect.prototype.isOverlap = function (rectB) {
if (this.x + this.width > rectB.x &&
rectB.x + rectB.width > this.x &&
this.y + this.height > rectB.y &&
rectB.y + rectB.height > this.y) {
return true;
}
return false;
}
/**
* @brief 判断两个轴对齐的矩形是否重叠
* @param rc1 第一个矩阵的位置
* @param rc2 第二个矩阵的位置
* @return 两个矩阵是否重叠(边沿重叠,也认为是重叠)
*/
Rect.prototype.isOverlap1 = function (rectB) {
let x1 = this.x + this.width
let y1 = this.y - this.height;
let x2 = rectB.x + rectB.width
let y2 = rectB.y - rectB.height;
let olbx = Math.max(this.x, rectB.x);
let oltx = Math.min(x1, x2);;
let olby = Math.max(this.y, rectB.y);
let olty = Math.min(y1, y2);
return (olbx < oltx) && (olby < olty);
}
Rect.prototype.isOverlap2 = function (rectB) {
let limitX = [Math.max(this.x, rectB.x), Math.min(this.x + this.width, rectB.x + rectB.width)];
let limitY = [Math.max(this.y - this.height, rectB.y - rectB.height), Math.min(this.y, rectB.y)];
if ((limitX[0] < limitX[1]) && limitY[0] < limitY[1]) {
return true;
}
return false;
}
cc.Class({
extends: cc.Component,
properties: {
isVerticalMove: false,
isHorizontalMove: true,
moveNode: {
type: cc.Node,
default: null,
tootip: "待控制移动的节点"
},
arreaInsulateNode: {
type: cc.Node,
default: null,
tootip: "可在框起来的节点区域移动 anchorX anchorY必须为0.5"
},
arreaLimitNode: {
type: cc.Node,
default: [],
tootip: "节点不得进入的节点区域"
}
},
onLoad: function () {
this.updateTimer = 0;
this._registerHandler();
},
/**
*
* *外部预留控制接口
* @param {*} startTouchCallback
* @param {*} touchEndCallback 外部注册触摸结束时回调 原生的需要两个callback都注册
* @param {*} touchCancelCallback 外部注册触摸取消时回调
*/
initCallback: function (startTouchCallback, touchEndCallback, touchCancelCallback) {
this.__startTouchCallback = startTouchCallback;
this.__touchEndCallback = touchEndCallback;
this.__touchCancelCallback = touchCancelCallback;
},
_registerHandler: function () {
if (CC_JSB) {
this.moveNode.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this, true);
this.moveNode.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this, true);
this.moveNode.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchCancel, this, true);
this.moveNode.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this.true);
} else {
this.moveNode.on(cc.Node.EventType.MOUSE_LEAVE, this.onTouchEnd, this, true);
this.moveNode.on(cc.Node.EventType.MOUSE_ENTER, this.onTouchStart, this,true);
this.moveNode.on(cc.Node.EventType.MOUSE_MOVE, this.onTouchMove, this, true);
}
},
/**
*z外部主动调起注册事件的函数
*
*/
registerHandler: function () {
this.unregisterHandler();
this._registerHandler();
},
/**
* 外部空接口,注销事件,可主动调起,但是需要保证在向反处主动调起注册事件的函数registerHandler
*/
unregisterHandler: function () {
if (CC_JSB) {
this.moveNode.off(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.moveNode.off(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.moveNode.off(cc.Node.EventType.TOUCH_CANCEL, this.onTouchCancel, this);
this.moveNode.off(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
} else {
this.moveNode.off(cc.Node.EventType.MOUSE_MOVE, this.onTouchMove, this);
this.moveNode.off(cc.Node.EventType.MOUSE_LEAVE, this.onTouchEnd, this);
this.moveNode.off(cc.Node.EventType.MOUSE_ENTER, this.onTouchStart, this);
}
},
/**
*此处可以添加当玩家解除待移动节点时,节点变色,变粗等醒目操作
*
* @param {*} event
* @returns
*/
onTouchStart: function (event) {
if(!cc.isValid(this.arreaInsulateNode)) return;
let Vec = this.arreaInsulateNode.convertToWorldSpaceAR(event.getLocation());
let VecMove = this.arreaInsulateNode.convertToWorldSpaceAR(this.moveNode.getPosition());
console.log("Vec:",Vec);
console.log("VecMove:",VecMove);
this.__startTouchCallback && this.__startTouchCallback(event, this);
},
/**
*
*让节点回归常态就好啊
* @param {*} event
* @returns
*/
onTouchEnd: function (event) {
if(!cc.isValid(this.arreaInsulateNode)) return;
this.__touchEndCallback && this.__touchEndCallback(event, this);
},
onTouchCancel: function (event) {
if(!cc.isValid(this.arreaInsulateNode)) return;
this.__touchCancelCallback && this.__touchCancelCallback(event, this);
},
/**
*移动节点具体逻辑
*
* @param {*} event
* @returns
*/
onTouchMove: function (event) {
if (!cc.isValid(this.arreaInsulateNode)) return;
// console.log("", this.moveNode.zIndex);
let heightM = this.moveNode.height; // 待移动节点的长*宽
let widthM = this.moveNode.width;
let heightP = this.arreaInsulateNode.height;//假设可移动区域大小为 heightP*widthP 原点为左下角位置
let widthP = this.arreaInsulateNode.width;
let canvasNode = cc.director.getScene().getChildByName('Canvas');
if (!cc.isValid(canvasNode)) return;
let arreaLimit = canvasNode.convertToWorldSpaceAR(this.arreaInsulateNode.getPosition());
let changeX = this.moveNode.x + event.getDeltaX();
let chageXflag = false;
if (this.isHorizontalMove) {
let deltX = arreaLimit.x - widthP / 2;
let changeLimitX = widthP - (1 - this.moveNode.anchorX) * widthM + deltX;
let changeLimitXleft = this.moveNode.anchorX * widthM + deltX;
let newVec2 = this.arreaInsulateNode.convertToWorldSpaceAR(cc.v2(changeX, 0));
//changeX 属于区间(0,heightP)
if (newVec2.x > changeLimitXleft && newVec2.x < changeLimitX) {
chageXflag = true;
}
}
let changeY = this.moveNode.y + event.getDeltaY();
let changeYFlag = false;
if (this.isVerticalMove) {
let deltY = arreaLimit.y - heightP / 2;
let changeLimitY = heightP - (1 - this.moveNode.anchorY) * heightM + deltY;
let changeLimitYboom = this.moveNode.anchorY * heightM + deltY;
let newVec2 = this.arreaInsulateNode.convertToWorldSpaceAR(cc.v2(0, changeY));
if (newVec2.y >= changeLimitYboom && newVec2.y <= changeLimitY) {
changeYFlag = true;
}
}
// if (this.isRectLap(changeX, changeY)) {
// return;
// }
if (chageXflag&&this.isRectLap(changeX, this.moveNode.y)) {//只有需要计算的情况才去计算啦
chageXflag = false;
}
if (changeYFlag&&this.isRectLap(this.moveNode.x, changeY)) {
changeYFlag = false;
}
if (chageXflag) {
this.moveNode.x = changeX;
}
if (changeYFlag) {
this.moveNode.y = changeY;
}
// var a = this.moveNode.parent.convertTouchToNodeSpaceAR(event);
},
/**
*判断改变后,移动节点与不可穿越节点是否有重叠
*
* @param {*} changeX
* @param {*} changeY
* @returns
*/
isRectLap: function (changeX, changeY) {
if (!cc.isValid(this.arreaInsulateNode)) return;
if (this.arreaLimitNode.length <= 0) return;
let newVec2 = this.arreaInsulateNode.convertToWorldSpaceAR(cc.v2(changeX, changeY));
let Dx = newVec2.x - this.moveNode.anchorX * this.moveNode.width;
let Dy = newVec2.y + (1 - this.moveNode.anchorY) * this.moveNode.height;
let rectA = new Rect(Dx, Dy, this.moveNode.height, this.moveNode.width);
// console.log("rectA Dx:", Dx, " Dy", Dy, " height:", this.moveNode.height, " width:", this.moveNode.width)
for (let index = 0; index < this.arreaLimitNode.length; index++) {
let nodeA = this.arreaLimitNode[index];
if (cc.isValid(nodeA)) {
let newVecA = this.arreaInsulateNode.convertToWorldSpaceAR(nodeA.getPosition());
// console.log("rectb Dxa:",newVecA)
let Dxa = newVecA.x - nodeA.anchorX * nodeA.width;
let Dya = newVecA.y + (1 - nodeA.anchorY) * nodeA.height;
let rectB = new Rect(Dxa, Dya, nodeA.height, nodeA.width);
// console.log("rectb Dxa:", Dxa, " Dya", Dya, " height:", nodeA.height, " width:", nodeA.width);
// console.log("index",index,"rectA.isOverlap(rectB)=",rectA.isOverlap(rectB));
if (rectA.isOverlap2(rectB)) {
return true;
}
}
}
return false;
},
onDestroy: function () {
this.unregisterHandler();
},
// //每帧调用一次。根据滚动位置动态更新item的坐标和显示(所以spawnCount可以比totalCount少很多)
// update: function (dt) {
// this.updateTimer += dt;
// if (this.updateTimer < this.updateInterval) {
// return; // we don't need to do the math every frame
// }
// this.updateTimer = 0;
// },
});
文中主要实现是注册 TOUCH_MOVE和MOUSE_MOVE的来监听节点触摸移动。同时对节点的可移动区域进行处理。至于对其他时间监听主要是为了形成完整的接口处理节点的显示等状态。代码还算是逻辑清晰。