Javascript,鼠标事件和类

时间:2011-11-16 15:41:47

标签: javascript html5 javascript-events html5-canvas

随着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>

我在这里缺少什么,这是一种处理复杂对象的特别有效和智能的方法,需要大量的幕后代码?提前谢谢。

3 个答案:

答案 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);

http://jsfiddle.net/KdnZC/