尼斯拖放画布HTML5

you*_*t13 5 javascript html5 drag-and-drop canvas

我尝试在代表3个磁盘的画布上实现一个很好的拖放.

我想用鼠标改变每个质量的位置.我的主要问题是我受这3个球体中每个球体的长度约束.

目前,当鼠标在画布内移动时,我已经实现了以下功能(indexMass的值表示移动了哪个质量:1, 2 or 3t1, t2, t3分别表示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);

    }    

}
Run Code Online (Sandbox Code Playgroud)

如你所见,我为每个角度做了:

t = t + 0.1*Math.atan(y/x);
Run Code Online (Sandbox Code Playgroud)

用:

 var x = event.offsetX;
 var y = event.offsetY;
Run Code Online (Sandbox Code Playgroud)

但这种效果并不是很好.一旦用鼠标选中了球体(在鼠标点击下),我希望delta当我不在球体上时,光标会被这个球体或球体粘住,以跟随鼠标坐标的" ".

总而言之,我不知道如何创建一个优秀和用户友好的拖放,如果有人可以帮助我或给我一些建议,这将是伟大的.

谢谢

更新1

@ Blindman67:谢谢你的帮助,你的代码片段对我来说非常复杂,我完全不明白.但我是正确的方式.

我从第一个问题开始:在拖动时,使鼠标保持非常靠近或在其上方旋转所选磁盘.

目前,我修改了我的功能myMove(当我单击并移动鼠标进行拖动时调用),如:

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

    }                    

}
Run Code Online (Sandbox Code Playgroud)

其中indexMass是拖动磁盘的索引window['x'+indexMass],window['y'+indexMass]是所选磁盘中心的当前坐标.

之后,我dx, dy在开始拖动(mx, my返回getMousePos function)和移动鼠标坐标时,分别计算鼠标点击坐标.

最后,我为set全局变量(所选磁盘的theta)更改了磁盘的角度,即window['t'+indexMass]:

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

我已经接受了你的部分代码Math.atan2.

但是这个函数的结果并没有用鼠标拖动来制作好的动画,我想知道这可能来自哪里.

现在,我想只实现拖动而不修改轴的长度,稍后我会看到更多这个功能.

更新2

我一直在寻找一个关于用鼠标拖动选定质量的解决方案.

为了尝试我以前做过的综合,我相信下面的方法很好,但这种拖动方法效果不好:所选磁盘没有正确跟随鼠标,我不知道为什么.

myMove function(当我开始拖动时调用的函数),我决定:

  1. 计算鼠标坐标和所选磁盘坐标之间的dx,dy,即:

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

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

indexMass 表示所选磁盘的索引.

  1. 增加所选磁盘的位置(存储在临时变量中tmpX, tmpY)dx, dy.

  2. 计算新角度theta(通过全局变量在代码中标识)window['t'+indexMass]

  3. 使用此新值计算所选磁盘的新位置,theta例如使用disk1(indexMass=1和theta = t1):

    x1= x0 +l1 * sin(t1)
    y1= y0 +l1 * sin(t1)
    
    Run Code Online (Sandbox Code Playgroud)

我必须让你注意到我想用鼠标拖动而不是用鼠标来修改轴的长度,这是一个约束.

这是整个myMove function(在拖动开始时调用):

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

   }
}
Run Code Online (Sandbox Code Playgroud)

更新4 - 赏金:

问题解决了 !我忘了考虑"indexMass-1"磁盘的位置来计算Math.atan2函数的新角度.

Bli*_*n67 5

您无法移动OS鼠标位置.您可以隐藏鼠标canvas.style.cursor = "none";,然后在画布上绘制一个鼠标,但它会滞后一帧,因为当您获得鼠标坐标时,操作系统已将鼠标放在该位置,如果您使用requestAnimationFrame(RAF),画布的下一个演示文稿将在下一个显示刷新间隔.如果您不使用RAF,您可能会或可能不会在当前显示刷新上显示画布,但偶尔会出现闪烁和剪切.

为了解决问题(这是主观的)从旋转点通过球到鼠标位置画一条线,这至少会给用户一些关于发生了什么的反馈.

我还会为球添加一些手柄,这样你就可以改变质量(球体的体积*密度)和轴的长度.调整大小的光标是一个问题,因为当角度发生变化时,它将与所需的移动方向不匹配.您需要找到最接近正确角度的一个或将光标渲染到画布并使用它.

示例代码显示了我的意思.(不包括sim)将鼠标移到球上移动,当你超过时你也会看到两个圆圈出现改变距离和半径(质量)

/*-------------------------------------------------------------------------------------
 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 **/
Run Code Online (Sandbox Code Playgroud)