Andy  Gee
Andy Gee

Reputation: 3341

Maintaining focus of PaperJS drag item (Sketch)

I have a function closestPointData to determine the closest point of a line, highlight that point by showing a red dot and to determine which point needs to be dragged.

I'm having a problem with the tolerance, when I drag too fast, the mouse "drops" the point.

How can I ensure I don't exceed the tolerance of 30px

See Sketch for working version

line = new Path.Line([90, 90], [250, 250]);
line.strokeColor = 'black';
line.strokeWidth = 3;

var redDot = new Path.Circle({
  center: view.center,
  radius: 3,
  fillColor: 'transparent',
  position: [90, 90]
});

function onMouseDrag(e) {
  var cpd = closestPointData(e);
  if (cpd.closestDistance < 30) {
    line.segments[cpd.closestPointIndex].point = e.point;
    redDot.position = cpd.usePoint;
  }
}


function onMouseMove(e) {
  var cpd = closestPointData(e);
  if (cpd.closestDistance < 30) {
    redDot.position = cpd.usePoint;
    redDot.fillColor = 'red';
  }else{
    redDot.fillColor = 'transparent';
  }
}

// Determine which of the line segment points is closest to the mouse pointer
function closestPointData(e) {
  var closestPointIndex = null;
  var closestDistance = Infinity;
  var usePoint; // segment point coordinates
  // console.log(line.segments);
  
  // get point from each end of segment
  for (var i = 0; i < line.segments.length; i++) {
    var pointAt = line.getPointAt(line.segments[i]);
    var distance = e.point.getDistance(pointAt);
    
    // Qualify shortest distance
    if (distance < closestDistance) {
      closestDistance = distance;
      closestPointIndex = i;
      usePoint = pointAt;
    }
  }
  return {
    closestPointIndex: closestPointIndex,
    closestDistance: closestDistance,
    usePoint: usePoint
  };
}

Upvotes: 0

Views: 106

Answers (1)

sasensi
sasensi

Reputation: 4650

In my opinion, the onMouseDrag handler is usefull only when there is no onMouseMove, otherwise, it gets a bit confusing. I would do it this way.

// Set target size.
const MIN_DISTANCE = 30;

// Create items.
const path = new Path.Line({
    from: view.center,
    to: view.center + 150,
    strokeColor: 'black',
    strokeWidth: 2,
});
const dot = new Path.Circle({
    center: view.center,
    radius: 5,
    fillColor: 'transparent',
});

// Create state variables.
// This will store the active path point.
let activePoint;
// This will allow us to check if we are currently dragging or not.
let dragging = false;

function onMouseDown() {
    dragging = true;
}

function onMouseMove(event) {
    // If we are not currently dragging...
    if (!dragging) {
        // ...look for the closest path point...
        const closest = path.segments
            .map((it) => ({point: it.point, distance: it.point.getDistance(event.point)}))
            .sort((a, b) => a.distance - b.distance)
            .shift();
        // ...and if it is close enough...
        if (closest.distance <= MIN_DISTANCE) {
            // ...show the dot...
            dot.fillColor = 'red';
            dot.position = closest.point;
            // ...and store the active point.
            activePoint = closest.point;
        }
        // Otherwise...
        else {
            // ...hide the dot...
            dot.fillColor = 'transparent';
            // ...and store no active point.
            activePoint = null;
        }
    }
    // If we are dragging and there is an active point...
    else if (activePoint) {
        // ...move the dot...
        dot.position += event.delta;
        // ...and move the path point.
        activePoint.x = dot.position.x;
        activePoint.y = dot.position.y;
    }
}

function onMouseUp() {
    dragging = false;
}

Upvotes: 1

Related Questions