使用 jQuery 和 JavaScript 捕获客户端鼠标事件
用于捕获客户端鼠标事件的 JavaScript 库。
引言
我见过几个允许你捕获特定事件的客户端库——单击、双击、三击。但是,我需要一个更强大的库,它能够提供可配置的鼠标释放事件。这个 JavaScript 库就是这个需求的产物。
与其自己编写代码来捕获各种鼠标事件,并处理如何区分双击与两个单击,甚至三击的问题,不如使用这段代码来完成。
背景
我需要编写一个摄像头的 PTZ(平移、倾斜、缩放)控制,并希望为用户提供各种与鼠标相关的事件。
使用代码
这段代码本质上会挂接到一个名为 mouseLayer
的 div。你只需要引用 jQuery,一些将 div 定位在某个位置的 CSS,以及以下 JavaScript 库:
// vlcContainer can be defined from the calling file - this is the ID
// of the VLC player. if not, the default ID is vlc-player
var vlc;
// cascading events can occur - detection of end events such as single, double and triple left clicks
// for example:
// - single left-click without movement does not need to send delta (0, 0)
// - single left-click with movement needs to send a delta(0, 0)
// - double left-click - trigger event, but no need to send delta (0, 0) without movement
// - double left-click with movement (capture X and/or Y); send delta (0, 0)
// send a stopped event if movement with movement detected {t/f}
// if true, stop event sent on button release if movement occurred
// if false, stop event sent on button release
var stopEventWithMovement = {};
stopEventWithMovement["leftSingleClick"] = true;
stopEventWithMovement["leftDoubleClick"] = true;
stopEventWithMovement["leftTripleClick"] = true;
stopEventWithMovement["rightSingleClick"] = true;
stopEventWithMovement["rightDoubleClick"] = true;
stopEventWithMovement["rightTripleClick"] = true;
function getVLC(name) {
if (window.document[name]) {
return window.document[name];
}
if (navigator.appName.indexOf("Microsoft Internet") == -1) {
if (document.embeds && document.embeds[name])
return document.embeds[name];
}
else // if (navigator.appName.indexOf("Microsoft Internet")!=-1)
{
return document.getElementById(name);
}
return null;
}
function registerVideoContainer() {
// you can define the name ID of the player in JS in the source file
if (typeof vlcContainer == 'undefined') {
vlc = getVLC('vlc-player');
} else {
vlc = getVLC(vlcContainer);
}
try {
console.log("VLC plugin version: " + vlc.versionInfo());
// v 2.0.8 Twoflower
} catch (e) {
// do nothing
}
var options = new Array(":aspect-ratio=4:3", "--rtsp-tcpasdfasdf");
var id = vlc.playlist.add("rtsp://10.120.7.193/stream1", "LIVE STREAM", options);
//var id = vlc.playlist.add("http://people.videolan.org/~dionoea/
// vlc-plugin-demo/streams/sw_h264.asf", "LIVE STREAM", options);
vlc.playlist.playItem(id);
}
$(document).ready(function() {
registerVideoContainer();
var zoomFactor = 0;
var relativeStartPosition = function() {
this.X = 0;
this.Y = 0;
};
var relativePosition = function() {
this.X = 0;
this.Y = 0;
};
var deltaPosition = function() {
this.X = 0;
this.Y = 0;
};
var hasClickMovement = false;
var leftSingleClick = false;
var leftDoubleClick = false;
var leftTripleClick = false;
var rightSingleClick = false;
var rightDoubleClick = false;
var rightTripleClick = false;
var parentOffset;
var clickCount = 0;
var longClick = false;
var isMouseDown = false;
var clickCounter = {
increment: function (mouseButton) {
if (clickCounter._timeout != null)
clearTimeout(clickCounter._timeout);
if (clickCounter.mouseButton != mouseButton) {
clickCounter.count = 0;
clickCounter.mouseButton = mouseButton;
}
clickCounter.count++;
clickCounter._timeout = setTimeout(clickCounter.reset, 200);
},
reset: function () {
clearTimeout(clickCounter._timeout);
clickCounter._timeout = null;
if (clickCounter.count > 0) {
// TODO: Raise event.
//console.log("raise event");
//console.log(clickCounter.target);
$(clickCounter.target).trigger("clickCountEvent",
{ count: clickCounter.count, which: clickCounter.mouseButton });
}
clickCounter.count = 0;
clickCounter.mouseButton = null;
},
target: null,
_timeout: null,
mouseButton: null,
count: 0
};
clickCounter.target = $("#mouseLayer").get(0);
console.log("test:" + clickCounter.target);
//
// hook into the following action end events
//
function endCurrentAction() {
deltaPosition.X = 0;
deltaPosition.Y = 0;
if (leftSingleClick & (hasClickMovement && stopEventWithMovement["leftSingleClick"])) {
// finish single-click with movement
clickCounter.count = 0;
$("#deltaDisplay").html("Left Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
} else if (leftDoubleClick & (hasClickMovement && stopEventWithMovement["leftDoubleClick"])) {
// finish double-click with movement
$("#deltaDisplay").html("Left Double-Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
} else if (leftTripleClick & (hasClickMovement && stopEventWithMovement["leftTripleClick"])) {
// finish triple-click with movement
clickCounter.count = 0;
$("#deltaDisplay").html("Left Triple-Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
}
if (leftSingleClick & !stopEventWithMovement["leftSingleClick"]) {
// finish double-click with no movement
clickCounter.count = 0;
$("#deltaDisplay").html("Left Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
} else if (leftDoubleClick & !stopEventWithMovement["leftDoubleClick"]) {
// finish double-click with no movement
clickCounter.count = 0;
$("#deltaDisplay").html("Left Double-Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
} else if (leftTripleClick & !stopEventWithMovement["leftTripleClick"]) {
// finish triple-click with no movement
clickCounter.count = 0;
$("#deltaDisplay").html("Left Triple-Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
}
if (rightTripleClick & (hasClickMovement && stopEventWithMovement["rightTripleCLick"])) {
$("#deltaDisplay").html("Right Triple-Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
} else if (rightDoubleClick & (hasClickMovement && stopEventWithMovement["rightDoubleClick"])) {
$("#deltaDisplay").html("Right Double-Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
} else if (rightSingleClick & (hasClickMovement && stopEventWithMovement["rightSingleClick"])) {
$("#deltaDisplay").html("Right Click Stopped: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
}
leftSingleClick = false;
leftDoubleClick = false;
leftTripleClick = false;
rightSingleClick = false;
rightDoubleClick = false;
}
// disable the browser's context menu for right-clickability
$(document).bind("contextmenu", function(e) {
return false;
});
$("#mouseLayer").mousemove(function (e) {
$('body').css('cursor', 'crosshair');
parentOffset = $(this).parent().offset();
//offset -> method allows you to retrieve the current
// position of an element 'relative' to the document
relativeStartPosition.X = (e.pageX - parentOffset.left);
relativeStartPosition.Y = (e.pageY - parentOffset.top);
deltaPosition.X = relativeStartPosition.X - relativePosition.X;
deltaPosition.Y = relativePosition.Y - relativeStartPosition.Y;
if (deltaPosition.X != 0 | deltaPosition.Y != 0) {
//
// add hooks here on mouse-move for each button click type, capturing the delta positions
//
if (leftSingleClick) {
hasClickMovement = true;
$('body').css('cursor', 'move');
$("#deltaDisplay").html("Delta: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
$("#movement").html(" !!!").css("display", "inline").fadeOut("slow");
} else if (leftDoubleClick) {
hasClickMovement = true;
$("#deltaDisplay").html("Delta: (" + deltaPosition.X + ", " + deltaPosition.Y + ")");
$("#movement").html(" !!!").css("display", "inline").fadeOut("slow");
} else if (leftTripleClick) {
hasClickMovement = true;
$("#deltaDisplay").html("Delta: (" + deltaPosition.X + ", " + deltaPosition.Y + ")");
$("#movement").html(" !!!").css("display", "inline").fadeOut("slow");
} else if (rightSingleClick) {
hasClickMovement = true;
$("#deltaDisplay").html("Delta: (" + deltaPosition.X + ", " + deltaPosition.Y + ")");
$("#movement").html(" !!!").css("display", "inline").fadeOut("slow");
} else if (rightDoubleClick) {
hasClickMovement = true;
$("#deltaDisplay").html("Delta: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
$("#movement").html(" !!!").css("display", "inline").fadeOut("slow");
} else if (rightTripleClick) {
hasClickMovement = true;
$("#deltaDisplay").html("Delta: (" +
deltaPosition.X + ", " + deltaPosition.Y + ")");
$("#movement").html(" !!!").css(
"display", "inline").fadeOut("slow");
} else {
relativePosition.X = 0;
relativePosition.Y = 0;
//relativeStartPosition.X = 0;
//relativeStartPosition.Y = 0;
}
}
$("#position").html("<p><strong>X-Position: </strong>" +
relativeStartPosition.X + " | <strong>Y-Position: </strong>" +
relativeStartPosition.Y + "</p>");
})
.bind("clickCountEvent", function (e, data) {
//console.log("click count: " + data.count);
switch (data.which) {
case 1:
// left
// now if mouse is moved, return new relative positions
if (data.count == 1) { // left single-click
if (!leftSingleClick) {
relativePosition.X = relativeStartPosition.X;
relativePosition.Y = relativeStartPosition.Y;
}
if (isMouseDown) {
leftSingleClick = true;
leftDoubleClick = false;
leftTripleClick = false;
$("#clicked").html("left click");
}
} else if (data.count == 2) { // left double-click
if (!leftDoubleClick) {
relativePosition.X = relativeStartPosition.X;
relativePosition.Y = relativeStartPosition.Y;
}
if (isMouseDown) {
leftSingleClick = false;
leftDoubleClick = true;
leftTripleClick = false;
$("#clicked").html("double-left click");
$("#zoomFactor").html("Zoom In (click)").css(
"display", "inline").fadeOut("slow");
}
} else if (data.count == 3) { // left triple-click
if (!leftTripleClick) {
relativePosition.X = relativeStartPosition.X;
relativePosition.Y = relativeStartPosition.Y;
}
if (isMouseDown) {
leftSingleClick = false;
leftDoubleClick = false;
leftTripleClick = true;
$("#clicked").html("triple-left click");
}
}
break;
case 2:
// middle
$("#clicked").html("middle click");
break;
case 3:
// right
if (data.count == 1) {
// right single click
if (!rightSingleClick) {
relativePosition.X = relativeStartPosition.X;
relativePosition.Y = relativeStartPosition.Y;
}
if (isMouseDown) {
rightSingleClick = true;
rightDoubleClick = false;
rightTripleClick = false;
$("#clicked").html("right click");
}
} else if (data.count == 2) {
// right double click
if (!rightDoubleClick) {
relativePosition.X = relativeStartPosition.X;
relativePosition.Y = relativeStartPosition.Y;
}
if (isMouseDown) {
rightSingleClick = false;
rightDoubleClick = true;
rightTripleClick = false;
$("#clicked").html("double-right click");
$("#zoomFactor").html("Zoom Out (click)").css(
"display", "inline").fadeOut("slow");
}
} else if (data.count == 3) {
// right triple click
$("#clicked").html("triple-right click");
if (!rightDoubleClick) {
relativePosition.X = relativeStartPosition.X;
relativePosition.Y = relativeStartPosition.Y;
}
if (isMouseDown) {
rightSingleClick = false;
rightDoubleClick = false;
rightDoubleClick = true;
$("#clicked").html("triple-left click");
}
}
break;
default:
// error
$("#clicked").html("unsupported mouse: " + e.which);
}
})
.mousedown(function (e) {
isMouseDown = true;
clickCounter.increment(e.which);
})
.mouseout(function () {
clickCounter.count = 0;
isMouseDown = false;
$("#position").html("<p><strong>X-Position: </strong>" +
relativeStartPosition.X + " | <strong>Y-Position: </strong>" +
relativeStartPosition.Y + "</p>");
$('body').css('cursor', 'default');
if ((deltaPosition.X != 0 && deltaPosition.Y != 0) |
(!isNaN(deltaPosition.X) && !isNaN(deltaPosition.Y))) {
//console.log(deltaPosition.X + ", " + deltaPosition.Y);
endCurrentAction();
}
})
.mouseup(function (event) {
isMouseDown = false;
$("#clicked").html("");
$("#deltaDisplay").html("");
$('body').css('cursor', 'crosshair');
endCurrentAction();
hasClickMovement = false;
//console.log("Mouse up dPos: " + (deltaPosition.X + ", " + deltaPosition.Y));
});
$('#mouseLayer').bind('mousewheel', function (event, delta) {
if (event.originalEvent.wheelDelta > 0) {
$("#zoomFactor").html("Zoom In").css(
"display", "inline").fadeOut("slow");
} else {
$("#zoomFactor").html("Zoom Out").css(
"display", "inline").fadeOut("slow");
}
return false;
});
});
代码解释
在 #mouseLayer
中,我们 .bind()
clickCountEvent
函数,该函数通过 switch()
语句响应适当的 data.count
。
单击次数由 clickCounter
管理,单击次数的输出由单击之间的延迟决定,在本例中为 200 毫秒:
clickCounter._timeout = setTimeout(clickCounter.reset, 200);
在每个 switch 分支中,我们开始跟踪按钮按下期间的 delta X/Y。 通过这些值(可以想象成点击时创建的新向量),你可以为你要控制的任何内容设置一个加速因子。
你可能还需要为你的设备发送一个“停止”事件。这是通过释放按钮点击来实现的。
你可以在文件头定义的 stopEventWithMovement[whichButtonClickType]={bool}
数组中选择是否引发停止事件(因为如果鼠标没有移动,你可能不需要它)。
样式
以下是我设置激活 DIV 的 CSS 的方式:
/* positioning is relative to the container */
#mouseLayer{
position: absolute;
top:10px;
left:10px;
height: calc(90% - 20px);
width: calc(90% - 10px);
/*height: auto;*/
/*min-height:480px;*/
margin:0px auto;
overflow-y: auto;
z-index:1000;
text-align:left;
padding:15px;
border:1px dashed #333;
/*background-color:#0000ee;*/
background-color:rgba(255,0,0,0.05);
padding-top: 30px;
overflow: hidden;
}
关注点
要查看事件激活时触发的诊断代码,请参阅 http://www.whatsinyourlunch.com/capturing-client-side-mouse-events-with-javascript-jquery/。 在这里,我将更详细地介绍如何跟踪某些条件 mouseUp
事件 以及如何设置参数。
你可以通过简单地在 switch 语句中添加新的可触发值来设置 N 次单击的事件。因此,你可以触发 10 次左键单击、30 次右键单击等事件。
历史
没有重大历史记录。