2016-07-11 18 views
5

Próbuję zaimplementować ładny przeciągnij i upuść na płótnie reprezentującym 3 dyski.Nice Przeciągnij i upuść na płótnie HTML5

Chciałbym zmienić za pomocą myszki pozycję każdej masy. Moim głównym problemem jest to, że jestem ograniczony przez długość topora dla każdej z tych 3 sfer.

W tej chwili mam realizowane następujące funkcje gdy mysz porusza się wewnątrz obszaru roboczego (wartość indexMass wskazuje których masa jest przemieszczana: 1, 2 or 3 i t1, t2, t3 reprezentuje odpowiednio 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); 

    }  

} 

Jak widać zrobiłem dla każdego kąta:

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

z:

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

Ale ten efekt nie jest zbyt miły. Gdy sfera zostanie wybrana za pomocą myszy (kliknięciem myszki), chciałbym, aby kursor został zablokowany za pomocą tej kuli lub kuli podążając za "delta" współrzędnych myszy, kiedy nie jestem już na kuli.

Podsumowując, nie wiem, jak stworzyć dobry i przyjazny dla użytkownika przeciągnij i upuść, jeśli ktoś mógłby mi pomóc lub udzielił mi rad, byłoby świetnie.

Dzięki

UPDATE 1

@ Blindman67: Dzięki za pomoc, Twój fragment kodu jest dość skomplikowane dla mnie, to nie wszystko rozumiem. Ale jestem na dobrej drodze.

Rozpoczynam od pierwszego wydania: obróć zaznaczony dysk myszką, pozostając bardzo zamkniętą do niego lub ponad nią, podczas przeciągania.

na chwilę, mam zmodyfikowane mojej funkcji myMove (która jest wywoływana kiedy kliknięciu w dół i przesunąć kursor myszy do przeciągania), takie jak:

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

    }      

} 

gdzie indexMass jest indeksem przeciągnięty dysku i window['x'+indexMass], window['y'+indexMass] są bieżącymi współrzędnymi wybranego centrum dysku.

Po tym, obliczam odpowiednio dx, dy ze współrzędnych myszy klikniętych podczas uruchamiania przeciągania (mx, my zwrócona przez getMousePos function) i współrzędnych myszy z ruchem.

Wreszcie zmienić kąt dysku przez zestaw, dla zmiennej globalnej (theta wybranego dysku), tj window['t'+indexMass]:

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

mam zabrał swoją część kodu z Math.atan2.

Ale wynik tej funkcji nie zapewnia dobrej animacji z przeciąganiem myszy, chciałbym wiedzieć, skąd to może pochodzić.

W tej chwili chciałbym wdrożyć tylko przeciąganie bez modyfikowania długości osi, zobaczę więcej później dla tej funkcjonalności.

UPDATE 2

Ciągle dzieje się znaleźć rozwiązanie dotyczące przeciągania wybranego masy z myszy.

Do próby syntezy tego, co zrobiłem wcześniej, Wierzę, że następująca metoda jest dobra, ale ta metoda przeciągania nie działa bardzo dobrze: wybrany dysk nie podąża poprawnie myszą i nie wiem czemu.

W myMove function (funkcja wywoływana, gdy zacznę przeciąganie), postanowiłem:

  1. obliczyć dx, dy między współrzędnych myszy i wybranych współrzędnych dyskowych, a mianowicie:

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

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

indexMass reprezentuje indeks wybranego dysku.

  1. Przyrost położenie wybranego dysku (przechowywany w zmiennych tymczasowych tmpX, tmpY) przez dx, dy.

  2. Oblicz nowy kąt theta (oznaczony w kodzie o zmiennej globalnej window['t'+indexMass]

  3. obliczyć nowe położenia wybranej dysku z nowej wartości theta, to znaczy na przykład z disk1 (indexMass=1 i teta = t1)

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

muszę zrobić można zauważyć, że chcę przeciągając myszką, aby nie modyfikować długości osie z myszą, jest to ograniczenie.

Oto cała myMove function (tzw kiedy zaczyna drag):

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

Problem rozwiązany! Zapomniałem wziąć pod uwagę pozycję dysku "indexMass-1", aby obliczyć nowy kąt z funkcją Math.atan2.

Odpowiedz

5

Nie można przesunąć pozycji myszy systemu operacyjnego. Możesz ukryć mysz canvas.style.cursor = "none";, a następnie narysować mysz na kanwie, ale pozostanie ona w tyle za jedną klatką, ponieważ po uzyskaniu współrzędnych myszy system operacyjny umieścił już mysz w tej pozycji, a jeśli użyjesz requestAnimationFrame (RAF) następna prezentacja płótna będzie następna odsłona odświeżania ekranu. Jeśli nie używasz RAF możesz, ale nie możesz pokazać płótna na bieżącym odświeżaniu wyświetlacza, ale od czasu do czasu dostaniesz migotanie i ścinanie.

Aby rozwiązać problem (który jest subiektywny) narysuj linię od punktu obrotu przez piłkę do pozycji myszy, to przynajmniej da użytkownikowi jakąś informację zwrotną dotyczącą tego, co się dzieje.

Dodałbym również kilka uchwytów do kulek, abyś mógł zmienić masę (objętość sfery * gęstość) i długość osi. Kursory zmiany rozmiaru są problemem, ponieważ nie będą pasowały do ​​kierunku wymaganego ruchu, gdy kąty mają zmiany. Musisz znaleźć jeden najbliższy odpowiedniego kąta lub uczynić kursor na płótnie i użyć go.

Przykładowy kod pokazuje, co mam na myśli. (Nie zawiera karty SIM) Najedź kursorem na kulki do przesuwania, gdy ponad będą również dwa koła wydają się zmienić odległość i promienia (masowy)

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

Powiązane problemy