Search code examples
javascriptsvgpaint

Make something like paint with SVG and JS


I tried to make something like paint of Windows, with SVG and JS. It works well, but it has a bug. I can not describe that how is that. You should see it. But it seems when you're drawing a line, it jumps to another position! Can anyone help me please?

Here is the Code:

Html:

<div id="Test" >
<svg height="300" width="300"></svg>
</div>

JavaScript:

var i=0;
var Sx;
var Sy;
var Show = false;
document.addEventListener('DOMContentLoaded', function(event) {
document.getElementById('Test').onmousedown = function clickEvent(e) {
      var rect = e.target.getBoundingClientRect();
      Sx = e.clientX - rect.left; //x position within the element.
      Sy = e.clientY - rect.top;  //y position within the element.
      Show = true;
      
}
document.getElementById('Test').onmouseup = function clickEvent2(e) {
      rect = e.target.getBoundingClientRect();
      var Ex = e.clientX - rect.left; //x position within the element.
      var Ey = e.clientY - rect.top;  //y position within the element.
      var TheLine = document.createElementNS("http://www.w3.org/2000/svg","line");
      TheLine.innerHTML = "";
      document.getElementsByTagName("svg")[0].appendChild(TheLine);
      i++;
      var LineZ = document.getElementsByTagName("line")[i];
      Show = false;
}
document.getElementById('Test').onmouseleave = function clickEvent4(e) {
    Show = false;
    console.log(":(");
    i++;
}
document.getElementById('Test').onmousemove = function clickEvent3(e) {
    if (Show == true)
    {
        rect = e.target.getBoundingClientRect();
        Ex = e.clientX - rect.left; //x position within the element.
        Ey = e.clientY - rect.top;
        
        var TheLine = document.createElementNS("http://www.w3.org/2000/svg","line");
      document.getElementsByTagName("svg")[0].appendChild(TheLine);
      ///i++;
      ///console.log(Sx," ",Sy," ", Ex, " ", Ey);
      var LineZ = document.getElementsByTagName("line")[i];
      if (LineZ != undefined && Ex != 0 && Ey != 0 && Ex != 1 && Ey != 1){
      document.getElementsByTagName("line")[i].setAttributeNS(null,"x1", Sx);
      document.getElementsByTagName("line")[i].setAttributeNS(null,"y1", Sy);
      document.getElementsByTagName("line")[i].setAttributeNS(null,"x2", Ex);
      document.getElementsByTagName("line")[i].setAttributeNS(null,"y2", Ey);
      document.getElementsByTagName("line")[i].setAttribute("style","stroke:rgb(255,0,0);stroke-width:2;visibility:visible");
      }
    }
}
})

Edit:

I think the problem is "e.target.getBoundingClientRect"; Cause I've set the SVG's width to 300. But when the line had jumped, I looked for "rect.width" and saw that's 20! While it had to be 300!

Here is a Fiddle:

https://jsfiddle.net/qrL2mvea/1

And here is the full code on snippest:

