简体   繁体   中英

How can I draw a continuous line using d3 without needing to click&drag the mouse, but rather just use the mouseover event?

I am trying to use d3 to draw a continuous line without clicking the mouse. Currently I have this d3 code that works when I click and move, but does not work when I simply enter the area where the line should be drawn. I provided a CodePen link here: https://codepen.io/pwill2/pen/NLdJoL

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Freehand drawing</title>
    <link rel="stylesheet" href="index.css">
    <script src="http://d3js.org/d3.v3.min.js"></script>
  </head>
  <body>
    <svg id="canvas" width="500px" height="250px"></svg>
    <script src="index.js"></script>
  </body>
</html>

index.css

#canvas {
  background: oldlace;
}
.swatch {
  pointer-events: all;
}
.swatch.active {
  stroke-width: 2px;
  stroke: black;
}
.swatch {
  cursor: pointer;
}
.btn {
  pointer-events: all;

  font-family: FontAwesome;
  fill: #333;
  font-size: 32px;
  text-anchor: middle;

  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.btn:hover {
  fill: black;
  cursor: pointer;
}

.line {
  fill: none;
  stroke-width: 2px;
  stroke-linejoin: round;
  stroke-linecap: round;
}

index.js

(function() {
  var SWATCH_D, active_color, active_line, canvas, drag, drawing_data, lines_layer, palette, redraw, render_line, swatches, trash_btn, ui;

  SWATCH_D = 22;

  render_line = d3.svg.line().x(function(d) {
    return d[0];
  }).y(function(d) {
    return d[1];
  }).interpolate('basis');

  drawing_data = {
    lines: [
      {
        color: "#000000",
        points: []}
    ]
  };

  active_line = null;

  active_color = "#000000";

  canvas = d3.select('#canvas');

  lines_layer = canvas.append('g');

  drag = d3.behavior.drag();

  drag.on('dragstart', function() {
    active_line = {
      points: [],
      color: active_color
    };
    drawing_data.lines.push(active_line);
    return redraw(active_line);
  });

  drag.on('drag', function() {
    active_line.points.push(d3.mouse(this));
    return redraw(active_line);
  });

  drag.on('dragend', function() {
    if (active_line.points.length === 0) {
      drawing_data.lines.pop();
    }
    active_line = null;
    return console.log(drawing_data);
  });

  canvas.call(drag);

  redraw = function(specific_line) {
    var lines;
    lines = lines_layer.selectAll('.line').data(drawing_data.lines);
    lines.enter().append('path').attr({
      "class": 'line',
      stroke: function(d) {
        return d.color;
      }
    }).each(function(d) {
      return d.elem = d3.select(this);
    });
    if (specific_line != null) {
      specific_line.elem.attr({
        d: function(d) {
          return render_line(d.points);
        }
      });
    } else {
      lines.attr({
        d: function(d) {
          return render_line(d.points);
        }
      });
    }
    return lines.exit().remove();
  };

  redraw();

}).call(this);

Instead of using a drag, why not listen for a mousemove event on your canvas and push points to a path drawing function that way.

As a simple example:

 var points = []; var canvas = d3.select("body") .append("canvas") .attr("height",500) .attr("width", 600) .on("mousemove", addPoint); var context = canvas.node().getContext("2d"); function addPoint() { points.push(d3.mouse(this)); draw(); } function draw() { context.clearRect(0,0,600,600); context.beginPath(); context.moveTo(...points[0]); points.forEach(function(d) { context.lineTo(...d); }) context.lineWidth = 1; context.stroke(); } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 

If you need to have drawing after a click on each entry of the mouse over the canvas, you could assign the mousemove listener on click too (mouse out removes the mousemove listener below, so you can draw multiple lines):

 var points = []; var canvas = d3.select("body") .append("canvas") .attr("height",500) .attr("width", 600) .on("click", function() { points.push([]) d3.select(this).on("mousemove", addPoint); }) .on("mouseout", function() { d3.select(this).on("mousemove",null); }) var context = canvas.node().getContext("2d"); function addPoint() { points[points.length-1].push(d3.mouse(this)); draw(); } function draw() { context.clearRect(0,0,600,600); points.forEach(function(p) { context.beginPath(); context.moveTo(...p[0]); p.forEach(function(d) { context.lineTo(...d); }) context.lineWidth = 1; context.stroke(); }) } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 

And if you wanted to smoothen out the curves because hands are shaky you could use d3.line and a d3 curve to draw the lines:

 var points = []; var canvas = d3.select("body") .append("canvas") .attr("height",500) .attr("width", 600) .on("mousemove", addPoint); var context = canvas.node().getContext("2d"); var line = d3.line().context(context) .x(function(d) { return d[0]; }) .y(function(d) { return d[1]; }) .curve(d3.curveBasis); function addPoint() { points.push(d3.mouse(this)); draw(); } function draw() { context.clearRect(0,0,600,600) context.beginPath(); line(points); context.lineWidth = 1; context.stroke(); } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 

Though you could also filter out points in the array to get something smoother (since points added by mousemove will be added very close together depending on mouse speed, making smoothening harder), or use different d3 curves:

 var points = []; var canvas = d3.select("body") .append("canvas") .attr("height",500) .attr("width", 600) .on("mousemove", addPoint); var context = canvas.node().getContext("2d"); var line = d3.line().context(context) .x(function(d) { return d[0]; }) .y(function(d) { return d[1]; }) .curve(d3.curveBasis); function addPoint() { points.push(d3.mouse(this)); draw(); } function draw() { context.clearRect(0,0,600,600) context.beginPath(); line(points.filter(function(p,i) { if(i%8 == 0 || i == points.length-1) return p; })); context.lineWidth = 1; context.stroke(); } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM