尝试在画布上选择线条或矩形

时间:2017-09-07 09:55:39

标签: javascript css html5 html5-canvas

我希望能够点击任何一行(肌肉)和新行,然后在我的程序中突出显示它,就像我现在可以对节点做的那样。我想使用context.isPointInPath(),但我觉得这样做的限制因为这些行只有1个像素宽。现在我想看看将线条更改为矩形。因为那时我只能看看鼠标点击是否在矩形的高度和宽度内。但是,我很难找到一种方法将矩形连接到两个节点,就像我现在用笔画一样。 我的计划到目前为止:



/*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();
&#13;
#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;
}
&#13;
<!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>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:1)

解决此问题的另一种方法是使用较粗的线宽并改为使用isPointInStroke()

&#13;
&#13;
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();
};
&#13;
body, html {margin:0}
&#13;
<canvas id=c></canvas>
&#13;
&#13;
&#13;

注意:IE11不支持path参数 - 对于它你需要在上下文本身使用普通路径(ctx.moveTo等。)