var i=0;
var Sx;
var Sy;
var Show = false;
document.addEventListener('DOMContentLoaded', function(event) {
document.getElementById('Test').onmousedown = function clickEvent(e) {
      var rect = e.target.getBoundingClientRect();
      Sx = e.clientX - rect.left; //x position within the element.
      Sy = e.clientY - rect.top;  //y position within the element.
      Show = true;
      
}
document.getElementById('Test').onmouseup = function clickEvent2(e) {
      rect = e.target.getBoundingClientRect();
      var Ex = e.clientX - rect.left; //x position within the element.
      var Ey = e.clientY - rect.top;  //y position within the element.
      var TheLine = document.createElementNS("http://www.w3.org/2000/svg","line");
      TheLine.innerHTML = "";
      document.getElementsByTagName("svg")[0].appendChild(TheLine);
      i++;
      var LineZ = document.getElementsByTagName("line")[i];
      Show = false;
}
document.getElementById('Test').onmouseleave = function clickEvent4(e) {
    Show = false;
    console.log(":(");
    i++;
}
document.getElementById('Test').onmousemove = function clickEvent3(e) {
    if (Show == true)
    {
        rect = e.target.getBoundingClientRect();
        Ex = e.clientX - rect.left; //x position within the element.
        Ey = e.clientY - rect.top;
        
        var TheLine = document.createElementNS("http://www.w3.org/2000/svg","line");
      document.getElementsByTagName("svg")[0].appendChild(TheLine);
      ///i++;
      ///console.log(Sx," ",Sy," ", Ex, " ", Ey);
      var LineZ = document.getElementsByTagName("line")[i];
      if (LineZ != undefined && Ex != 0 && Ey != 0 && Ex != 1 && Ey != 1){
      document.getElementsByTagName("line")[i].setAttributeNS(null,"x1", Sx);
      document.getElementsByTagName("line")[i].setAttributeNS(null,"y1", Sy);
      document.getElementsByTagName("line")[i].setAttributeNS(null,"x2", Ex);
      document.getElementsByTagName("line")[i].setAttributeNS(null,"y2", Ey);
      document.getElementsByTagName("line")[i].setAttribute("style","stroke:rgb(255,0,0);stroke-width:2;visibility:visible");
      }
    }
}
})
body
{
    margin: 0px;
}
#Test
{
    border:2px dotted blue;
    width:300px;
    height:300px;
    background-color:lightblue;
    position: fixed;
    top: 10%;
  }
<div id="Test" >
<svg height="300" width="300"></svg>
</div>

Edit2: I tried to use JQuery's:

"event.pageX - $(this).offset().left;"

and "event.pageY - $(this).offset().top;",

but exactly the same happens :(

The funny part is that when I use e.clientX and e.clientX instead, it's better, but again it's not Ok :(


Solution

  • H i again, I solved it! I used D3 (Version 5.9.2 in jsfiddle, and 5 from it's origirnal website).

    Here is the new code:

    Html:

    <div id="Test" >
    

    CSS:

    body
    {
        margin: 0px;
    }
    #Test
    {
        border:2px dotted blue;
        width:300px;
        height:300px;
        background-color:lightblue;
        position: fixed;
        top: 100px;
        left: 100px;
    }
    svg {
        border: 1px solid grey;
    }
    
    line {
        stroke: steelblue;
        stroke-width: 2px;
        stroke-linecap: round;
    }
    

    JavaScript:

    var line;
    
    var vis = d3.select("div").append("svg") 
        .attr("width", 300)
        .attr("height", 300)
        .on("mousedown", mousedown)
        .on("mouseleave", mouseleave)
        .on("mouseup", mouseup);
    
    function mousedown() {
        var m = d3.mouse(this);
        line = vis.append("line")
            .attr("x1", m[0])
            .attr("y1", m[1])
            .attr("x2", m[0])
            .attr("y2", m[1]);
        
        vis.on("mousemove", mousemove);
    }
    
    function mousemove() {
        var m = d3.mouse(this);
        line.attr("x2", m[0])
            .attr("y2", m[1]);
    }
    
    function mouseup() {
        vis.on("mousemove", null);
    }
    function mouseleave() {
    
     vis.on("mousemove", null);
     
    }
    

    And here is a fiddle:

    http://jsfiddle.net/zsaeLokh/

    Tips:

    "d3.mouse" doesn't work in new versions of D3 anymore. Use V5.9.2 or less. If you use it, It'll give an error:

    d3.mouse is not a function

    I read Here that we can use "d3.pointer(event)" instead.

    You can add Version 5 of that by adding this code to your html code:

    <script src="https://d3js.org/d3.v5.min.js"></script>
    

    You can add it Wherever you want. But remember to add the JS part After the div. Otherwise, it'll give you an error:

    Uncaught ReferenceError: d3 is not defined

    And at last, in

    var vis = d3.select("div").append("svg") 
    

    You should remember to change the div, to the element's name. For Example, if it's a span, change it like this:

    var vis = d3.select("span").append("svg") 
    

    Thanks, hope you can use it, Bye.

    Note:

    I didn't write the full code, I used this fiddle.