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

Desenvolvimento de programas de desenho simples com RequireJS

前言

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

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

开始

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

其中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'
  }
})

Principalmente configura o diretório raiz do projeto como 'js/diretório 'lib', configurando um caminho chamado 'app' com o valor '../diretório 'app', ou seja, 'js/diretório 'app'.

2. Escrever código de módulo

Os módulos principais deste projeto são os seguintes: point.js, line.js, rect.js, arc.js, utils.js, explicaremos um a um:

point.js:

O módulo point.js representa um ponto (x, y), o código é o seguinte:

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

O código acima define o módulo ponto usando define, retornando uma classe com dois parâmetros x, y e um método equals para comparar se dois pontos são iguais.
Para usar este módulo, podemos usar o seguinte código:

require(['app/point'], function(Point) {
  //Criar um objeto da classe ponto
  var point = new Point(3, 5);
})

Aqui é importante notar que o primeiro parâmetro da função require() é um array, o Point dentro da função de callback representa nossa classe de ponto, e os objetos da classe de ponto são criados de maneira new Point().

line.js:

O módulo line.js representa uma linha, o código é o seguinte:

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

A definição do módulo de linha é semelhante à do módulo de ponto, ambas retornam uma classe dentro da função de callback do define, a construtora da classe de linha possui dois parâmetros do tipo ponto, representando o ponto de início e o ponto final da linha, a classe de linha também possui um método drawMe, que recebe um objeto context para desenhar a linha.

rect.js:

O módulo rect.js representa um retângulo, o código é o seguinte:

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

onde startPoint é as coordenadas do canto superior esquerdo do retângulo, é uma classe point, width e height representam a largura e altura do retângulo, além disso, há um método drawMe que desenha o retângulo próprio.

arc.js:

O módulo arc.js representa um círculo, o código é o seguinte:

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

onde startPoint representa as coordenadas do canto superior esquerdo do retângulo onde está o círculo, radius representa o raio do círculo, o método drawMe é o método para desenhar o círculo.
Nos módulos acima, as classes de linha, retângulo e círculo contêm o método drawMe(), aqui envolve conhecimento de desenho no canvas, se houver alguma dúvida, consulte o documento: HTML 5 Manual de Referência do Canvas

utils.js

O módulo utils.js é usado principalmente para lidar com classes de ferramentas de desenho de gráficos variados, incluindo o desenho de linhas, retângulos e círculos, bem como o registro de traçados de desenho e a limpeza de traçados, o código é o seguinte:

/** ferramentas de gestão de desenho */
define(function() { 
  var historia = []; //vetor de histórico para salvar registros de desenhos, contendo objetos do tipo linha, retângulo ou círculo
  function desenharLinha(contexto, linha) {
    linha.desenhar(contexto);
  }
  function desenharRetangulo(contexto, retangulo) {
    rect.desenharMe(context);
  }
  function drawArc(context, arc) {
    arc.desenharMe(context);
  }
  /** adicionar um traçado de desenho */
  function addHistory(item) {
    histórico.push(item);
  }
  /** desenhar o traçado histórico */
  function drawHistory(context) {
    for(var i = 0; i < histórico.length; i++) {
      var obj = histórico[i];
      obj.desenharMe(context);      
    }
  }
  /** limpar o traçado histórico */
  function clearHistory() {
    histórico = [];
  }
  return {
    drawLine: drawLine,
    drawRect: drawRect,
    drawArc: drawArc,
    addHistory: addHistory,
    drawHistory: drawHistory,
    clearHistory: clearHistory
  };
})

III. Escrever código da interface, lidar com eventos do mouse

Acima, todos os módulos do programa de desenho simples foram definidos. Durante o desenho dos gráficos, são usados apenas os módulos acima. Agora, vamos começar a escrever o código da interface principal. A interface principal contém quatro botões e um grande canvas para desenhar. Abaixo está o código do arquivo index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Programa de Desenho Simples</title>
  <style type="text/css">
    canvas {
      fundo-cor: #ECECEC;
      cursor: padrão; /** configurar o cursor do mouse para o padrão */
    }
    .tool-bar {
      margem-bottom: 10px;
    }
  </style>
