最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - Javascript, MouseEvents and Classes - Stack Overflow

programmeradmin4浏览0评论

With all the buzz of HTML5 I've began by investigating the Canvas's capabilities along with interaction from Javascript. Unfortunately things haven't been going well due to idiosyncrasies of Javascript and its OO model.

For instance, I figured I could create a wrapper class for my canvas object and effectively box all appropriate methods and properties into it making the development side of things much easier. Unfortunately I'm struggling with the way the mouse handlers are working. In my case, I have the 'DrawArea' class that adds three mouse handlers for drawing rectangles and a 'Draw' routine titled 'Invalidate'. When the mouse events are fired (mouseMove and mouseUp methods), they fail claiming that the 'Invalidate' function is invalid - almost like it is out of context of the method it is being called within. Code below.

<script type="text/javascript" src=".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 pletion 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 code...

<body>
    <div id="DrawContainer">        
        <canvas id="cDrawArea" width="800" height="600"></canvas>
    </div>
</body>

What am I missing here and is this a particular efficient and smart way of handling plex objects that will require a lot of behind the scenes code?

With all the buzz of HTML5 I've began by investigating the Canvas's capabilities along with interaction from Javascript. Unfortunately things haven't been going well due to idiosyncrasies of Javascript and its OO model.

For instance, I figured I could create a wrapper class for my canvas object and effectively box all appropriate methods and properties into it making the development side of things much easier. Unfortunately I'm struggling with the way the mouse handlers are working. In my case, I have the 'DrawArea' class that adds three mouse handlers for drawing rectangles and a 'Draw' routine titled 'Invalidate'. When the mouse events are fired (mouseMove and mouseUp methods), they fail claiming that the 'Invalidate' function is invalid - almost like it is out of context of the method it is being called within. Code below.

<script type="text/javascript" src="http://ajax.googleapis./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 pletion 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 code...

<body>
    <div id="DrawContainer">        
        <canvas id="cDrawArea" width="800" height="600"></canvas>
    </div>
</body>

What am I missing here and is this a particular efficient and smart way of handling plex objects that will require a lot of behind the scenes code?

Share Improve this question edited Oct 21, 2019 at 18:58 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Nov 16, 2011 at 15:41 SeanCocteauSeanCocteau 1,86619 silver badges25 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

This is a mon misunderstanding. JavaScript doesn't have classes, and it doesn't have methods. It has functions. Unlike some other languages (Java, C#, C++), this is determined entirely by how a function is called, not where a function is defined. (This is incredibly powerful, but surprising to someone ing from class-based languages.) So this line of code:

this.SrcArea.addEventListener('mousedown', this.mouseDown, false);

...does hook up the function referenced by the mouseDown property, but does nothing to ensure that when that function is called, this is the value you expect.

If you're really using an ECMAScript5-pliant browser (there are some that have canvas but are not pletely ES5-pliant), you can use the new Function#bind feature, but again note that this is only about two years old:

// 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();
}

Alternately, you can do pretty much the same thing yourself using closures:

// 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();
}

More reading:

  • Mythical methods
  • You must remember this
  • Closures are not plicated

Try:

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

The "bind()" method on the Function prototype (should be in any browser with <canvas> I think) returns a function that will force the this value to be the parameter you pass, in this case your wrapper object instance.

If you don't do something like that, then the handler won't have the this you expect.

The this is not the DrawArea instance in the handler, but the element itself.

You should bind (freeze) the this value with bind. This is the easiest, but is not available in all browsers. There is a shim available, though.

//                                   guarantee the 'this' value inside handler
this.SrcArea.addEventListener('mousedown', this.mouseDown.bind(this), false);

http://jsfiddle/KdnZC/

发布评论

评论列表(0)

  1. 暂无评论