English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Simple drawing program development with RequireJS

Introduction

RequireJS的出现让前端代码模块化变得容易,当前端项目越来越大,代码越来越多的时候,模块化代码让项目结构更清晰,不仅在开发时让我们的思路更清晰,而且后期维护起来也更容易。下面是我学习RequireJS后使用RequireJS开发的一款简易绘图程序,运行在浏览器中如下图所示:

如果你对RequireJS还不是很了解,可以看我的RequireJS学习笔记:http://blog.csdn.net/yubo_725/article/details/52913853

Start

这个简易绘图程序的项目结构如下图所示:

其中index.html是项目的主页,js目录下存放所有js文件,js/app目录为我们自定义的模块文件,js/lib目录中暂时没有文件,当我们的项目里用到一些其他前端框架如jquery等时,js/lib目录就存放这些框架的js文件,js/main.js为requirejs的配置文件,主要是配置一些路径,js/require.min.js是RequireJS框架的文件。下面请跟我一步一步完成这个简易的绘图程序吧!

一、配置requirejs

本项目的配置文件代码放在js/main.js中,代码内容如下:

require.config({
  baseUrl: 'js'/lib',
  paths: {
    app: '../app'
  }
})

It mainly configures the root directory of the project as 'js/lib', then configured a path named 'app', the path is '../app' means 'js/app' directory.

Second, write module code

The modules in this project mainly include: point.js, line.js, rect.js, arc.js, utils.js. They are explained one by one below:

point.js:

The point.js module represents a point (x, y), the code is as follows:

/** point */
define(function() {
  return function(x, y) {
    this.x = x;
    this.y = y;
    this.equals = function(point) {
      return this.x === point.x && this.y === point.y;
    };
  };
})

The above code uses define to define the point module, and returns a class in the callback function. This class has two parameters x, y, and an equals method to compare whether two points are equal.
To use this module, we can use the following code:

require(['app/point'], function(Point) {
  //Create an object of the point class
  var point = new Point(3, 5);
})

It is important to note that the first parameter of the require() function is an array, and the Point in the callback function represents our point class. Points of the point class are created in the way of new Point().

line.js:

The line.js module represents a straight line, the code is as follows:

/** line */
define(function() {
  return function(startPoint, endPoint) {
    this.startPoint = startPoint;
    this.endPoint = endPoint;
    this.drawMe = function(context) {
      context.strokeStyle = "#000000";
      context.beginPath();
      context.moveTo(this.startPoint.x, this.startPoint.y);
      context.lineTo(this.endPoint.x, this.endPoint.y);
      context.closePath();
      context.stroke();
    }
  }
})

The definition of the line module is similar to that of the point module, which returns a class in the callback function of define. The constructor of the line class has two parameters of the point class, representing the starting point and the end point of the line. The line class also has a drawMe method, which takes a context object as an argument to draw itself.

rect.js:

The rect.js module represents a rectangle, the code is as follows:

/** rectangle */
define(['app/point'], function() {
  return function(startPoint, width, height) {
    this.startPoint = startPoint;
    this.width = width;
    this.height = height;
    this.drawMe = function(context) {
      context.strokeStyle = "#000000";
      context.strokeRect(this.startPoint.x, this.startPoint.y, this.width, this.height);
    }
  }
})

Among them, startPoint is the coordinates of the top-left point of the rectangle, which is a point class, and width and height represent the width and height of the rectangle, respectively. There is also a drawMe method to draw the rectangle itself.

arc.js:

The arc.js module represents a circle, the code is as follows:

/** Circle */
define(function() {
  return function(startPoint, radius) {
    this.startPoint = startPoint;
    this.radius = radius;
    this.drawMe = function(context) {
      context.beginPath();
      context.arc(this.startPoint.x, this.startPoint.y, this.radius, 0, 2 * Math.PI);
      context.closePath();
      context.stroke();
    }
  }
})

