2016-07-11 16 views
5

Tôi cố gắng triển khai kéo và thả đẹp trên canvas đại diện cho 3 đĩa.Kéo và thả đẹp trên canvas HTML5

Tôi muốn thay đổi bằng chuột với vị trí của từng khối. Vấn đề chính của tôi là tôi bị ràng buộc bởi chiều dài của rìu cho mỗi 3 quả cầu.

Đối với thời điểm này, tôi đã thực hiện các chức năng sau khi chuột được di chuyển bên trong vải (giá trị của indexMass chỉ đó khối lượng được chuyển: 1, 2 or 3t1, t2, t3 đại diện tương ứng the angle of mass 1, 2, 3):

// Happens when the mouse is moving inside the canvas 
function myMove(event) { 

    if (isDrag) { 
    var x = event.offsetX; 
    var y = event.offsetY; 

    if (indexMass == 1) 
     { // Update theta1 value 
     t1 = t1 + 0.1*Math.atan(y/x); 
     } 
    else if (indexMass == 2) 
     { // Update theta2 value 
     t2 = t2 + 0.1*Math.atan(y/x); 
     } 
    else if (indexMass == 3) 
     { // Update theta3 value 
     t3 = t3 + 0.1*Math.atan(y/x); 
     } 

    // Update drawing 
    DrawPend(canvas); 

    }  

} 

Như bạn có thể nhìn thấy , tôi đã làm cho mỗi góc:

t = t + 0.1*Math.atan(y/x); 

với:

var x = event.offsetX; 
var y = event.offsetY; 

Nhưng hiệu ứng này không phải là rất tốt đẹp. Khi quả cầu được chọn bằng chuột (khi nhấp chuột), tôi muốn con trỏ bị mắc kẹt với quả cầu này hoặc hình cầu theo sau "delta" của tọa độ chuột khi tôi không ở trên quả cầu nữa.

Để tóm tắt, tôi không biết cách tạo và kéo thả thân thiện và dễ sử dụng, nếu ai đó có thể giúp tôi hoặc cho tôi một số lời khuyên, điều này thật tuyệt.

Cảm ơn

UPDATE 1

@ Blindman67: nhờ sự giúp đỡ của bạn, đoạn mã của bạn là khá phức tạp đối với tôi, tôi không hiểu tất cả. Nhưng tôi đang đi đúng hướng.

Tôi bắt đầu bằng vấn đề đầu tiên: làm cho xoay đĩa đã chọn bằng chuột ở rất gần với nó hoặc trên nó, khi kéo.

Đối với thời điểm này, tôi đã sửa đổi chức năng của tôi myMove (được gọi là khi tôi đã nhấp xuống và di chuyển chuột để kéo) như:

// Happens when the mouse is moving inside the canvas 
function myMove(event) { 

    // If dragging 
    if (isDrag) { 

    // Compute dx and dy before calling DrawPend 
    var lastX = parseInt(event.offsetX - mx); 
    var lastY = parseInt(event.offsetY - my); 

    var dx = lastX - window['x'+indexMass]; 
    var dy = lastY - window['y'+indexMass]; 

    // Change angle when dragging 
    window['t'+indexMass] = Math.atan2(dy, dx); 

    // Update drawing 
    DrawPend(canvas); 

    // Highlight dragging disk 
    fillDisk(indexMass, 'pink'); 

    }      

} 

nơi indexMass là chỉ số của đĩa kéo và window['x'+indexMass], window['y'+indexMass] là tọa độ hiện tại của trung tâm đĩa đã chọn.

Sau đó, tôi tính toán tương ứng dx, dy từ tọa độ được nhấp chuột khi bắt đầu kéo (mx, my được trả lại bởi getMousePos function) và tọa độ chuột khi di chuyển.

Cuối cùng, tôi thay đổi góc của đĩa bằng cách thiết lập, cho biến toàn cục (theta của đĩa được chọn), tức là window['t'+indexMass]:

// Change angle when dragging 
window['t'+indexMass] = Math.atan2(dy, dx); 

Tôi đã mất một phần của mã với Math.atan2.

