2017-09-07 2 views
1

나는 지금 당장 노드로 할 수있는 것처럼 내 라인 (근육)과 새 라인을 클릭하여 내 프로그램에서 강조 할 수 있기를 원합니다. 나는 context.isPointInPath()를 사용하고 싶지만, 라인이 단지 1 픽셀 너비라는 사실이 너무 제한적일 것이라고 생각합니다. 이제 직사각형을 선으로 변경하는 방법을 살펴 보겠습니다. 왜냐하면 mouseclick이 사각형의 높이와 너비 내에 있는지를 볼 수 있기 때문입니다. 하지만, 지금 당장 뇌졸중과 같이 직사각형을 두 노드에 연결하는 방법을 찾는 데 어려움을 겪고 있습니다. 지금까지 내 프로그램 :캔버스에서 선이나 직사각형을 선택하려고 시도했습니다.

/*jshint esversion: 6 */ 
 
//draw everything on canvas 
 
//TODO: Change use of canvas to a container and moving elements around to avoid the buffer of frame drawing 
 

 
//Node class 
 
class Node { 
 
    constructor(x, y, r, color, highlight, highlightColor) { 
 
    this.x = x; 
 
    this.y = y; 
 
    this.r = r || 20; 
 
    this.color = color || "#ff0"; 
 
    this.highlight = highlight || false; 
 
    this.highlightColor = highlightColor || "#0000FF"; 
 
    } 
 
} 
 

 
//Muscle class 
 
class Muscle { 
 
    constructor(node1, node2, width, color) { 
 
    this.node1 = node1; 
 
    this.node2 = node2; 
 
    this.width = width || 5; 
 
    this.color = color || "#f00"; 
 

 

 
    //Properties of the nodes this muscle attaches to 
 
    Object.defineProperties(this, { 
 

 
     node1x: { 
 
     "get":() => this.node1.x, 
 
     "set": x => { 
 
      this.node1.x = x; 
 
     } 
 
     }, 
 

 
     node1y: { 
 
     "get":() => this.node1.y, 
 
     "set": y => { 
 
      this.node1.y = y; 
 
     } 
 
     }, 
 

 
     node2x: { 
 
     "get":() => this.node2.x, 
 
     "set": x => { 
 
      this.node2.x = x; 
 
     } 
 
     }, 
 

 
     node2y: { 
 
     "get":() => this.node2.y, 
 
     "set": y => { 
 
      this.node2.x = y; 
 
     } 
 
     } 
 
    }); 
 
    } 
 
} 
 

 
function setParentForNodes() { 
 
    this.nodes.forEach(node => { 
 
    node.parentCreature = this; 
 
    }); 
 
} 
 

 
class Creature { 
 
    constructor(nodes, muscles, nodeColors) { 
 
    this.nodes = nodes; 
 
    this.muscles = muscles; 
 
    this.nodeColors = nodeColors || "#ff0"; 
 
    setParentForNodes.call(this); 
 

 
    Object.defineProperties(this, { 
 

 
     creatureNumber: { 
 
     "get":() => creatures.indexOf(this), 
 
     } 
 
    }); 
 
    } 
 

 
    addNewNode(newNode) { 
 
    newNode.parentCreature = this; 
 
    this.nodes.push(newNode); 
 
    } 
 
    addNewNodes(newNodes) { 
 
    newNodes.forEach(function(node) { 
 
     node.parentCreature = this; 
 
    }, this); 
 
    this.nodes = this.nodes.concat(newNodes); 
 
    } 
 
} 
 

 
var nodes = [ 
 
    new Node(100, 100), 
 
    new Node(200, 200) 
 
]; 
 

 
var muscles = [ 
 
    new Muscle(nodes[0], nodes[1]) 
 
]; 
 

 
var creatures = [ 
 
    new Creature(nodes, muscles) 
 
]; 
 

 
var addNodePressed = false; 
 
var attachMusclePressed = false; 
 