</head>
<body>
  <div class="tool-bar">
    <button id="btn-line">desenhar linha</>botão>
    <button id="btn-rect">desenhar retângulo</>botão>
    <button id="btn-oval">desenhar círculo</>botão>
    <button id="btn-clear">limpar o canvas</>botão>
    <span id="hint" style="color: vermelho;">Operação atual: desenhar linha</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(); //得到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, 2分别代表画直线、画矩形、画圆
      var downPoint = new Point(0, 0), 
        movePoint = new Point(0, 0), 
        upPoint = new Point(0, 0);
      var line;
      var rect;
      var arc;
      /** 处理鼠标按下的事件 */
      function handleMouseDown(event) {
        downPoint.x = event.clientX - canvasRect.left;
        if(selection === 0) { - canvasRect.top;
        line = new Line(downPoint, downPoint); 
          line.startPoint = downPoint;
          rect = new Rect(new Point(downPoint.x, downPoint.y), 0, 0);
        } 1) {
          else if(selection ===
        } 2) {
          arc = new Arc(new Point(downPoint.x, downPoint.y), 0);
        }
        mouseDown = true;
      }
      /** Tratar o evento de mover o mouse */
      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);
        }
      }
      /** Tratar o evento de levantar o mouse */
      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);
        }
      }
      /** Limpar o quadro */
      function clearCanvas() {
        context.clearRect(0, 0, canvas.width, canvas.height);
      }
      /** 事件处理菜单按钮的点击 */
      function menuBtnClicked(event) {
        var domID = event.srcElement.id;
        if(domID === 'btn-clear') {
          clearCanvas();
          Utils.clearHistory();
        } else if(domID == 'btn-line') {
          selection = 0;
          showHint('当前操作:画直线');
        } else if(domID == 'btn-rect') {
          selection = 1;
          showHint('当前操作:画矩形');
        } else if(domID == 'btn-oval') {
          selection = 2;
          showHint('当前操作:画圆形');
        }
      }
      function showHint(msg) {
        document.getElementById('hint').innerHTML = msg;
      }
      /** Ligar o evento de clique ao elemento DOM correspondente pelo id */
      function bindClick(domID, handler) {
        document.getElementById(domID).addEventListener('click', handler);
      }
    });
  </script>
</body>
</html>

O arquivo index.html contém muito código, mas o código mais importante é o monitoramento e o processamento dos eventos de clique, movimento e soltura do mouse, além disso, ao obter a posição do mouse no canvas, é necessário prestar atenção: pois os valores clientX e clientY obtidos no objeto event são as coordenadas do mouse em relação à página, para obter as coordenadas do mouse no canvas, é necessário obter o retângulo de área do canvas, e usar clientX-canvas.left, clientY-canvas.top, para obter a posição do mouse no canvas.

Código-fonte

O código-fonte deste blog foi hospedado no github, clique aqui para visualizarCódigo-fonte

Bug conhecido

Ao desenhar um círculo, é necessário arrastar o mouse do canto superior esquerdo para o canto inferior direito para desenhar o círculo. Caso contrário, a posição do círculo estará errada.

Isso é tudo sobre o conteúdo deste artigo, esperamos que ajude no seu aprendizado e que você apoie mais o tutorial de clamor.

Declaração: o conteúdo deste artigo é extraído da Internet, pertence ao respectivo proprietário, foi contribuído e carregado voluntariamente pelos usuários da Internet, o site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidades legais relacionadas. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie e-mail para: notice#oldtoolbag.com (ao enviar e-mail, troque # por @ para denunciar e forneça provas relevantes. Caso seja confirmado, o site deletará imediatamente o conteúdo suspeito de violação de direitos autorais.)

Você também pode gostar