Nhưng kết quả của chức năng này không tạo ra hoạt ảnh tốt bằng tính năng kéo chuột, tôi muốn biết vị trí này có thể đến từ đâu.

Ngay bây giờ, tôi chỉ muốn thực hiện thao tác kéo mà không sửa đổi độ dài của trục, tôi sẽ xem thêm sau này cho chức năng này.

UPDATE 2

Tôi tiếp tục xảy ra để tìm một giải pháp về việc kéo một khối lượng được lựa chọn với chuột.

Để thử tổng hợp những gì tôi đã làm trước đây, tôi tin rằng phương pháp sau là tốt nhưng phương pháp kéo này không hoạt động tốt: đĩa đã chọn không theo đúng con chuột và tôi không biết tại sao.

Trong myMove function (hàm được gọi khi tôi bắt đầu kéo), tôi quyết định:

  1. Tính dx, dy giữa các tọa độ chuột và tọa độ đĩa được lựa chọn, ví dụ:

var dx = parseInt (event.offsetX - window ['x' + indexMass]);

var dy = parseInt (event.offsetY - window ['y' + indexMass]);

indexMass đại diện cho chỉ mục của đĩa đã chọn.

  1. Tăng vị trí của đĩa đã chọn (được lưu trong các biến tạm thời tmpX, tmpY) bởi dx, dy.

  2. Tính góc mới theta (được xác định trong mã của biến toàn cầu window['t'+indexMass]

  3. Tính các vị trí mới của đĩa được lựa chọn với giá trị mới này của theta, tức là ví dụ với disk1 (indexMass=1 và theta = t1):

    x1= x0 +l1 * sin(t1) 
    y1= y0 +l1 * sin(t1) 
    

tôi phải làm cho bạn nhận thấy rằng tôi muốn kéo với chuột không điều chỉnh độ dài của trục với chuột, đây là một ràng buộc.

Dưới đây là toàn bộ myMove function (gọi khi kéo được bắt đầu):

// Happens when the mouse is moving inside the canvas 
function myMove(event) { 

    // If dragging 
    if (isDrag) { 

    console.log('offsetX', event.offsetX); 
    console.log('offsetY', event.offsetY); 
    var dx = parseInt(event.offsetX - window['x'+indexMass]); 
    var dy = parseInt(event.offsetY - window['y'+indexMass]); 
    console.log('dx', dx); 
    console.log('dy', dy); 

     // Temp variables 
     var tmpX = window['x'+indexMass]; 
     var tmpY = window['y'+indexMass]; 

     // Increment temp positions 
     tmpX += dx; 
     tmpY += dy; 
     // Compute new angle for indexMass 
     window['t'+indexMass] = Math.atan2(tmpX, tmpY); 
     console.log('printf', window['t'+indexMass]); 

     // Compute new positions of disks 
     dragComputePositions(); 

     // Update drawing 
     DrawPend(canvas); 

     // Highlight dragging disk 
     fillDisk(indexMass, 'pink'); 

    } 
} 

CẬP NHẬT 4 - Bounty:

Vấn đề giải quyết! Tôi quên tính đến vị trí của đĩa "indexMass-1" để tính toán góc mới với hàm Math.atan2.

Trả lời

5

Bạn không thể di chuyển vị trí chuột OS. Bạn có thể ẩn chuột canvas.style.cursor = "none"; và sau đó vẽ chuột lên khung hình của bạn nhưng nó sẽ bị trễ bởi một khung vì khi bạn nhận được tọa độ chuột, hệ điều hành đã đặt con chuột ở vị trí đó và nếu bạn sử dụng requestAnimationFrame (RAF) bản trình bày tiếp theo của canvas sẽ ở khoảng thời gian làm mới hiển thị tiếp theo. Nếu bạn không sử dụng RAF, bạn có thể hoặc không thể trình bày canvas trên màn hình làm mới hiện tại, nhưng thỉnh thoảng bạn sẽ bị lóa và cắt.

Để giải quyết vấn đề (chủ quan) vẽ một đường từ điểm quay qua quả bóng đến vị trí chuột, ít nhất nó sẽ cung cấp cho người dùng một số phản hồi về những gì đang xảy ra.

Tôi cũng sẽ thêm một số tay cầm vào các quả bóng để bạn có thể thay đổi khối lượng (khối lượng hình cầu * mật độ) và chiều dài của trục .. Các con trỏ thay đổi kích thước là một vấn đề vì sẽ không phù hợp với hướng chuyển động cần thiết khi các góc có thay đổi. Bạn sẽ cần phải tìm một góc gần nhất với góc chính xác hoặc hiển thị con trỏ tới canvas và sử dụng nó.

Mã ví dụ cho thấy ý của tôi. (Không bao gồm sim) Di chuyển chuột lên quả bóng để di chuyển, khi qua bạn cũng sẽ thấy hai vòng tròn xuất hiện để thay đổi khoảng cách và bán kính (khối lượng)

/*------------------------------------------------------------------------------------- 
 
answer code 
 
---------------------------------------------------------------------------------------*/ 
 

 

 

 

 

 

 
var balls = []; 
 
var startX,startY; 
 
var mouseOverBallIndex = -1; 
 
var mouseOverDist = false; 
 
var mouseOverMass = false; 
 
const DRAG_CURSOR = "move"; 
 
const MASS_CURSOR = "ew-resize"; 
 
const DIST_CURSOR = "ns-resize"; 
 
var dragging = false; 
 
var dragStartX = 0; 
 
var dragStartY = 0; 
 
function addBall(dist,radius){ 
 
    balls.push({ 
 
     dist : dist, 
 
     radius : Math.max(10,radius), 
 
     angle : -Math.PI/2, 
 
     x : 0, 
 
     y : 0, 
 
     mass : (4/3) * radius * radius * radius * Math.PI, 
 
    }); 
 
} 
 
function drawBalls(){ 
 
    var i = 0; 
 
    var len = balls.length; 
 
    var x,y,dist,b,minDist,index,cursor; 
 
    ctx.lineWidth = 2; 
 
    ctx.strokeStyle = "black"; 
 
    ctx.fillStyle = "blue" 
 
    ctx.beginPath(); 
 
    x = startX; 
 
    y = startY; 
 
    ctx.moveTo(x, y) 
 
    for(; i < len; i += 1){ 
 
     b = balls[i]; 
 
     x += Math.cos(b.angle) * b.dist; 
 
     y += Math.sin(b.angle) * b.dist; 
 
     ctx.lineTo(x, y); 
 
     b.x = x; 
 
     b.y = y; 
 
    } 
 
    ctx.stroke(); 
 
    minDist = Infinity; 
 
    index = -1; 
 
    for(i = 0; i < len; i += 1){ 
 
     b = balls[i]; 
 
     ctx.beginPath(); 
 
     ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2); 
 
     ctx.fill(); 
 
     if(!dragging){ 
 
      x = b.x - mouse.x; 
 
      y = b.y - mouse.y; 
 
      dist = Math.sqrt(x * x + y * y); 
 
      if(dist < b.radius + 5 && dist < minDist){ 
 
       minDist = dist; 
 
       index = i; 
 
      } 
 
     } 
 
    } 
 
    if(!dragging){ 
 
     mouseOverBallIndex = index; 
 
     if(index !== -1){ 
 
      cursor = DRAG_CURSOR; 
 
      b = balls[index]; 
 
      ctx.fillStyle = "Red" 
 
      ctx.beginPath(); 
 
      ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2); 
 
      ctx.fill(); 
 
      dx = b.x - Math.cos(b.angle) * b.radius; 
 
      dy = b.y - Math.sin(b.angle) * b.radius; 
 
      x = dx - mouse.x; 
 
      y = dy - mouse.y; 
 
      dist = Math.sqrt(x * x + y * y); 
 
      ctx.beginPath(); 
 
      if(dist < 6){ 
 
       ctx.strokeStyle = "Yellow" 
 
       mouseOverDist = true; 
 
       ctx.arc(dx, dy, 12, 0, Math.PI * 2); 
 
       cursor = DIST_CURSOR; 
 
      }else{ 
 
       ctx.strokeStyle = "black" 
 
       mouseOverDist = false; 
 
       ctx.arc(dx, dy, 5, 0, Math.PI * 2); 
 

 
      } 
 
      ctx.stroke();MASS_CURSOR 
 
      dx = b.x - Math.cos(b.angle + Math.PI/2) * b.radius; 
 
      dy = b.y - Math.sin(b.angle + Math.PI/2) * b.radius; 
 
      x = dx - mouse.x; 
 
      y = dy - mouse.y; 
 
      dist = Math.sqrt(x * x + y * y); 
 
      ctx.beginPath(); 
 
      if(dist < 6){ 
 
       ctx.strokeStyle = "Yellow" 
 
       mouseOverMass = true; 
 
       ctx.arc(dx, dy, 12, 0, Math.PI * 2); 
 
       cursor = MASS_CURSOR; 
 
      }else{ 
 
       ctx.strokeStyle = "black" 
 
       mouseOverMass = false; 
 
       ctx.arc(dx, dy, 5, 0, Math.PI * 2); 
 

 
      } 
 
      ctx.stroke(); 
 
      canvas.style.cursor = cursor; 
 
     }else{ 
 
      canvas.style.cursor = "default"; 
 
     } 
 
    }else{ 
 
     b = balls[mouseOverBallIndex]; 
 
     ctx.fillStyle = "Yellow" 
 
     ctx.beginPath(); 
 
     ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2); 
 
     ctx.fill();   
 
     
 
    } 
 

 
} 
 