Among them, startPoint represents the coordinates of the top-left point of the rectangle where the circle is located, and radius represents the radius of the circle. The drawMe() method is the method to draw the circle.
In the above several modules, the line, rectangle, and circle classes all contain a drawMe() method, which involves knowledge of canvas drawing. If there is anything unclear, you can check the documentation: HTML 5 Canvas Reference Manual

utils.js

The utils.js module is mainly used for various graphic drawing utility classes, including the drawing of lines, rectangles, and circles, as well as recording drawing tracks and clearing drawing tracks. The code is as follows:

/** Drawing management tools */
define(function() { 
  var history = []; //An array used to save the historical drawing records, storing objects of line, rectangle, or circle classes
  function drawLine(context, line) {
    line.drawMe(context);
  }
  function drawRect(context, rect) {
    rect.drawMe(context);
  }
  function drawArc(context, arc) {
    arc.drawMe(context);
  }
  /** Add a drawing track */
  function addHistory(item) {
    history.push(item);
  }
  /** Draw the historical track */
  function drawHistory(context) {
    for(var i = 0; i < history.length; i++) {
      var obj = history[i];
      obj.drawMe(context);      
    }
  }
  /** Clear the historical track */
  function clearHistory() {
    history = [];
  }
  return {
    drawLine: drawLine,
    drawRect: drawRect,
    drawArc: drawArc,
    addHistory: addHistory,
    drawHistory: drawHistory,
    clearHistory: clearHistory
  };
})

3. Write interface code, handle mouse events

All modules for this simple drawing program have been defined above. The modules mentioned above are used when drawing graphics. Now, let's start writing the code for the main interface. The main interface includes four buttons and a large canvas for drawing. Below is the code from the index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Simple Drawing Program</title>
  <style type="text/css">
    canvas {
      background-color: #ECECEC;
      cursor: default; /** Set the mouse cursor to the default pointer */
    }
    .tool-bar {
      margin-bottom: 10px;
    }
  </style>
