1
\$\begingroup\$

I've written a quick directive that provides two features:

  • Clicking on an empty area of the canvas will allow a 'drag and draw' function
  • Clicking on a an existing rectangle on the canvas will allow a 'drag and drop' function.

The primary thing I would like feedback on is whether my method for drawing and redrawing the canvas objects is the right way to do this.

Essentially, I'm redrawing the entire canvas and all of its contained objects, every time the canvas changes (eg a shape is moved or resized).

Here's the code for the directive, and I'll put comments in the parts I'm more interested in feedback on:

app.directive('mycanvas', function($document){
    return {

        link: function(scope, element, attr){
            var gX = element[0].clientWidth, gY = element[0].clientHeight; 

            var ctx = element[0].getContext('2d'); 
            ctx.fillStyle = "rgb(100,0,0)";         

            var mouseDown = false;      //Physical mouse press down         
            var hasMouseover = false;   //Mouse is over an already drawn node

            var objs = [];

            var currentObj = {};

            function drawRect(r){
                ctx.fillRect(r.startX, r.startY, r.endX-r.startX,   r.endY-r.startY);
            }


            //??Is this the best way to do this? 
            function isWithinBounds(obj, x,y){    
                var oXmin, oXmax, oYmin, oYmax;
                if (obj.startX < obj.endX){
                    oXmin = obj.startX;
                    oXmax = obj.endX; 
                }
                else {
                    oXmin = obj.endX;
                    oXmax = obj.startX 
                }

                if (obj.startY < obj.endY){
                    oYmin = obj.startY;
                    oYmax = obj.endY;
                }
                else{
                    oYmin = obj.endY; 
                    oYmax = obj.startY; 
                }

                return (x > oXmin && x < oXmax && y > oYmin && y < oYmax);

            }

            //?? Redraws the entire thing. 
            //?? Would it be better to have a 'temporary' layer (ie a second canvas), to avoid having the redraw the inplace objects? 
            function redraw(){
                ctx.clearRect(0, 0, gX, gY); 

                angular.forEach(objs, function(obj){
                    drawRect(obj); 
                });

                drawRect(currentObj); 
            }

            element.on('mousedown', function(event){
                event.preventDefault();

                if (hasMouseover){
                    //clickX/clickY - the position of the original click event, for the purposes of calculating the new drag position
                    currentObj.clickX = event.clientX-8;
                    currentObj.clickY = event.clientY-8;

                    objs = _.reject(objs, currentObj); 
                }
                else{
                    currentObj = {};
                    currentObj.startX = event.clientX-8; 
                    currentObj.startY = event.clientY-8; 
                }


                mouseDown = true; 
            }); 

            element.on('mouseup', function(event){
                event.preventDefault(); 
                objs.push(currentObj); 
                mouseDown = false; 
                //redraw();

            });

            element.on('mousemove', function(event){
                event.preventDefault();

                if (mouseDown){
                    if (hasMouseover){
                        //drag the object
                        var cx =event.clientX-8 , cy = event.clientY-8; 

                        var dx = cx - currentObj.clickX, dy = cy - currentObj.clickY; 

                        currentObj.clickX = cx;
                        currentObj.clickY = cy; 

                        currentObj.startX += dx; 
                        currentObj.endX += dx;
                        currentObj.startY += dy; 
                        currentObj.endY += dy; 

                    }
                    else{
                        //resize new object
                        currentObj.endX = event.clientX-8; 
                        currentObj.endY = event.clientY-8;
                    }
                    redraw();
                }

                else{
                    //Non-clicked mouse move - detect for mouse over only. 

                    var cx =event.clientX-8 , cy = event.clientY-8; 

                    if (hasMouseover){ //check current object first
                        if (isWithinBounds(currentObj, cx, cy)) return;     //break straight away
                    }

                    hasMouseover = false;

                    //Detect mouseover                  
                    for (var i = 0; i< objs.length; i++){
                        var obj = objs[i]; 
                        if (isWithinBounds(obj, cx, cy)){
                            hasMouseover = true;                            
                            currentObj = obj; 

                            break; 
                        }
                    }



                    if (hasMouseover) {
                        element.css({cursor: 'pointer'});
                    }
                    else{
                        element.css({cursor: 'auto'});
                    }                   
                }               
            }); 

        }

    };
});

There are those magic numbers everywhere (-8). Please ignore, I plan to tidy that up, for now I'm just wondering about the overall technique of drawing this canvas.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.