2013-12-20 32 views
3

我需要能夠在單擊鼠標左鍵時創建的圓對象上啓動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

我還以爲你可以用這樣的回答「Raphaël Object: Simulate click」技術的一些變化來傳遞事件給了一圈,但沒有按沒有工作。

我的另一個想法是基於Raphael源代碼的工作原理,當你創建一個可拖動的元素時,爲mousedown事件添加一個處理程序。它應該可以直接在正確的上下文中調用該處理程序函數,並將它已經擁有的mousedown事件(在handle_mousedown_path內)傳遞給它。但它是非常哈克,我無法得到它的工作,如果別人可以在這裏就是我要怎樣做:

c = make_circle(new_point, this).data("just_created", true); 
c.events[0].f.call(c, e); // This is very specific to this scenario 

我能想到這樣做是添加鼠標移動的唯一的其他方式/ mouseup處理程序到整個文檔。因此,創建一個全局變量dragging,當你創建一個圓設置dragging是元素,在全球mouseUp處理可以清除變量:

function global_mouseup(e) {  
    dragging = null; 
} 

您已經有一個移動的處理程序,因此使用,我們只需要確保this是功能正確的(見這樣的回答:Javascript: how to set "this" variable easily?

function global_mousemove(e) {  
    if (dragging) { 
     handle_mousemove_circle.call(dragging, e);  
    } 
} 

現在,它們綁定到該文件(並刪除圓上自己的個人處理):

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); 
} 

(看到這個答案:Add event handler to HTML element using javascript爲什麼我們使用的語法,但如果你使用一個框架,你可以使用它的命令)。

而且你去那裏,working fiddle

這並不完美,因爲拉斐爾拖動處理程序更聰明,並知道要保持在畫布內的對象,你需要改變你的handle_mousemove_circle函數來解決這個問題。

+1

+1這與我的想法是一致的。謝謝你的一個工作示例,我更新了handle_mousemove_circle(),使其更好一些,並且與IE和FF更兼容http://jsfiddle.net/A27NZ/2/ – mikhail

+0

好東西,謝謝分享最終工作代碼,希望別人認爲將來有用。 – SpaceDog

+0

再次更新:http://jsfiddle.net/A27NZ/3/。重構和改進以包含來自_ [Raphael的演示](http://raphaeljs.com/picker.html)的一些想法_,似乎很好地進行了邊界檢測。我想我終於對它感到滿意了(現在......) – mikhail

0

Spacedog的通過所提供的解決方案是爲我工作相當不錯。然而,還有一個小問題:如果通過單擊路徑添加兩個新圓圈,並且在某處拖動新圓圈,則每秒「拖動」都是錯誤的,因爲路徑上的mousedown事件會觸發標準拖動功能html元素(see this fiddle by mikhail)

爲了防止出現這種情況,只需在文檔中添加另一個EventListener,即檢查每個mousedown是否使用了「自定義」拖動功能。如果是這樣,默認行爲是被阻止的:

if (document.addEventListener) { 
    document.addEventListener("mousemove", global_mousemove, false); 
    document.addEventListener("mouseup", global_mouseup, false); 
    document.addEventListener("mousedown", global_mousedown, false); 
} else { 
    document.attachEvent('onmousemove', global_mousemove); 
    document.attachEvent('onmouseup', global_mouseup); 
    document.attachEvent('onmousedown', global_mousedown); 
} 

function global_mousedown(e) { 
    if(dragging){ 
     e.preventDefault();  
    } 
} 
相關問題