var addLimbPressed = false; 
 

 
function draw(container, ctx, nodes, creatureMuscles) { 
 

 
    //draw in the container 
 
    ctx.fillStyle = "#000000"; 
 
    ctx.fillRect(container.y, container.x, container.width, container.height); 
 

 
    // for loop to draw all objects of nodes 
 
    for (let i = 0; i < creatures.length; i++) { 
 

 
    var creatureNodes = creatures[i].nodes; 
 

 
    for (let i = 0; i < creatureNodes.length; i++) { 
 
     ctx.beginPath(); 
 
     ctx.arc(creatureNodes[i].x, creatureNodes[i].y, creatureNodes[i].r, 0, 2 * Math.PI); 
 
     ctx.fillStyle = creatureNodes[i].color; 
 
     ctx.closePath(); 
 
     ctx.fill(); 
 

 
     //check if node needs to be highlighted 
 
     if (creatureNodes[i].highlight == true) { 
 
     ctx.beginPath(); 
 
     ctx.arc(creatureNodes[i].x, creatureNodes[i].y, creatureNodes[i].r, 0, 2 * Math.PI); 
 
     ctx.strokeStyle = creatureNodes[i].highlightColor; 
 
     ctx.lineWidth = 5; // for now 
 
     ctx.closePath(); 
 
     ctx.stroke(); 
 
     } 
 
    } 
 
    creatureMuscles = creatures[i].muscles; 
 
    //loop and draw every muscle 
 
    for (let i = 0; i < creatureMuscles.length; i++) { 
 
     ctx.beginPath(); 
 
     ctx.moveTo(creatureMuscles[i].node1x, creatureMuscles[i].node1y); 
 
     ctx.lineTo(creatureMuscles[i].node2x, creatureMuscles[i].node2y); 
 
     ctx.strokeStyle = creatureMuscles[i].color; 
 
     ctx.lineWidth = creatureMuscles[i].width; 
 
     ctx.closePath(); 
 
     ctx.stroke(); 
 
    } 
 
    } 
 
} 
 

 
//Handle moving a node with mousedrag 
 
function handleMouseDrag(canvas, creatureNodes) { 
 
    var isDrag = false; 
 
    var dragNode; 
 
    var offset = { 
 
    x: 0, 
 
    y: 0, 
 
    x0: 0, 
 
    y0: 0 
 
    }; 
 

 

 
    canvas.addEventListener("mousedown", function(e) { 
 
    //mousedown then save the position in var x and y 
 
    var x = e.offsetX, 
 
     y = e.offsetY; 
 

 
    //loop through all the nodes to find the first node that is within radius of the mouse click 
 
    for (let i = 0; i < creatures.length; i++) { 
 
     var creatureNodes = creatures[i].nodes; 
 

 
     for (let i = 0; i < creatureNodes.length; i++) { 
 
     if (Math.pow(x - creatureNodes[i].x, 2) + Math.pow(y - creatureNodes[i].y, 2) < Math.pow(creatureNodes[i].r, 2)) { 
 
      isDrag = true; 
 
      dragNode = creatureNodes[i]; 
 

 
      //offset.x&y = where the node is currently 
 
      //offset x0&y0 = where the user clicked 
 
      offset = { 
 
      x: dragNode.x, 
 
      y: dragNode.y, 
 
      x0: x, 
 
      y0: y 
 
      }; 
 
      return; 
 
     } 
 
     } 
 
    } 
 
    }); 
 
    // when mouse moves and isDrag is true, move the node's position 
 
    canvas.addEventListener("mousemove", function(e) { 
 
    /*when the user moves the mouse, take the difference of where his mouse is right now and where the user clicked. 
 
    Then, add that to where the node is right now to find the correct placement of the node without centering on your mouse 
 
    */ 
 
    if (isDrag) { 
 
     dragNode.x = e.offsetX - offset.x0 + offset.x; // where the mouse is right now - where the user mousedown + where the node is right now 
 
     dragNode.y = e.offsetY - offset.y0 + offset.y; 
 
    } 
 
    }); 
 

 
    canvas.addEventListener("mouseup", function(e) { 
 
    isDrag = false; 
 
    }); 
 

 
    canvas.addEventListener("mouseleave", function(e) { 
 
    isDrag = false; 
 
    }); 
 
} 
 

 
//Handle highlighting and button functionality 
 
