我需要能夠在單擊鼠標左鍵時創建的圓對象上啓動raphael「拖動」事件。raphael:如何通過代碼而不是通過「鼠標向下」在對象上啓動「拖動」事件?
詳情:
我工作的一段代碼操縱的閉合路徑。我需要這些特點:路徑上
1.通過鼠標拖動四處移動現有點
2.拆下點右鍵單擊
3.添加點左鍵單擊,導致路分支在該位置
4.如果是通過創建點左鍵單擊,允許用戶釋放鼠標拖放它
這裏之前將其拖動到另一個位置是我的函數創建一個給定拉斐爾「路徑」對象的新圈子:
// create a new circle wrapped around the given point reference
function make_circle(point, path) {
var c = paper.circle(point[1], point[2], 6)
.attr({fill: "#DDD", stroke: "black"});
// record the point and path reference in the circle to allow updates
c.data("point", point).data("path", path);
// set event handlers
c.drag(dragmove,dragstart);
c.update = update_coordinates_circle;
c.mousemove(handle_mousemove_circle);
c.mouseup(handle_mouseup_circle);
c.mousedown(handle_mousedown_circle);
return c;
}
然後我可以這樣做:
var pt1 = ["M", 0, 0],
pt2 = ["L", 10, 0],
pt3 = ["L", 10, 10],
pt4 = ["L", 0, 10],
point_set = [pt1, pt2, pt3, pt4, ["Z"]],
path = paper.path(point_set),
c1 = make_circle(pt1, path),
c2 = make_circle(pt2, path),
c3 = make_circle(pt3, path),
c4 = make_circle(pt4, path);
通過點擊路徑創建新的點時,我這樣做:
make_circle(new_point, this).data("just_created", true);
...和這個圈子檢查的鼠標移動處理器:
if (this.data("just_created")) { ... // follow mouse
我全碼這裏:http://jsfiddle.net/T7XS3/
問題是因爲圓的半徑很小,所以在釋放一個新的點之前快速移動鼠標會中斷mousemove處理程序,因爲它附着在圓上。
當通過「拖動」事件移動現有的圓時,一切正常。無論鼠標移動得有多快,該圓都與之保持一致。
那麼,是否有可能對按下鼠標左鍵時創建但尚未釋放的對象啓動raphael「拖動」事件?
解決方案(http://jsfiddle.net/A27NZ/3/):
var width = 300,
height = 300,
paper_offset = 50,
maxX = width,
maxY = height,
paper = Raphael(paper_offset, paper_offset, 300, 300),
dragging;
make_path([100, 100], [200, 100], [200, 200], [100, 200], "green");
// reset 'dragging' to avoid initial drag
dragging = null;
// some math to determine if a point is between two other points, within some threshold
// based on: http://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment
function isBetween(a, b, c) {
var x1 = a[1],
x2 = b[1],
x3 = c[1],
y1 = a[2],
y2 = b[2],
y3 = c[2],
THRESHOLD = 1000;
var dotproduct = (x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1);
if (dotproduct < 0) return false; // early return if possible
var squaredlengthba = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
if (dotproduct > squaredlengthba) return false; // early return if possible
var crossproduct = (y3 - y1) * (x2 - x1) - (x3 - x1) * (y2 - y1);
if (Math.abs(crossproduct) <= THRESHOLD) return true;
else return false;
}
function global_mousemove(e) {
if (dragging) {
handle_mousemove_circle.call(dragging, e);
}
}
function global_mouseup(e) {
dragging = null;
}
if (document.addEventListener) {
document.addEventListener("mousemove", global_mousemove, false);
document.addEventListener("mouseup", global_mouseup, false);
} else {
document.attachEvent('onmousemove', global_mousemove);
document.attachEvent('onmouseup', global_mouseup);
}
// move circle to given coordinates
function update_circle_xy(new_x, new_y) {
var point = this.data("point"),
path = this.data("path");
// don't follow the mouse outside the canvas, we don't want to lose the point
if (new_x <= 0) {
new_x = 0;
} else if (new_x >= maxX) {
new_x = maxX;
}
if (new_y < 0) {
new_y = 0;
} else if (new_y > maxY) {
new_y = maxY;
}
// update circle coords
this.attr({
cx: new_x,
cy: new_y
});
// update the referenced point
point[1] = new_x;
point[2] = new_y;
// redraw the path
path.attr({
path: path.data("point_set")
});
}
// move circle based on mouse event
function handle_mousemove_circle(e) {
var new_x = e.clientX - paper_offset,
new_y = e.clientY - paper_offset;
update_circle_xy.call(this, new_x, new_y);
}
// handle mouse down on circle
// e.which 1 = left click
// e.which 3 = right click
function handle_mousedown_circle(e) {
// remove the target point on right-click
if (e.which === 3) {
var path = this.data("path"),
point_set = path.data("point_set"),
point = this.data("point"),
index = point_set.indexOf(point);
// don't do anything if we only have 2 points left
// (checking if < 4 because last element is not a point ("Z"))
if (point_set.length < 4) return false;
// remove the target point
point_set.splice(index, 1);
// if removed point was head of set, make the following point the new head
if (index === 0) point_set[0][0] = "M";
// redraw the path
path.attr({
path: point_set
});
// finally, remove the circle
this.remove();
} else if (e.which === 1) {
dragging = this;
}
}
// handle mouse click on path
function handle_mousedown_path(e) {
// split on left-click
if (e.which === 1) {
var X = e.clientX - paper_offset,
Y = e.clientY - paper_offset,
new_point = ["L", X, Y],
point_set = this.data("point_set"),
index;
// "open" the path by removing the end ("Z")
point_set.pop();
for (var i = 0; i < point_set.length; i += 1) {
// cur point
var pt1 = point_set[i], // cur point
pt2; // next point
// circular wrap for next point
if (i === point_set.length - 1) {
pt2 = point_set[0];
} else {
pt2 = point_set[i + 1];
}
// check if these are the two points we want to split between
if (isBetween(pt1, pt2, new_point)) {
index = i + 1;
break;
}
}
// we should have found a place to insert the point, put it there
if (index) {
point_set.splice(index, 0, new_point);
} else {
return; // we didn't find a place to put the new point
}
// "close" the path with a ("Z")
point_set.push("Z");
// redraw the path
this.attr({
path: point_set
});
// create new circle to represent the new point
c = make_circle(new_point, this);
}
}
// create a new circle wrapped around the given point reference
function make_circle(point, path) {
var c = paper.circle(point[1], point[2], 6).attr({
fill: "#DDD",
stroke: "black"
});
// record the point and path reference in the circle to allow updates
c.data("point", point).data("path", path);
// set event handlers
c.mousedown(handle_mousedown_circle);
// start dragging the new circle
dragging = c;
return c;
}
// create a new colored path from four point coordinate pairs
function make_path(p1, p2, p3, p4, color) {
// starting points
var pt1 = ["M", p1[0], p1[1]],
pt2 = ["L", p2[0], p2[1]],
pt3 = ["L", p3[0], p3[1]],
pt4 = ["L", p4[0], p4[1]],
point_set = [pt1, pt2, pt3, pt4, ["Z"]],
path = paper.path(point_set).attr({
stroke: color,
"stroke-width": 5,
"stroke-linecap": "round"
});
// keep a reference to the set of points
path.data("point_set", point_set);
// add listener to the path to allow path-splitting
path.mousedown(handle_mousedown_path);
// create the circles that represent the points
make_circle(pt1, path);
make_circle(pt2, path);
make_circle(pt3, path);
make_circle(pt4, path);
}
+1這與我的想法是一致的。謝謝你的一個工作示例,我更新了handle_mousemove_circle(),使其更好一些,並且與IE和FF更兼容http://jsfiddle.net/A27NZ/2/ – mikhail
好東西,謝謝分享最終工作代碼,希望別人認爲將來有用。 – SpaceDog
再次更新:http://jsfiddle.net/A27NZ/3/。重構和改進以包含來自_ [Raphael的演示](http://raphaeljs.com/picker.html)的一些想法_,似乎很好地進行了邊界檢測。我想我終於對它感到滿意了(現在......) – mikhail