2016-07-11 35 views
5

Provo ad implementare un bel drag and drop su una tela che rappresenta 3 dischi.Nice Trascina su una tela HTML5

Vorrei cambiare con il mouse la posizione di ciascuna massa. Il mio problema principale è che sono limitato dalla lunghezza dell'ascia per ognuna di queste 3 sfere.

Per il momento, ho implementato la seguente funzione quando il mouse si muove all'interno della tela (valore indexMass indica quale massa viene spostata: 1, 2 or 3 e t1, t2, t3 rappresenta rispettivamente 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); 

    }  

} 

Come si può vedere , l'ho fatto per ogni angolo:

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

con:

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

Ma questo effetto non è molto bello. Una volta che la sfera è stata selezionata con il mouse (con il mouse), vorrei che il cursore fosse bloccato con questa sfera o sfera per seguire "delta" delle coordinate del mouse quando non sono più sulla sfera.

Per riassumere, non so come creare un trascinamento fine e intuitivo, se qualcuno potesse aiutarmi o darmi dei consigli, sarebbe fantastico.

Grazie

UPDATE 1

@ Blindman67: grazie per il vostro aiuto, il vostro frammento di codice è piuttosto complessa per me, non ho capito tutto. Ma io sono sulla buona strada.

Sto iniziando dal primo problema: fai ruotare il disco selezionato con il mouse rimanendo molto chiuso su di esso o sopra di esso, durante il trascinamento.

Per il momento, ho modificato la mia funzione myMove (che viene chiamato quando ho cliccato e sposta il mouse per trascinare) come:

// 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'); 

    }      

} 

dove indexMass è l'indice del disco trascinato e window['x'+indexMass], window['y'+indexMass] sono le coordinate correnti del centro del disco selezionato.

Dopo, ho calcolato il dx, dy rispettivamente dalle coordinate del mouse su cui si è fatto clic quando si avvia il trascinamento (mx, my restituito da getMousePos function) e le coordinate del mouse con lo spostamento.

Infine, modificare l'angolo di disco fisso, per la variabile globale (theta del disco selezionato), cioè window['t'+indexMass]:

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

ho preso la tua parte di codice con Math.atan2.

Ma il risultato di questa funzione non crea una buona animazione con il trascinamento del mouse, vorrei sapere da dove potrebbe venire.

In questo momento, vorrei implementare solo il trascinamento senza modificare la lunghezza dell'asse, vedrò più avanti per questa funzionalità.

UPDATE 2

ho continuare ad andare avanti per trovare una soluzione per il trascinamento di una massa selezionata con il mouse.

per aver tentato una sintesi di quello che ho fatto in precedenza, credo che il seguente metodo è buono, ma questo metodo di trascinamento non sta funzionando molto bene: il disco selezionato non segue correttamente il mouse e non so perché.

In myMove function (funzione chiamata quando inizio trascinamento), abbiamo deciso di:

  1. Calcolare il dx, dy fra le coordinate del mouse e le coordinate disco selezionato, cioè:

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

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

indexMass rappresenta l'indice del disco selezionato.

  1. Incremento posizione del disco selezionato (memorizzato in variabili temporanee tmpX, tmpY) da dx, dy.

  2. Calcola il nuovo angolo theta (identificata nel codice variabile globale window['t'+indexMass]

  3. Calcola le nuove posizioni di disco selezionato con questo nuovo valore di theta, cioè ad esempio con disk1 (indexMass=1 e theta = t1):

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

devo fare si nota che voglio trascinando con il mouse non modificare le lunghezze di assi con il mouse, questo è un vincolo.

Ecco l'intero myMove function (chiamato quando la resistenza sta cominciando):

// 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'); 

    } 
} 

UPDATE 4 - Bounty:

Problema risolto! Ho dimenticato di prendere in considerazione la posizione del disco "indexMass-1" per calcolare il nuovo angolo con la funzione Math.atan2.

risposta

5

Non è possibile spostare la posizione del mouse del sistema operativo. Puoi nascondere il mouse canvas.style.cursor = "none"; e poi disegnare un topo sulla tela, ma rimarrà indietro di un fotogramma perché quando ottieni le coordinate del mouse il sistema operativo ha già posizionato il mouse in quella posizione, e se usi requestAnimationFrame (RAF) la prossima presentazione della tela sarà al prossimo intervallo di aggiornamento della visualizzazione. Se non si utilizza RAF, è possibile o meno presentare la tela sull'aggiornamento del display corrente, ma si otterrà uno sfarfallio e una tosatura occasionali.

Per risolvere il problema (che è soggettivo) tracciare una linea dal punto di rotazione attraverso la palla alla posizione del mouse, questo darà almeno all'utente un feedback su ciò che sta accadendo.

Vorrei aggiungere anche alcune maniglie alle sfere in modo da poter modificare la massa (volume della sfera * densità) e la lunghezza dell'asse .. I cursori di ridimensionamento rappresentano un problema poiché non corrispondono alla direzione del movimento richiesto quando gli angoli hanno cambiamenti. Dovresti trovarne uno più vicino all'angolo corretto o rendere un cursore su una tela e usarlo.

Il codice di esempio mostra cosa intendo. (Non include sim) Muovi il mouse palle per muoversi, quando su di voi vedrà anche due cerchi sembrano cambiare la distanza e il raggio (di massa)

/*------------------------------------------------------------------------------------- 
 
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 **/