function handleMouseClick(canvas, nodes, muscles) { 
 
    var highlighted; 
 
    var highlightedNode; 
 

 
    canvas.addEventListener("mousedown", function(e) { 
 
    var x = e.offsetX, 
 
     y = e.offsetY; 
 

 
    var loopbreak = false; 
 

 
    for (let i = 0; i < creatures.length; i++) { 
 

 
     var creatureNodes = creatures[i].nodes; 
 

 
     for (let i = 0; i < creatureNodes.length; i++) { 
 
     // check if click is within radius of a node, if it is, highlight and set highlight boolean to true. 
 

 
     if (Math.pow(x - creatureNodes[i].x, 2) + Math.pow(y - creatureNodes[i].y, 2) < Math.pow(creatureNodes[i].r, 2)) { 
 
      var clickedNode = creatureNodes[i]; 
 

 
      if (addNodePressed) { 
 
      console.log("Not valid. Cannot add a node on top of another node."); 
 
      loopbreak = true; 
 
      break; 
 
      } else if (addLimbPressed) { 
 
      console.log("Not valid. Cannot add a limb on top of another node."); 
 
      loopbreak = true; 
 
      break; 
 
      } else if (attachMusclePressed) { 
 
      if (highlightedNode == clickedNode) { 
 
       console.log("Not valid. Cannot attach muscle to the same node."); 
 
       loopbreak = true; 
 
       break; 
 
      } else { 
 
       var newMuscle; 
 

 
       if (highlightedNode.parentCreature.creatureNumber == clickedNode.parentCreature.creatureNumber) { 
 
       newMuscle = new Muscle(highlightedNode, clickedNode); 
 
       highlightedNode.parentCreature.muscles.push(newMuscle); 
 
       attachMuscle(); 
 
       highlightedNode.highlight = false; 
 
       highlighted = false; 
 
       devTools(true, false, false, false); 
 
       } else { 
 
       var newNodes = []; 
 
       var newMuscles = []; 
 

 
       if (highlightedNode.parentCreature.creatureNumber > clickedNode.parentCreature.creatureNumber) { 
 
        highlightedNode.parentCreature.nodes.forEach(function(node) { 
 
        newNodes.push(node); 
 
        }); 
 
        highlightedNode.parentCreature.muscles.forEach(function(muscle) { 
 
        newMuscles.push(muscle); 
 
        }); 
 
        newMuscle = new Muscle(highlightedNode, clickedNode); 
 
        clickedNode.parentCreature.muscles.push(newMuscle); 
 
        clickedNode.parentCreature.muscles = clickedNode.parentCreature.muscles.concat(newMuscles); 
 
        creatures.splice(creatures.indexOf(highlightedNode.parentCreature), 1); 
 
        clickedNode.parentCreature.addNewNodes(newNodes); 
 
       } else { 
 
        clickedNode.parentCreature.nodes.forEach(function(node) { 
 
        newNodes.push(node); 
 
        console.log("Clicked node is bigger."); 
 
        }); 
 
        clickedNode.parentCreature.muscles.forEach(function(muscle) { 
 
        newMuscles.push(muscle); 
 
        }); 
 
        newMuscle = new Muscle(highlightedNode, clickedNode); 
 
        highlightedNode.parentCreature.muscles.push(newMuscle); 
 
        highlightedNode.parentCreature.muscles = highlightedNode.parentCreature.muscles.concat(newMuscles); 
 
        creatures.splice(creatures.indexOf(clickedNode.parentCreature), 1); 
 
        highlightedNode.parentCreature.addNewNodes(newNodes); 
 
       } 
 
       highlightedNode.highlight = false; 
 
       attachMuscle(); 
 
       devTools(true, false, false, false); 
 
       } 
 
      } 
 
      } 
 
      //no button pressed - highlight/unhighlight node 
 
      else { 
 
      if (highlighted || creatureNodes[i].highlight) { 
 
       if (highlightedNode != creatureNodes[i]) { 
 
       highlightedNode.highlight = false; 
 
       highlightedNode = creatureNodes[i]; 
 
       highlightedNode.highlight = true; 
 
       devTools(false, true, true, true); 
 
       } else { 
 
       highlightedNode = creatureNodes[i]; 
 
       highlightedNode.highlight = false; 
 
       highlighted = false; 
 
       highlightedNode = undefined; 
 
       devTools(true, false, false, false); 
 
       } 
 
      } else { 
 
       highlightedNode = creatureNodes[i]; 
 
       highlightedNode.highlight = true; 
 
       highlighted = true; 
 
       devTools(false, true, true, true); 
 
      } 
 
      loopbreak = true; 
 
      break; 
 
      } 
 
     } 
 
     } 
 
    } 
 

 
    // if click was not in radius of any nodes then check for add limb or create node button press. 
 
    if (!loopbreak) { 
 
     loopbreak = false; 
 
     var newNode; 
 
     if (addNodePressed) { 
 
     newNode = new Node(x, y); 
 
     let newNodes = []; 
 
     let newMuscles = []; 
 
     newNodes.push(newNode); 
 
     var newCreature = new Creature(newNodes, newMuscles); 
 
     creatures.push(newCreature); 
 
     addNode(); 
 
     addNodePressed = false; 
 
     devTools(true, false, false, false); 
 
     } else if (addLimbPressed) { 
 
     newNode = new Node(x, y); 
 
     let newMuscle = new Muscle(newNode, highlightedNode); 
 
     highlightedNode.parentCreature.addNewNode(newNode); 
 
     highlightedNode.parentCreature.muscles.push(newMuscle); 
 
     addLimb(); 
 
     addLimbPressed = false; 
 
     highlightedNode.highlight = false; 
 
     highlighted = false; 
 
     highlightedNode = undefined; 
 
     devTools(true, false, false, false); 
 
     } 
 
    } 
 
    }); 
 
} 
 

 
//Handle Devtools 
 