function display(){ // put code in here 
 
    var x,y,b 
 
    
 
    if(balls.length === 0){ 
 
     startX = canvas.width/2; 
 
     startY = canvas.height/2; 
 
     addBall((startY * 0.8) * (1/4), startY * 0.04); 
 
     addBall((startY * 0.8) * (1/3), startY * 0.04); 
 
     addBall((startY * 0.8) * (1/2), startY * 0.04); 
 
     
 
    } 
 
    ctx.setTransform(1,0,0,1,0,0); // reset transform 
 
    ctx.globalAlpha = 1;   // reset alpha 
 
    ctx.clearRect(0,0,w,h); 
 
    if((mouse.buttonRaw & 1) && mouseOverBallIndex > -1){ 
 
     b = balls[mouseOverBallIndex]; 
 
     if(dragging === false){ 
 
      dragging = true; 
 
      dragStartX = balls[mouseOverBallIndex].x; 
 
      dragStartY = balls[mouseOverBallIndex].y; 
 
     }else{ 
 
      b = balls[mouseOverBallIndex]; 
 
      if(mouseOverBallIndex === 0){ 
 
       x = startX; 
 
       y = startY; 
 
      }else{ 
 
       x = balls[mouseOverBallIndex-1].x 
 
       y = balls[mouseOverBallIndex-1].y 
 
      } 
 
      if(mouseOverDist){ 
 
       var dist = Math.sqrt(Math.pow(x-mouse.x,2)+Math.pow(y-mouse.y,2)); 
 
       b.dist = dist + b.radius; 
 
       
 
      }else  
 
      if(mouseOverMass){ 
 
       var dist = Math.sqrt(Math.pow(dragStartX-mouse.x,2)+Math.pow(dragStartY-mouse.y,2)); 
 
       b.radius = Math.max(10,dist); 
 
       b.mass = dist * dist * dist * (4/3) * Math.PI; 
 
      }else{ 
 
       b.angle = Math.atan2(mouse.y - y, mouse.x - x); 
 
       ctx.beginPath(); 
 
       ctx.lineWidth = 1; 
 
       ctx.strokeStyle = "grey"; 
 
       ctx.moveTo(x,y); 
 
       ctx.lineTo(mouse.x, mouse.y); 
 
       ctx.stroke(); 
 
      } 
 
     } 
 
     
 
    }else if(dragging){ 
 
     dragging = false; 
 
    } 
 

 
    drawBalls(); 
 
} 
 

 
/*------------------------------------------------------------------------------------- 
 
answer code END 
 
---------------------------------------------------------------------------------------*/ 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
/** SimpleFullCanvasMouse.js begin **/ 
 
