Desenho com SVG

Preâmbulo
O propósito da presente apostila (ou página) é desenvolver um código que desenha formas simples em linguagem SVG dinamicamente e quando dominar essa técnica aperfeiçoar o código com mais recursos.
Porque o Canvas do HTML5 não é Recomendado para Desenho Técnico?
O canvas é literalmente uma tela de pintura, os desenhos são produzidos através de imagens do tipo bitmaps que consistem em centenas de linhas e colunas de pequenos elementos. Cada elemento é armazenado individualmente pelo computador e chama-se pixel (abreviatura de picture element),  isto é, a imagem consiste numa matriz de pontos individuais (ou pixels) em que cada um tem a sua própria cor (descrita usando bits, a mais pequena unidade de informação para um computador).

O problema na criação de desenhos técnicos é na necessidade de alterar sua posição, cor, tamanho, escala, etc. Como no canvas um desenho se sobrepõe a outro, sua alteração requer um código complexo e inviável.
O recomentado para criação de desenhos técnicos é a linguagem vetorizada porque cada código corresponde a seu respectivo desenho, mudando o código muda também o desenho, assim a qualidade da imagem continua a mesma.

Linguagem Vetorial
SVG significa Scalable Vector Graphics, é uma tecnologia desenvolvida pelo World Wide Web Consortium (W3C: http://www.w3.org/), para criar gráficos vetoriais escaláveis, com base na linguagem XML.
Em termos de gráficos, scalable significa possibilidade de mudar de tamanho sem perder a forma e a qualidade da imagem.
Gráficos SVG são escaláveis em diferentes resoluções de display.
O memo gráfico SVG pode ser posto em tamanhos diferentes numa mesma página Web, e re-usado em tamanhos diferentes em páginas diferentes.
Gráfico SVG pode aumentar parte do desenho afim de formar uma vista detalhada.
SVG é uma linguagem para descrever vetores 2D e mesclar gráficos vector/raster em XML.

EVENTOS
Eventos ou ações podem ser disparados utilizando o método element.addEventListener() que possui três parâmetros. O evento ficará na escuta (listener), ou seja, poderá ser acionado várias vezes.
O método .removeEventListener() remove a escuta, assim o código HTML pode executar outro element.addEventListener(), lembrando que a execução do código HTML é de cima para baixo.

element.addEventListener(event, listener,options)

event
: click/dblclick, mousedown/onmousedown, etc.
listener
: nome da função que reponderá ao evento.
options: false ou true. Diz respeito às fases de um evento, capturing e bubbling. Alguns navegadores mais antigos são obrigados configurá-los. O padrão é false, ou seja, sem esta opçao.

Eventos com o Mouse em JavaScript
click/dblclick: quando é pressionado e liberado o botão primário do mouse com um clique/dois cliques.
mousedown/mouseup: pressionar/soltar o mouse.
mouseover/mouseout: quando o mouse está sobre algo/quando o mouse deixa de estar sobre algo.
mousemove: sempre que o cursor do mouse se move.

- Cada variável e cada função tevem ter exclusivamente um nome seu que os diferenciam das demais, essa regra é universal em programação, um nome deve ser único principalmente em JavaScript cuja variáveis não são tipadas. Use essa regra e evite problemas.- Variáveis dentro da função sem a declaração var são visíveis no escopo onde a função está.

The MouseEvent Object
Events that occur when the mouse interacts with the HTML document belongs to the MouseEvent Object.

Property/Method
Description
altKey Returns whether the "ALT" key was pressed when the mouse event was triggered
button Returns which mouse button was pressed when the mouse event was triggered
buttons Returns which mouse buttons were pressed when the mouse event was triggered
clientX Returns the horizontal coordinate of the mouse pointer, relative to the current window,
when the mouse event was triggered
clientY Returns the vertical coordinate of the mouse pointer, relative to the current window,
when the mouse event was triggered
ctrlKey Returns whether the "CTRL" key was pressed when the mouse event was triggered
getModifierState() Returns true if the specified key is activated
metaKey Returns whether the "META" key was pressed when an event was triggered
movementX Returns the horizontal coordinate of the mouse pointer relative to the position of the
last mousemove event
movementY Returns the vertical coordinate of the mouse pointer relative to the position of the
last mousemove event
offsetX Returns the horizontal coordinate of the mouse pointer relative to the position of the edge
of the target element
offsetY Returns the vertical coordinate of the mouse pointer relative to the position of the edge of
the target element
pageX Returns the horizontal coordinate of the mouse pointer, relative to the document, when the
mouse event was triggered
pageY Returns the vertical coordinate of the mouse pointer, relative to the document, when the mouse
event was triggered region
relatedTarget Returns the element related to the element that triggered the mouse event
screenX Returns the horizontal coordinate of the mouse pointer, relative to the screen, when an event
was triggered
screenY Returns the vertical coordinate of the mouse pointer, relative to the screen, when an event
was triggered
shiftKey Returns whether the "SHIFT" key was pressed when an event was triggered
which Returns which mouse button was pressed when the mouse event was triggered
Fonte: https://www.w3schools.com/jsref/obj_mouseevent.asp

mousedown
The event occurs when the user presses a mouse button over an element
MouseEvent
mouseenter The event occurs when the pointer is moved onto an element
MouseEvent
mouseleave The event occurs when the pointer is moved out of an element MouseEvent
mousemove The event occurs when the pointer is moving while it is over an element
MouseEvent
mouseover The event occurs when the pointer is moved onto an element, or onto one of its children MouseEvent
mouseout The event occurs when a user moves the mouse pointer out of an element, or out of one
of its children
MouseEvent
Fonte: https://www.w3schools.com/jsref/dom_obj_event.asp

Capturando as Coordenadas do Mouse
- Zeragem da Origem
As constantes clentX e clientY correspondem as coordenadas do mouse no window (janelaHTML = tela + barras), que varia conforme a barra de rolagem. Para que as coordenadas do <svg> corresponda a posição do mouse devemos subtrair sua posição no window, ou seja, a posição do mouse no canto superior esquerdo do <svg> deve ser (0,0).


Arquivo HTML com elemento SVG incorporado diretamente:
<!DOCTYPE html>
<html>
<body>

<h1>My first SVG</h1>

<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="510" height="510"
      style="border: 1px solid #000000"
      onclick="clique(evt)" id="id2">
 
<script>
var svgNS = "http://www.w3.org/2000/svg";
var xlinkNS = "http://www.w3.org/1999/xlink";
 
function clique(evt){ 
var topleft=document.getElementById("id1");
var r=document.getElementById("id2");
rr=r.getBoundingClientRect();
 
    var dx = evt.clientX-rr.left
    var dy = evt.clientY-rr.top;
   
    topleft.setAttributeNS(null, "x", dx);
    topleft.setAttributeNS(null, "y", dy);
}

</script>
<rect id="id1" x="20" y="30" width="100" height="100" rx="10" ry="10" stroke="black" stroke-width="2px" fill="yellow"/>
</svg>
</body>
</html>

O próximo exemplo abaixo é uma arquivo com extensão .svg que foi incorporado a página HTML com um <iframe>,  assim esse código svg é independente da janela ou window. Testes realizados com evt.clientX e evt.clientY tiveram o mesmo resultado que evt.offsetX e evt.offsetY para o GoogleChrome, porém no Internet Explore 11 evt.offsetY apareceu com várias casas decimais quando dentro de um iframe.

Código do arquivo de nome img1.svg: o mouse captura o canto superior esquerdo do rect.
<svg version="1.1"
      baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="510" height="510"
      style="border: 1px solid #000000"
      onclick="clique(evt)">
 
<script>
var svgNS = "http://www.w3.org/2000/svg";
var xlinkNS = "http://www.w3.org/1999/xlink";
 
function clique(evt){  
    var dx = evt.offsetX;
    var dy = evt.offsetY;
    var topleft=document.getElementById("id1");
    topleft.setAttributeNS(null, "x", dx);
    topleft.setAttributeNS(null, "y", dy);
}
</script>
<rect id="id1" x="20" y="30" width="100" height="100" rx="10" ry="10" stroke="black" stroke-width="2px" fill="yellow"/>
</svg>

<iframe width="520" height="520" src="img/img1.svg" scrolling="no" frameborder="0"/></iframe>:

Obs:
evt.clientX e evt.clientY tiveram o mesmo resultado que evt.offsetX e evt.offsetY em arquivo com extensão .svg.

Código do arquivo de nome img7.svg: o mouse captura o centro do círculo.
<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="510" height="510"
      style="border: 1px solid #000000"
      onclick="clique(evt)">
 
 <script>
 var svgNS = "http://www.w3.org/2000/svg";
 var xlinkNS = "http://www.w3.org/1999/xlink";
 
 function clique(evt){  
    var dx = evt.offsetX;
    var dy = evt.offsetY;
    var centro=document.getElementById("id1");
    centro.setAttributeNS(null, "cx", dx);
    centro.setAttributeNS(null, "cy", dy);
 }
 </script>
 <circle id="id1" r="50" cx="250" cy="250" stroke="black" stroke-width="10px" fill="white"/>
 </svg>

<iframe width="520" height="520" src="img/img7.svg" scrolling="no" frameborder="0"/></iframe>:

https://www.javascripture.com/ClientRect
ClientRect : Object
ClientRect describes a rectangular region an element occupies in the viewport. The Element.getBoundingClientRect() method returns a ClientRect containing the position of the Element. Element.getClientRects() returns a ClientRectList containing a list of ClientRects for each portion of the Element (ie, a text Element may have multiple rects if it was split across 2 lines).
Spec
Instance Properties
bottom: Number
The distance of the element to the bottom of the viewport.
height : Number
The height of the element.
left: Number
The distance of the element to the left of the viewport.
right: Number
The distance of the element to the right of the viewport.
top: Number
The distance of the element to the top of the viewport.
width: Number
The width of the element.


Criar Figuras em Tempo Real
O código abaixo é um arquivo.svg configurado para cirar um retângulo apenas.
createElementNS() setAttributeNS() appendChild()
<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="100" height="100"
      stroke="black" stroke-width="0.5px"
      onclick="clique(evt)">
 
<script>
var xmlns = "http://www.w3.org/2000/svg";
var elem = document.createElementNS(xmlns, "rect");
elem.setAttributeNS(null,"x",50);
elem.setAttributeNS(null,"y",50);
elem.setAttributeNS(null,"width",50);
elem.setAttributeNS(null,"height",50);
elem.setAttributeNS(null,"fill", "blue");
document.documentElement.appendChild(elem);
</script>
</svg>

<iframe width="100" height="100" src="img/createRect.svg" scrolling="no" frameborder="1"/>:

Criar Texto em Tempo Real
O código abaixo é um arquivo.svg configurado para cirar um texto apenas.
createElementNS() setAttributeNS() createTextNode() appendChild()
<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="150" height="100"
      stroke="black" stroke-width="0.5px"
      onclick="clique(evt)">
 
<script>
var xmlns = "http://www.w3.org/2000/svg";
var txtElem = document.createElementNS(xmlns, "text");
 
txtElem.setAttributeNS(null,"x",10);
txtElem.setAttributeNS(null,"y",25);
txtElem.setAttributeNS(null,"font-size",20);
 
var helloTxt = document.createTextNode("Hello World!");
txtElem.appendChild(helloTxt);
 
document.documentElement.appendChild(txtElem);
</script>
</svg>
<iframe width="410" height="4100" src="img/mouseCoord.svg" scrolling="no" frameborder="0"/>


Captura das Coordenadas do Mouse
Este arquivo captura as coordenadas do click do mouse na figura SVG.
As coordenadas são guardadas na variável de nome coord que é do tipo  Array. Cada elemento de coord é uma função do tipo Point2D().
c é a variável contador de clicks que começa com 0 (zero), no final da função func() a mesma é acescida em 1 (um), ou seja, conta o número de execuções da função func(). Com isso, a variável c pode ser utilizada como índice da variável coord.

<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="400" height="400"
      style="border: 1px solid #000000"
      id="id1">
 
<script>
var evento=document.getElementById("id1");
var coord = new Array();
evento.addEventListener('mousedown', func);
c=0;
function func(evt) {
   function Point2D(xx,yy) {
   this.xx = xx;
   this.yy = yy;
   }

xa = evt.clientX;
ya = evt.clientY;


coord.push(new Point2D(xa,ya));


xmlns = "http://www.w3.org/2000/svg";
elem = document.createElementNS(xmlns, "text");
elem.setAttributeNS(null,"x",coord[c].xx);
elem.setAttributeNS(null,"y",coord[c].yy);
elem.setAttributeNS(null,'fill', '#000');
textNode = document.createTextNode('('+coord[c].xx+','+coord[c].yy+')');

elem.appendChild(textNode);
document.documentElement.appendChild(elem);
c=c+1;
}
</script>
</svg>

<iframe width="410" height="410" src="img/mouseCoord.svg" scrolling="no" frameborder="0"/>

Contador de Click Par/Impar
Este arquivo é o primeiro passo para a criação de linhas SVG com o mouse. É uma implementação do arquivo anterior.
Cada click do mouse captura as coordenadas do ponto clicado e armazena na variável de nome coord do tipo Array, por sua vez, cada elemento do array é do tipo função Point2D() que é adcionada ao mesmo.
A variável c é um contador que inicia com 0 (zero) e é acrescido em 1 (um) após um click.
O simbolo de porcentagem, %, em JavaScript é um operador que pega o resto da divisão.
Um número é par se o resto da divisão do mesmo por 2 for zero. Se não for par será impar.
if(c%2==0){c é par}else{c é ímpar}

<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="400" height="400"
      stroke="black" stroke-width="1px"
      style="border: 1px solid #000000"
      id="id1">
 
<script>
var evento=document.getElementById("id1");
var coord = new Array();
evento.addEventListener('mousedown', func);
c=0;
function func(evt) {
   function Point2D(xx,yy) {
   this.xx = xx;
   this.yy = yy;
   }

xa = evt.clientX;
ya = evt.clientY;


coord.push(new Point2D(xa,ya));


xmlns = "http://www.w3.org/2000/svg";
elem = document.createElementNS(xmlns, "text");
elem.setAttributeNS(null,"x",coord[c].xx);
elem.setAttributeNS(null,"y",coord[c].yy);
elem.setAttributeNS(null,'fill', '#000');


    if(c%2==0){
textNode = document.createTextNode(c+' é par:'+'('+coord[c].xx+','+coord[c].yy+')');

    }else{
    textNode = document.createTextNode(c+' é ímpar:'+'('+coord[c].xx+','+coord[c].yy+')');
         }

elem.appendChild(textNode);
document.documentElement.appendChild(elem);
c=c+1;
}
</script>
</svg>
<iframe width="410" height="410" src="img/coordParImpar.svg" scrolling="no" frameborder="1"/>


Desenho de Linhas SVG com o Mouse 1
Após o primeiro click cada click seguinte formará um seguimento de reta com o ponto clicado anterior.
A variável c é inicializada com 0 (zero), porém o primeiro ponto da linha só será desenhado com c > 0, esse primeiro ponto da linha tem índice igual a c-1 e o segundo ponto da linha tem índice igual a c. Observe que o último ponto da linha anterior é igual ao primeiro ponto da próxima linha.
<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="400" height="400"
      stroke="black" stroke-width="1px"
      style="border: 1px solid #000000"
      id="nm1">
    
 
<script>
c=0;
var coord = new Array();
var evento = document.getElementById("nm1");
evento.addEventListener('mousedown', func);
function func(evt) {
   function Point2D(xx,yy) {
   this.xx = xx;
   this.yy = yy;
   }

xa = evt.offsetX;
ya = evt.offsetY;

coord.push(new Point2D(xa,ya));


      xmlns="http://www.w3.org/2000/svg";
linha = document.createElementNS(xmlns, "line");
    if(c>0){
         linha.setAttributeNS(null,"x1",coord[c-1].xx);
         linha.setAttributeNS(null,"y1",coord[c-1].yy);
         linha.setAttributeNS(null,'fill', '#000');
   
         linha.setAttributeNS(null,"x2",coord[c].xx);
         linha.setAttributeNS(null,"y2",coord[c].yy);
         }
document.documentElement.appendChild(linha);
     c=c+1;
}
</script>

</svg>

<iframe width="410" height="410" src="img/line1.svg" scrolling="no" frameborder="1"/>

Desenho de Linhas SVG com o Mouse 2
Modificaremos o código anterior. A variável c é inicializada com valor igual a 1 (um). O primeiro ponto da linha tem índice igual a c-1, e o segundo ponto da mesma linha tem índice igual a c. Após a execução da função func() a linha é criada e a variável c é acrescida em 2,  com isso, após o  primeiro click, o click seguinte formará um seguimento de reta com o ponto clicado anterior, assim, cada dois clicks formarão um seguimento de reta independende do anterior.
Observe a condição if(c>0){..., como c é inicializado igual a 1, a condição é satisfeita.
<svg version="1.1" baseProfile="full"
      xmlns="http://www.w3.org/2000/svg"
      width="400" height="400"
      stroke="black" stroke-width="1px"
      style="border: 1px solid #000000"
      id="nm1">
    
 
<script>
c=1;
var coord = new Array();
var evento = document.getElementById("nm1");
evento.addEventListener('mousedown', func);
function func(evt) {

   function Point2D(xx,yy) {
   this.xx = xx;
   this.yy = yy;
   }

xa = evt.offsetX;
ya = evt.offsetY;

coord.push(new Point2D(xa,ya));


      xmlns="http://www.w3.org/2000/svg";
linha = document.createElementNS(xmlns, "line");
    if(c>0){
         linha.setAttributeNS(null,"x1",coord[c-1].xx);
         linha.setAttributeNS(null,"y1",coord[c-1].yy);
         linha.setAttributeNS(null,'fill', '#000');
   
         linha.setAttributeNS(null,"x2",coord[c].xx);
         linha.setAttributeNS(null,"y2",coord[c].yy);
         }
document.documentElement.appendChild(linha);
     c=c+2;

}

</script>

</svg>

<iframe width="410" height="410" src="img/line2.svg" scrolling="no" frameborder="1"/>

a

a

a
a





Referências
:
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick
https://stackoverflow.com/questions/8215021/create-svg-tag-with-javascript


Vocabulário Inglês:
wise (waɪz): adj. 1. sensato, prudente; 2. sábio.
none (nʌn): adv. 1. às vezes equivale a nada ou a nenhum/nenhuma: None of this will affect you. Nada disso vai afetá-los. pron. 1. She had four children but none survived. Ela teve quatro filhos mas nenhum sobreviveu.
otherwise (ˈʌðərwaɪz): adv. fora isso; conj. senão, do contrário.
odd (ɑd): adj. 1. estranho, 2. an odd number um número ímpar.
wind (wɪnd): subs. 1. vento.
wind (waɪnd): v. (passado & particípio wound) 1. to wind sth around sth enrolar algo em algo, 2. to wind a tape forward/back avançar/rebobinar uma fita.
rule (rul): subs. 1. regra to be against the rules ser contra o regulamento, ser proibido, 2. dominação, domínio, 3. reinado.
listener (ˈlɪsənər): subs. 1. ouvinte, 2. to be a good listener saber ouvir.
belongings (bɪˈlɔŋɪŋz): subs. pl. pertences.