Canvas 内部元素如何实现 mouseover/mousemove 事件?
我在使用 Collie 引擎 来开发一个简单的游戏,它提供了 mousedown、mouseup、click 这三个鼠标事件,但是我想实现的功能是:当我的鼠标移到元素上的时候,显示该元素的名字。但这个引擎并没有提供 mouseover 或 mousemove 事件。
Canvas 是不是只能通过获取指针在画面上的坐标,然后判断是否在元素的范围,来模拟这个事件?
如果是的话,有什么好的算法,用来判断指针是否在 x、y、width、height (或者是圆型 x、y、radius) 范围中?
如果不是的话,应该怎么做呢?
女王请用力些
10 years, 11 months ago
Answers
自己看源代碼不就好了
和我想得一樣,就是循環判斷,比大小確定範圍,直到找到爲止。
想要實現 mouseover 啥的,自己照着 _fireEvent 調用 _getTargetOnHitEvent 即可
/**
* 레이어에서 이벤트가 일어났을 때 표시 객체에 이벤트를 발생 시킨다
*
* @param {Object} e 이벤트 원본
* @param {String} sType 이벤트 타입, mouse 이벤트로 변형되서 들어온다
* @param {Number} nX 이벤트가 일어난 상대좌표
* @param {Number} nY 이벤트가 일어난 상대좌표
* @return {Boolean} 표시 객체에 이벤트가 발생했는지 여부
* @private
*/
_fireEvent : function (e, sType, nX, nY) {
var oDisplayObject = null;
var bIsNotStoppedBubbling = true;
// 캔버스에서 이전 레이어에 객체에 이벤트가 일어났으면 다음 레이어의 객체에 전달되지 않는다
if (sType !== "mousemove" && !collie.Renderer.isStopEvent(sType)) {
var aDisplayObjects = this._oLayer.getChildren();
oDisplayObject = this._getTargetOnHitEvent(aDisplayObjects, nX, nY);
// mousedown일 경우 객체를 저장한다
if (oDisplayObject) {
bIsNotStoppedBubbling = this._bubbleEvent(oDisplayObject, sType, e, nX, nY);
if (sType === "mousedown") {
this._setMousedownObject(oDisplayObject);
}
if (sType === "mouseup") {
this._unsetMousedownObject(oDisplayObject);
}
}
}
// mouseup 처리가 안된 경우 임의 발생
if (sType === "mouseup" && this._getMousedownObject() !== null) {
oDisplayObject = this._getMousedownObject();
this._bubbleEvent(oDisplayObject, sType, e, nX, nY);
this._unsetMousedownObject(oDisplayObject);
}
/**
* click 이벤트, 모바일 환경일 때는 touchstart, touchend를 비교해서 좌표가 일정 이내로 움직였을 경우 click 이벤트를 발생한다d
* @name collie.Layer#click
* @event
* @param {Object} htEvent
* @param {collie.DisplayObject} htEvent.displayObject 대상 객체
* @param {HTMLEvent} htEvent.event 이벤트 객체
* @param {Number} htEvent.x 상대 x좌표
* @param {Number} htEvent.y 상대 y좌표
*/
/**
* mousedown 이벤트, 모바일 환경일 때는 touchstart 이벤트도 해당 된다.
* @name collie.Layer#mousedown
* @event
* @param {Object} htEvent
* @param {collie.DisplayObject} htEvent.displayObject 대상 객체
* @param {HTMLEvent} htEvent.event 이벤트 객체
* @param {Number} htEvent.x 상대 x좌표
* @param {Number} htEvent.y 상대 y좌표
*/
/**
* mouseup 이벤트, 모바일 환경일 때는 touchend 이벤트도 해당 된다.
* @name collie.Layer#mouseup
* @event
* @param {Object} htEvent
* @param {collie.DisplayObject} htEvent.displayObject 대상 객체
* @param {HTMLEvent} htEvent.event 이벤트 객체
* @param {Number} htEvent.x 상대 x좌표
* @param {Number} htEvent.y 상대 y좌표
*/
/**
* mousemove 이벤트, 모바일 환경일 때는 touchmove 이벤트도 해당 된다.
* @name collie.Layer#mouseup
* @event
* @param {Object} htEvent
* @param {collie.DisplayObject} htEvent.displayObject 대상 객체
* @param {HTMLEvent} htEvent.event 이벤트 객체
* @param {Number} htEvent.x 상대 x좌표
* @param {Number} htEvent.y 상대 y좌표
*/
if (bIsNotStoppedBubbling) { // stop되면 Layer이벤트도 일어나지 않는다
this._oLayer.fireEvent(sType, {
event : e,
displayObject : oDisplayObject,
x : nX,
y : nY
});
}
return !!oDisplayObject;
},
/**
* 이벤트 대상을 고른다
* - 가장 위에 있는 대상이 선정되어야 한다
* @private
* @param {Array|collie.DisplayObject} vDisplayObject
* @param {Number} nX 이벤트 상대 x 좌표
* @param {Number} nY 이벤트 상대 y 좌표
* @return {collie.DisplayObject|Boolean}
*/
_getTargetOnHitEvent : function (vDisplayObject, nX, nY) {
var oTargetObject = null;
if (vDisplayObject instanceof Array) {
for (var i = vDisplayObject.length - 1; i >= 0; i--) {
// 자식부터
if (vDisplayObject[i].hasChild()) {
oTargetObject = this._getTargetOnHitEvent(vDisplayObject[i].getChildren(), nX, nY);
// 찾았으면 멈춤
if (oTargetObject) {
return oTargetObject;
}
}
// 본인도
oTargetObject = this._getTargetOnHitEvent(vDisplayObject[i], nX, nY);
// 찾았으면 멈춤
if (oTargetObject) {
return oTargetObject;
}
}
} else {
return this._isPointInDisplayObjectBoundary(vDisplayObject, nX, nY) ? vDisplayObject : false;
}
},
/**
* DisplayObject 범위 안에 PointX, PointY가 들어가는지 확인
*
* @private
* @param {collie.DisplayObject} oDisplayObject
* @param {Number} nPointX 확인할 포인트 X 좌표
* @param {Number} nPointY 확인할 포인트 Y 좌표
* @return {Boolean} 들어간다면 true
*/
_isPointInDisplayObjectBoundary : function (oDisplayObject, nPointX, nPointY) {
// 안보이는 상태거나 이벤트를 받지 않는다면 지나감
if (
!oDisplayObject._htOption.useEvent ||
!oDisplayObject._htOption.visible ||
!oDisplayObject._htOption.width ||
!oDisplayObject._htOption.height ||
(oDisplayObject._htOption.useEvent === "auto" && !oDisplayObject.hasAttachedHandler())
) {
return false;
}
var htHitArea = oDisplayObject.getHitAreaBoundary();
// 영역 안에 들어왔을 경우
if (
htHitArea.left <= nPointX && nPointX <= htHitArea.right &&
htHitArea.top <= nPointY && nPointY <= htHitArea.bottom
) {
// hitArea 설정이 없으면 사각 영역으로 체크
if (!oDisplayObject._htOption.hitArea) {
return true;
} else {
var htPos = oDisplayObject.getRelatedPosition();
// 대상 Point를 상대 좌표로 변경
nPointX -= htPos.x;
nPointY -= htPos.y;
// transform 적용
var aHitArea = oDisplayObject._htOption.hitArea;
aHitArea = collie.Transform.points(oDisplayObject, aHitArea);
return this._isPointInPolygon(aHitArea, nPointX, nPointY);
}
}
return false;
},
lolicn
answered 10 years, 11 months ago