随着HTML5的轰动,我开始研究Canvas的功能以及来自Javascript的交互。不幸的是,由于Javascript及其OO模型的特性,事情进展不顺利。
例如,我想我可以为canvas对象创建一个包装类,并有效地将所有适当的方法和属性包含在其中,使事情的开发方面变得更加容易。不幸的是,我正在努力解决鼠标处理程序的工作方式。在我的例子中,我有'DrawArea'类,它添加了三个用于绘制矩形的鼠标处理程序和一个标题为'Invalidate'的'Draw'程序。当触发鼠标事件(mouseMove和mouseUp方法)时,它们无法声称'Invalidate'函数无效 - 几乎就像它在被调用的方法的上下文之外一样。代码如下。
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
<script type="text/javascript">
// Top level variables
var dWrap;
// Point Class
function Point( xPos , yPos ){
this.X = xPos;
this.Y = yPos;
}
// Create wrapper class for the draw area
function DrawArea( da ){
this.SrcArea = da;
// Add mouse handlers
this.SrcArea.addEventListener('mousedown', this.mouseDown, false);
this.SrcArea.addEventListener('mousemove', this.mouseMove, false);
this.SrcArea.addEventListener('mouseup', this.mouseUp, false);
// And draw
// NOTE: this call works!
this.Invalidate();
}
// Properities
DrawArea.prototype.ProposedStartPos = undefined;
DrawArea.prototype.ProposedEndPos = undefined;
DrawArea.prototype.IsDrawing = false;
// Mouse Events
// Handles the mouse down event for new objects
DrawArea.prototype.mouseDown = function(m) {
// Flag as drawing
this.IsDrawing = true;
// Record the start position
this.ProposedStartPos = new Point(m.layerX, m.layerY);
}
// Handles mouse movement when creating a proposed object
DrawArea.prototype.mouseMove = function(m) {
if (this.IsDrawing) {
// Set the current end position
this.ProposedEndPos = new Point(m.layerX, m.layerY);
// NOTE: this call doesn't work!
this.Invalidate();
}
}
// Handles the completion of a proposed object
DrawArea.prototype.mouseUp = function(m) {
if (this.IsDrawing) {
// Set the final end position
if (m.type != 'mouseout') this.ProposedEndPos = new Point(m.layerX, m.layerY);
// NOTE: this call doesn't work!
this.Invalidate();
}
}
// Redraws the source object
DrawArea.prototype.Invalidate = function() {
// Obtain
if (this.SrcArea.getContext) {
var context = this.SrcArea.getContext('2d');
// Clean up
context.clearRect(0, 0, this.SrcArea.width, this.SrcArea.height);
context.save();
// Draw the background
context.strokeStyle = "#000000";
context.fillStyle = "#AAAFFF";
context.beginPath();
context.rect(0, 0, this.SrcArea.width, this.SrcArea.height);
context.closePath();
context.stroke();
context.fill();
// Are we drawing any proposed items
if (this.IsDrawing) {
context.strokeStyle = this.ProposedColorStroke;
context.fillStyle = this.ProposedColorFill;
context.beginPath();
context.rect(this.ProposedStartPos.X, this.ProposedStartPos.Y, this.ProposedEndPos.X - this.ProposedStartPos.X, this.ProposedEndPos.Y - this.ProposedStartPos.Y);
context.closePath();
context.stroke();
context.fill();
}
}
// Flush
context.restore();
}
// Initialise the wrapper class
$(document).ready(function() {
// Obtain the canvas and set
var cWrap = $('#cDrawArea')[0];
dWrap = new DrawArea( cWrap );
});
Html代码......
<body>
<div id="DrawContainer">
<canvas id="cDrawArea" width="800" height="600"></canvas>
</div>
</body>
我在这里缺少什么,这是一种处理复杂对象的特别有效和智能的方法,需要大量的幕后代码?提前谢谢。
答案 0 :(得分:3)
这是一种常见的误解。 JavaScript没有类,也没有方法。它有功能。与其他一些语言(Java,C#,C ++)不同,this
完全由如何调用函数决定,而不是定义函数。 (这非常强大,但对于来自基于类的语言的人来说却令人惊讶。)所以这行代码:
this.SrcArea.addEventListener('mousedown', this.mouseDown, false);
...会挂钩mouseDown
属性引用的函数,但不会确保在调用该函数时,this
是您期望的值。
如果您真的使用符合ECMAScript5标准的浏览器(有些浏览器有canvas
但不完全符合ES5标准),您可以使用新的Function#bind
功能,但请注意,这只有两年左右的时间:
// Create wrapper class for the draw area
function DrawArea( da ){
this.SrcArea = da;
// Add mouse handlers using ECMAScript5's new `Function#bind`
this.SrcArea.addEventListener('mousedown', this.mouseDown.bind(this), false);
this.SrcArea.addEventListener('mousemove', this.mouseMove.bind(this), false);
this.SrcArea.addEventListener('mouseup', this.mouseUp.bind(this), false);
// And draw
// NOTE: this call works!
this.Invalidate();
}
或者,您可以使用闭包自己做同样的事情:
// Create wrapper class for the draw area
function DrawArea( da ){
var self = this; // Set up a variable referencing the instance
this.SrcArea = da;
// Add mouse handlers - these are closures over the context of this
// call to the constructor, and have access to the `self` variable
// above. They just relay the call to the functions on the prototype,
// but in a way that ensures that `this` is what you expect.
this.SrcArea.addEventListener('mousedown', function(event) {
return self.mouseDown(event);
}, false);
this.SrcArea.addEventListener('mousemove', function(event) {
return self.mouseMove(event);
}, false);
this.SrcArea.addEventListener('mouseup', function(event) {
return self.mouseUp(event);
}, false);
// And draw
// NOTE: this call works!
this.Invalidate();
}
更多阅读:
答案 1 :(得分:1)
尝试:
this.SrcArea.addEventListener('mousedown', this.mouseDown.bind(this), false);
this.SrcArea.addEventListener('mousemove', this.mouseMove.bind(this), false);
this.SrcArea.addEventListener('mouseup', this.mouseUp.bind(this), false);
Function原型上的“bind()”方法(应该在<canvas>
我认为的任何浏览器中)返回一个函数,该函数将强制this
值作为您传递的参数,这种情况你的包装器对象实例。
如果你不做那样的事情,那么处理程序就没有你期望的this
。
答案 2 :(得分:1)
this
不是处理程序中的DrawArea
实例,而是元素本身。
您应该将this
值与bind
绑定(冻结)。这是最简单的,但并非在所有浏览器中都可用。但是有一个垫片。
// guarantee the 'this' value inside handler
this.SrcArea.addEventListener('mousedown', this.mouseDown.bind(this), false);