function devTools(addNode, removeNode, attachMuscle, addLimb) { 
 

 
    var creatureNumberHTML = document.getElementById("creatureNumber"); 
 
    var selectedHTML = document.getElementById("selected"); 
 
    var addNodeB = document.getElementById("addNode"); 
 
    var removeNodeB = document.getElementById("removeNode"); 
 
    var attachMuscleB = document.getElementById("attachMuscle"); 
 
    var addLimbB = document.getElementById("addLimb"); 
 

 
    addNodeB.disabled = (addNode) ? false : true; 
 
    removeNodeB.disabled = (removeNode) ? false : true; 
 
    attachMuscleB.disabled = (attachMuscle) ? false : true; 
 
    addLimbB.disabled = (addLimb) ? false : true; 
 

 
    for (let i = 0; i < creatures.length; i++) { 
 
    var creatureNumber = i; 
 
    var creatureNodes = creatures[i].nodes; 
 

 
    for (let i = 0; i < creatureNodes.length; i++) { 
 
     if (creatureNodes[i].highlight == true) { 
 
     selectedHTML.innerHTML = `Selected: ${i} node`; 
 
     creatureNumberHTML.innerHTML = `Creature number: ${creatureNumber}`; 
 
     return; 
 
     } else { 
 
     creatureNumberHTML.innerHTML = "Creature number: -"; 
 
     selectedHTML.innerHTML = "Selected: None"; 
 
     } 
 
    } 
 
    } 
 
} 
 

 
//Handle add node button 
 
function addNode() { 
 
    var addNodeB = document.getElementById("addNode"); 
 

 
    if (addNodePressed) { 
 
    addNodePressed = false; 
 
    addNodeB.style.background = ""; 
 
    } else { 
 
    addNodePressed = true; 
 
    addNodeB.style.backgroundColor = "#808080"; 
 
    //and unhighlight 
 
    } 
 
} 
 

 
//Handle remove node button 
 
function removeNode() { 
 
    for (let i = 0; i < creatures.length; i++) { 
 
    var creatureNodes = creatures[i].nodes; 
 
    var creatureMuscles = creatures[i].muscles; 
 

 
    for (let i = 0; i < creatureNodes.length; i++) { 
 
     if (creatureNodes[i].highlight == true) { 
 

 
     let highlightedNode = creatureNodes[i]; 
 

 
     for (let i = 0; i < creatureMuscles.length; i++) { 
 
      if (creatureMuscles[i].node1 == highlightedNode || creatureMuscles[i].node2 == highlightedNode) { 
 
      creatureMuscles.splice(i, 1); 
 
      i--; 
 
      } 
 
     } 
 
     creatureNodes.splice(i, 1); 
 
     } 
 
    } 
 
    } 
 
    devTools(true, false, false, false); 
 
} 
 

 
//Handle attach muscle button 
 
function attachMuscle() { 
 
    var attachMuscleB = document.getElementById("attachMuscle"); 
 

 
    if (attachMusclePressed) { 
 
    attachMusclePressed = false; 
 
    attachMuscleB.style.background = ""; 
 
    } else { 
 
    attachMusclePressed = true; 
 
    attachMuscleB.style.backgroundColor = "#808080"; 
 
    } 
 
} 
 

 
//Handle add limb button 
 
function addLimb() { 
 
    var addLimbB = document.getElementById("addLimb"); 
 

 
    if (addLimbPressed) { 
 
    addLimbPressed = false; 
 
    addLimbB.style.background = ""; 
 
    } else { 
 
    addLimbPressed = true; 
 
    addLimbB.style.backgroundColor = "#808080"; 
 
    } 
 
} 
 

 
//Main - Grabs document elements to draw a canvas on, init node and muscle arrays and then continuously updates frame to redraw 
 