</head>
<body>
  <div class="tool-bar">
    <button id="btn-line">Draw a line</button>
    <button id="btn-rect">Draw a rectangle</button>
    <button id="btn-oval">Draw a circle</button>
    <button id="btn-clear">Clear canvas</button>
    <span id="hint" style="color: red;">Current operation: Draw a line</span>
  </div>
  <canvas id="canvas" width="800" height="600"></canvas>
  <script type="text/javascript" src="js/require.min.js" data-main="js/main"></script>
  <script type="text/javascript">
    require(['app/point', 'app/line', 'app/rect', 'app/arc', 'app/utils], 
      function(Point, Line, Rect, Arc, Utils) {
      var canvas = document.getElementById("canvas");
      var context = canvas.getContext('2d');
      var canvasRect = canvas.getBoundingClientRect(); //Get the rectangle of the canvas
      canvas.addEventListener('mousedown', handleMouseDown);
      canvas.addEventListener('mousemove', handleMouseMove);
      canvas.addEventListener('mouseup', handleMouseUp);
      bindClick('btn-clear', menuBtnClicked);
      bindClick('btn-line', menuBtnClicked);
      bindClick('btn-rect', menuBtnClicked);
      bindClick('btn-oval', menuBtnClicked);
      var mouseDown = false; 
      var selection = 1; // 0, 1, 2representing drawing a line, rectangle, and circle
      var downPoint = new Point(0, 0), 
        movePoint = new Point(0, 0), 
        upPoint = new Point(0, 0);
      var line;
      var rect;
      var arc;
      /** Handle the mouse down event */
      function handleMouseDown(event) {
        downPoint.x = event.clientX - canvasRect.left;
        downPoint.y = event.clientY - canvasRect.top;
        if(selection === 0) { 
          line = new Line(downPoint, downPoint);
          line.startPoint = downPoint;
        } 1) {
          rect = new Rect(new Point(downPoint.x, downPoint.y), 0, 0);
        } 2) {
          arc = new Arc(new Point(downPoint.x, downPoint.y), 0);
        }
        mouseDown = true;
      }
      /** Handle the mouse move event */
      function handleMouseMove(event) {
        movePoint.x = event.clientX - canvasRect.left;
        movePoint.y = event.clientY - canvasRect.top;
        if(movePoint.x == downPoint.x && movePoint.y == downPoint.y) {
          return ;
        }
        if(movePoint.x == upPoint.x && movePoint.y == upPoint.y) {
          return ;
        }
        if(mouseDown) {
          clearCanvas();
          if(selection == 0) {
            line.endPoint = movePoint; 
            Utils.drawLine(context, line);
          } else if(selection == 1) {
            rect.width = movePoint.x - downPoint.x;
            rect.height = movePoint.y - downPoint.y;
            Utils.drawRect(context, rect);
          } else if(selection == 2) {
            var x = movePoint.x - downPoint.x;
            var y = movePoint.y - downPoint.y;
            arc.radius = x > y &63; (y / 2) : (x / 2);
            if(arc.radius < 0) { 
              arc.radius = -arc.radius;
            }
            arc.startPoint.x = downPoint.x + arc.radius;
            arc.startPoint.y = downPoint.y + arc.radius;
            Utils.drawArc(context, arc);
          }
          Utils.drawHistory(context);
        }
      }
      /** Handle the mouse up event */
      function handleMouseUp(event) {
        upPoint.x = event.clientX - canvasRect.left;
        upPoint.y = event.clientY - canvasRect.top;
        if(mouseDown) {
          mouseDown = false;
          if(selection == 0) {
            line.endPoint = upPoint;  
            if(!downPoint.equals(upPoint)) {
              Utils.addHistory(new Line(new Point(downPoint.x, downPoint.y), 
                new Point(upPoint.x, upPoint.y))); 
            }  
          } else if(selection == 1) {
            rect.width = upPoint.x - downPoint.x;
            rect.height = upPoint.y - downPoint.y;
            Utils.addHistory(new Rect(new Point(downPoint.x, downPoint.y), rect.width, rect.height));
          } else if(selection == 2) {
            Utils.addHistory(new Arc(new Point(arc.startPoint.x, arc.startPoint.y), arc.radius));
          }
          clearCanvas();
          Utils.drawHistory(context);
        }
      }
      /** Clear canvas */
      function clearCanvas() {
        context.clearRect(0, 0, canvas.width, canvas.height);
      }
      /** Menu button click event handler */
      function menuBtnClicked(event) {
        var domID = event.srcElement.id;
        if(domID === 'btn-clear') {
          clearCanvas();
          Utils.clearHistory();
        } else if(domID == 'btn-line') {
          selection = 0;
          showHint('Current operation: Draw line');
        } else if(domID == 'btn-rect') {
          selection = 1;
          showHint('Current operation: Draw rectangle');
        } else if(domID == 'btn-oval') {
          selection = 2;
          showHint('Current operation: Draw circle');
        }
      }
      function showHint(msg) {
        document.getElementById('hint').innerHTML = msg;
      }
      /** Bind click event to the corresponding id dom element */
      function bindClick(domID, handler) {
        document.getElementById(domID).addEventListener('click', handler);
      }
    });
  </script>
</body>
</html>

There is a lot of code in the index.html file, but the main code is still the listening and processing of the three events: mouse down, move, and lift. In addition, attention should be paid to obtaining the coordinate position of the mouse in the canvas: since the clientX and clientY obtained from the event object are the coordinates of the mouse relative to the page, in order to obtain the coordinate position of the mouse in the canvas, it is necessary to obtain the rectangular area where the canvas is located, and then use clientX-canvas.left, clientY-canvas.top to get the position of the mouse in the canvas.

Source code

The source code in this blog has been hosted on github. Click here to viewSource code

Known bug

When drawing a circle, you need to drag the mouse from the upper left corner to the lower right corner to draw a circle. If it is not like this, the position of the circle will be wrong.

That's all for this article. I hope it will be helpful to everyone's learning and I also hope everyone will support the Yelling Tutorial more.

Statement: The content of this article is from the Internet, the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please feel free to send an email to: notice#w3If you find any infringement, please send an email to: notice#w with the # replaced by @ and provide relevant evidence. Once verified, this site will immediately delete the infringing content.

You May Also Like