const CANVAS_ELEMENT_ID = "canv"; 
 
const U = undefined; 
 
var w, h, cw, ch; // short cut vars 
 
var canvas, ctx, mouse; 
 
var globalTime = 0; 
 
var createCanvas, resizeCanvas, setGlobals; 
 
var L = typeof log === "function" ? log : function(d){ console.log(d); } 
 
createCanvas = function() { 
 
    var c,cs; 
 
    cs = (c = document.createElement("canvas")).style; 
 
    c.id = CANVAS_ELEMENT_ID;  
 
    cs.position = "absolute"; 
 
    cs.top = cs.left = "0px"; 
 
    cs.zIndex = 1000; 
 
    document.body.appendChild(c); 
 
    return c; 
 
} 
 
resizeCanvas = function() { 
 
    if (canvas === U) { canvas = createCanvas(); } 
 
    canvas.width = window.innerWidth; 
 
    canvas.height = window.innerHeight; 
 
    ctx = canvas.getContext("2d"); 
 
    if (typeof setGlobals === "function") { setGlobals(); } 
 
} 
 
setGlobals = function(){ cw = (w = canvas.width)/2; ch = (h = canvas.height)/2; balls.length = 0; } 
 
mouse = (function(){ 
 
    function preventDefault(e) { e.preventDefault(); } 
 
    var mouse = { 
 
     x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0, 
 
     over : false, // mouse is over the element 
 
     bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits; 
 
     mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",") 
 
    }; 
 
    var m = mouse; 
 
    function mouseMove(e) { 
 
     var t = e.type; 
 
     m.x = e.offsetX; m.y = e.offsetY; 
 
     if (m.x === U) { m.x = e.clientX; m.y = e.clientY; } 
 
     m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey; 
 
     if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; } 
 
     else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; } 
 
     else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; } 
 
     else if (t === "mouseover") { m.over = true; } 
 
     else if (t === "mousewheel") { m.w = e.wheelDelta; } 
 
     else if (t === "DOMMouseScroll") { m.w = -e.detail; } 
 
     if (m.callbacks) { m.callbacks.forEach(c => c(e)); } 
 
     e.preventDefault(); 
 
    } 
 
    m.addCallback = function (callback) { 
 
     if (typeof callback === "function") { 
 
      if (m.callbacks === U) { m.callbacks = [callback]; } 
 
      else { m.callbacks.push(callback); } 
 
     } else { throw new TypeError("mouse.addCallback argument must be a function"); } 
 
    } 
 
    m.start = function (element, blockContextMenu) { 
 
     if (m.element !== U) { m.removeMouse(); }   
 
     m.element = element === U ? document : element; 
 
     m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu; 
 
     m.mouseEvents.forEach(n => { m.element.addEventListener(n, mouseMove); }); 
 
     if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); } 
 
    } 
 
    m.remove = function() { 
 
     if (m.element !== U) { 
 
      m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); }); 
 
      if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);} 
 
      m.element = m.callbacks = m.contextMenuBlocked = U; 
 
     } 
 
    } 
 
    return mouse; 
 
})(); 
 
var done = function(){ 
 
    window.removeEventListener("resize",resizeCanvas) 
 
    mouse.remove(); 
 
    document.body.removeChild(canvas);  
 
    canvas = ctx = mouse = U; 
 
    L("All done!") 
 
} 
 

 
resizeCanvas(); // create and size canvas 
 
mouse.start(canvas,true); // start mouse on canvas and block context menu 
 
window.addEventListener("resize",resizeCanvas); // add resize event 
 

 
function update(timer){ // Main update loop 
 
    globalTime = timer; 
 
    display(); // call demo code 
 
    // continue until mouse right down 
 
    if (!(mouse.buttonRaw & 2)) { requestAnimationFrame(update); } else { done(); } 
 
} 
 
requestAnimationFrame(update); 
 

 
/** SimpleFullCanvasMouse.js end **/

Các vấn đề liên quan