function main() { 
 
    var canvas = document.getElementById("canvas"); 
 
    var ctx = canvas.getContext("2d"); 
 
    var container = { 
 
    x: 0, 
 
    y: 0, 
 
    get width() { 
 
     return canvas.width; 
 
    }, 
 
    get height() { 
 
     return canvas.height; 
 
    } 
 
    }; 
 

 
    handleMouseDrag(canvas, nodes); 
 
    handleMouseClick(canvas, nodes, muscles); 
 
    // refresh and redraw with new properties in an updateframe infinite loop 
 
    function updateFrame() { 
 
    ctx.save(); 
 
    draw(container, ctx, nodes, muscles); 
 
    ctx.restore(); 
 
    requestAnimationFrame(updateFrame); 
 
    } 
 
    updateFrame(); 
 
} 
 

 
main();
#canvas { 
 
    display: block; 
 
} 
 

 
#info { 
 
    display: inline-block; 
 
    text-overflow: clip; 
 
    overflow: hidden; 
 
    margin-right: 200px; 
 
} 
 

 
#commands { 
 
    display: inline-block; 
 
    text-align: center; 
 
    margin-left: 200px; 
 
} 
 

 
#devTools { 
 
    background-color: aqua; 
 
    width: 1500px; 
 
} 
 

 
section { 
 
    width: 200px; 
 
    height: 200px; 
 
    background-color: grey; 
 
    vertical-align: top; 
 
}
<!DOCTYPE HTML> 
 

 
<html> 
 

 
<head> 
 
    <meta charset="UTF-8"> 
 
    <link rel="stylesheet" type="text/css" href="styles.css" /> 
 
</head> 
 

 
<body> 
 
    <!--TODO: Adjust the size of the canvas to fit the window--> 
 
    <canvas id="canvas" width="1500" , height="600"></canvas> 
 

 
    <!--TODO: Create buttons for all devtools under the canvas--> 
 
    <!--TODO: Make a container for all devtools under the canvas, then add all the functionality to it after--> 
 
    <div id="devTools"> 
 
    <section id="info"> 
 
     <p>Info</p> 
 
     <p id="creatureNumber">Creature Number: -</p> 
 
     <p id="selected">Selected: </p> 
 
    </section> 
 

 
    <section id="commands"> 
 
     <p>Commands</p> 
 
     <button type="button" id="addNode" onclick="addNode()">Add node</button> 
 
     <button type="button" id="removeNode" disabled=true onclick="removeNode()">Remove node</button> 
 
     <button type="button" id="attachMuscle" disabled=true onclick="attachMuscle()">Attach muscle</button> 
 
     <button type="button" id="addLimb" disabled=true onclick="addLimb()">Add Limb</button> 
 
     <div id="muscleLength"> 
 
     <button type="button" id="increaseLengthB">&uarr;</button> 
 
     <p>Muscle Length</p> 
 
     <button type="button" id="decreaseLengthB">&darr;</button> 
 
     </div> 
 

 

 

 
    </section> 
 
    </div> 
 

 
    <script src="scripts/script.js"></script> 
 
</body> 
 

 
</html>

답변

1

이 문제를 해결하는 또 다른 방법은 두꺼운 라인 폭을 사용하는 대신 isPointInStroke()을 사용하는 것입니다.

var ctx = c.getContext("2d"); 
 
var path = new Path2D();       // to store and reuse path 
 
var onLine = false;        // state (for demo) 
 
path.moveTo(10, 10);        // store a line on path 
 
path.lineTo(200, 100); 
 
ctx.lineWidth = 16;        // line width 
 
render();           // initial render 
 

 
function render() { 
 
    ctx.clearRect(0,0,300,150); 
 
    ctx.strokeStyle = onLine ? "#09f" : "#000";  // color based on state 
 
    ctx.stroke(path);        // stroke path 
 
} 
 

 
c.onmousemove = function(e) {      // demo: is mouse on stroke? 
 
    onLine = ctx.isPointInStroke(path, e.clientX, e.clientY); 
 
    render(); 
 
};
body, html {margin:0}
<canvas id=c></canvas>

참고 : IE11 경로 인수를 지원하지 않습니다 - 당신이이 일을

+0

상황 자체 (ctx.moveTo 등)에 일반 경로를 사용해야합니다 그것을 위해,하지만 난 ' 여전히 현재 프로그램에 문제가 있어도 lineWidth를 조정 함에도 불구하고 라인의 중간에있는 1 픽셀 만 인식합니다. – Chenny

+0

jsfiddle로 다시 만들었습니다 : https://jsfiddle.net/pm796vL3/19/ 잘 작동하기 때문에 지금 내 프로그램이 설정되는 방식에 문제가있을 것입니다. – Chenny

+0

Nevermind, 문제가 발견되었습니다. ctx.save() 및 ctx.restore()와 관련이 있습니다. – Chenny

관련 문제