How to move a circle from a canvas with the mouse (when it's hold)?

时间:2016-12-26 16:30:32

标签: javascript canvas html5-canvas

I have this problem, and I don't know how to deal with it. I struggled almost 2 hours to fix it. I tried various methods, but with no luck. If it's a nobbish question please forgive me and explain me in depth every step you do.

I have created a canvas, and I drew a circle in it. Now, I want to move the circle with the mouse, but while the mouse is hold. Is there any way to do it, or I'm just wasting time? Here is the code:

<canvas draggable="true" id="canv" name="canv" width="600" height="500" style="border:1px solid #000000;" onclick="">Your browser does not support the HTML5 canvas tag.</canvas> <!-- < -- the canvas -->

<script>

var canv = document.getElementById("canv");
var context = canv.getContext("2d");

var width = canv.offsetWidth-1;
var height = canv.offsetHeight-1;

context.beginPath();
context.arc(width/2, height/2, 75, 0, 2*Math.PI);
context.stroke();

</script>

Some methods I have tried. These one are left in remarks /**/. Other ones I deleted them.

1.

<canvas draggable="true" id="canv" name="canv" width="600" height="500" style="border:1px solid #000000;" onclick="circle_move()"></canvas>

function circle_move(event){

    var x = event.clientX;
    var y = event.clientY;

    document.getElementById("txt").setAttribute("value", "X: "+x+" and Y: "+y);

    document.getElementById("canv").addEventListener("onmousemove", function(){
        context.beginPath();
        context.clearRect(0, 0, width, height);
        context.arc(x, y, 75, 0, 2*Math.PI);
        context.stroke();
    });
}

2.

document.getElementById("canv").addEventListener("mousedown", 
    function(event){
        var x = event.clientX;
        var y = event.clientY;

        document.getElementById("txt").setAttribute("value", "X: "+x+" and Y: "+y);

        context.beginPath();
        context.clearRect(0, 0, width, height);
        context.arc(x, y, 75, 0, 2*Math.PI);
        context.stroke();
    }
);

3.

<canvas draggable="true" id="canv" name="canv" width="600" height="500" style="border:1px solid #000000;" onmousedown="launch"></canvas>

function loop(event){
    var x = event.clientX;
    var y = event.clientY;

    document.getElementById("txt").setAttribute("value", "X: "+x+" and Y: "+y);

    context.beginPath();
    context.clearRect(0, 0, width, height);
    context.arc(x, y, 75, 0, 2*Math.PI);
    context.stroke();
}

function launch(event){
    loop();
    if(event.which !== 1 || event.which !== 2 || event.which !== 3){
        launch(event);
    }   
}

3 个答案:

答案 0 :(得分:1)

您可以从评论和代码中看到发生了什么。基本上你需要一个全局鼠标键状态变量,让函数知道鼠标键是否被按下。我使用了mousemove事件来实际开始绘制圆圈。最后,你需要光标和圆心之间的距离小于半径,否则你可以从圆圈外面拖动圆圈(我认为这不是你想要的)。

//keep track of previous x and y coordinates
var x0 = 200;
var y0 = 300;
//keep track of mouse key state
var down = false;

var x;
var y;

//draw the initial circle someplace random
var context = document.getElementById("canv").getContext("2d");
context.clearRect(0, 0, 600, 500);
context.arc(200,300, 75, 0, 2*Math.PI);
context.stroke();

document.addEventListener("mousedown", function()
  {
    //if mousedown event is logged and we are within the area of the circle then state of down is true
    if(getDistance(x, x0, y, y0) < 75) down = true;
  });

document.addEventListener("mouseup", function()
  {
    //if mouseup event is logged then down is now false
    down = false;
  });

document.getElementById("canv").addEventListener("mousemove", 
    function(event){
      x = event.clientX;
      y = event.clientY;
     //we need to be in "down" state in order for a redraw to be necessary
     if(down)
     {  
        //set the previous coordinates to the new coordinates we are drawing.
        x0 = x;
        y0 = y;
        
        //draw the darn thing
        context.beginPath();
        context.clearRect(0, 0, 600, 500);
        context.arc(x, y, 75, 0, 2*Math.PI);
        context.stroke();
     }
    }
);

function getDistance(x0, x1, y0, y1)
{
  return Math.sqrt(Math.pow(x1-x0,2) + Math.pow(y1-y0, 2));  
  
}
<canvas id="canv" name="canv" width="600" height="500" style="border:1px solid #000000;" onclick="">Your browser does not support the HTML5 canvas tag.</canvas>

答案 1 :(得分:1)

动画和用户界面

当您在画布上拖动任何内容时,您实际上是为画布设置动画。为了获得最佳效果,您需要停止思考如何使用DOM完成任务并开始编写类似于编写游戏的代码。

一个统治所有人的功能。

要协调所有正在进行的操作,您需要一个处理所有动画的函数。有时在游戏圈中称为mainLoop,这使得处理动画内容变得更加容易。

主循环

function mainLoop(time){  // this is called 60 times a second if there is no delay
    // clear the canvas ready to be rendered on.
    ctx.clearRect(0,0,canvas.width,canvas.height);

    updateDisplay(); // call  the function that is rendering the display

    // get the next frame
    requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);

在主循环中,您可以检查当前的应用状态并调用相应的功能。在这个例子中,它只是调用updateDisplay()

并不多

KISS IO活动。

鼠标,键盘,触摸等事件处理程序不应该执行任何功能逻辑。这些事件可以非常快速地发射,但是显示器每隔60秒就会更新一次。当用户只能看到CPU正在执行的工作量的10%以上时,从一秒钟内可以触发500次的事件无法呈现。

编写IO事件的最佳方法就像数据记录器一样。尽快获取尽可能快的信息并离开,不要为每种类型的事件编写不同的事件监听器,保持代码简单,只编写一个事件处理程序来尽可能多地处理。如果您需要键盘将其添加到同一个侦听器。

var mouse = (function(){
    var bounds;
    var m = {x:0,y:0,button:false};
    function mouseEvent(event){
        bounds = event.currentTarget.getBoundingClientRect();
        m.x = event.pageX - bounds.left + scrollX;
        m.y = event.pageY - bounds.top + scrollY;
        if(event.type === "mousedown"){
            m.button = true;
        }else if(event.type === "mouseup"){
            m.button = false;
        }
    }
    m.start = function(element){
        ["mousemove","mousedown","mouseup"].forEach(eventType => element.addEventListener(eventType, mouseEvent)); 
    }
    return m;
}())
mouse.start(canvas);

现在,您可以通过简单的鼠标界面随时访问鼠标。

如果没有什么可以移动的话,没有必要添加一个漂亮的界面。以下是帮助管理圈子的对象。它创建,绘制和定位圆圈。

包含findClosest函数,它获取鼠标下的圆圈。它将返回鼠标下方的最小圆圈。如果鼠标下没有任何内容,它将返回undefined。

var circles = {
    items [],
    drawCircle(){  // function for the circle
        ctx.beginPath();    
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        ctx.stroke();
    },
    each(callback){  // iterator
        var i;
        for(i = 0; i < this.items.length; i++){
            callBack(this.items[i],i);
        }
    },
    drawCircles(){
        this.each(c => {
            c.draw();
        })
    },
    addCircle(x,y,radius){
        var circle = {x, y, radius, draw : this.drawCircle};
        this.items.push(circle);
        return circle;
    },
    getClosest(pos){
        var minDist, i, dist, x, y, foundCircle;
        minDist = Infinity;
        this.each(c =>{
            x = pos.x - c.x;
            y = pos.y - c.y;
            dist = Math.sqrt(x * x + y * y);
            if(dist <= c.radius && dist < minDist){
                minDist = dist;
                foundCircle = c;
            }
        })
        return foundCircle;
    }
}

如何拖动。

拖动对象涉及很多内容。您需要一个函数来查找与鼠标最近的对象。您需要提供反馈,以便用户可以看到可以拖动的内容。

您最终可能会遇到许多不同的拖动类型事件。拖动以创建,拖动以移动,将某些内容拖动到画布上,或者操纵自定义呈现的UI对象。如果您从一个对象管理拖动状态,则可以确保在拖动圆圈时不会意外地单击UI项目。

当拖动开始时,您将检查拖动将执行的操作。然后标记您正在拖动,并指出拖动要执行的操作。 (请参阅updateDisplay函数)

当拖动处于活动状态时执行该操作。当鼠标启动时,只需停用拖动

var dragging = {
    started : false, // true if dragging
    type : null,     // string with the type of drag event
    currentObj : null,  // what we are dragging
    startX : 0,      // info about where the drag started
    startY : 0,
    start(type, obj){  // called at the start of a drag.
        this.startX = mouse.x;
        this.startY = mouse.y;
        this.started = true;
        this.type = type;
        this.currentObj = obj;
    }
}

然后是渲染功能。每帧调用一次。我检查鼠标按钮是否关闭,如果是这样做,设置光标以便人们知道该做什么,并绘制圆圈。

var cursor = "default";  // to hold the cursor
var overCircle = null;   // this holds whatever circle happens to be under the mouse when not dragging
function updateDisplay(){
    var x,y, c;
    cursor = "default";  // set default cursor
    // check that the mouse if over the canvas
    if(mouse.x >= 0 && mouse.x < canvas.width && mouse.y >= 0 && mouse.y < canvas.height){
        cursor = "crosshair";
    }
    // is the mouse button down
    if(mouse.button){  // the button is down
        if(!dragging.started){  // if not dragging start 
            if(overCircle){  // start a move drag if over a circle
                dragging.start("move",overCircle)
                overCircle = null;
            }else{ // start a create drag 
                dragging.start("create",circles.addCircle(mouse.x, mouse.y, 1));
            }
        }
        c = dragging.currentObj;
        // Update the drag state o fthe object being draged and the type of drag
        if(dragging.type === "create"){
            x = c.x - mouse.x;
            y = c.y - mouse.y;
            c.radius = Math.sqrt(x * x + y * y);
        }else if(dragging.type === "move"){
            x = dragging.startX - mouse.x;
            y = dragging.startY - mouse.y;
            c.x -= x;
            c.y -= y;
            dragging.startX = mouse.x;
            dragging.startY = mouse.y;
        }
        cursor = "none";
    } else {  // button must be up
        if(dragging.started){ // have we been dragging something.
            dragging.started = false; // drop it
        }
    }
    // draw the circles
    ctx.strokeStyle = "black";
    circles.draw();
    // if not dragging 
    if(!dragging.started){
        // find circle under the mouse
        c = circles.getClosest(mouse);
        if(c !== undefined){  // if there is a circle under the mouse highlight it
            cursor = "move";
            ctx.strokeStyle = "red";
            ctx.fillStyle = "rgba(0,255,255,0.1)";
            c.draw();
            ctx.fill();
            overCircle = c;
        }else{
            overCircle = null;
        }
    }
    // set the cursor.
    canvas.style.cursor = cursor;
}

作为一个工作实例。

    var canvas = document.createElement("canvas");
    canvas.style.border = "1px black solid";
    canvas.width = 512;
    canvas.height = 200;
    var ctx = canvas.getContext("2d");
    document.body.appendChild(canvas);

    function mainLoop(time){  // this is called 60 times a second if there is no delay
        ctx.clearRect(0,0,canvas.width,canvas.height);        
        updateDisplay(); // call  the function that is rendering the display
        // get the next frame
        requestAnimationFrame(mainLoop);
    }
    // request the first frame. It will not start untill all the code below has been run
    requestAnimationFrame(mainLoop);

    
    var mouse = (function(){
        var bounds;
        var m = {x:0,y:0,button:false};
        function mouseEvent(event){
            bounds = event.currentTarget.getBoundingClientRect();
            m.x = event.pageX - bounds.left + scrollX;
            m.y = event.pageY - bounds.top + scrollY;
            if(event.type === "mousedown"){
                m.button = true;
            }else if(event.type === "mouseup"){
                m.button = false;
            }
        }
        m.start = function(element){
            ["mousemove","mousedown","mouseup"].forEach(eventType => element.addEventListener(eventType, mouseEvent)); 
        }
        return m;
    }())
    mouse.start(canvas);


    var circles = {
        items : [],
        drawCircle(){  // function for the circle
            ctx.beginPath();    
            ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
            ctx.stroke();
        },
        each(callback){  // iterator
            var i;
            for(i = 0; i < this.items.length; i++){
                callback(this.items[i],i);
            }
        },
        draw(){
            this.each(c => {
                c.draw();
            })
        },
        addCircle(x,y,radius){
            var circle = {x, y, radius, draw : this.drawCircle};
            this.items.push(circle);
            return circle;
        },
        getClosest(pos){
            var minDist, i, dist, x, y, foundCircle;
            minDist = Infinity;
            this.each(c =>{
                x = pos.x - c.x;
                y = pos.y - c.y;
                dist = Math.sqrt(x * x + y * y);
                if(dist <= c.radius){
                    if(foundCircle === undefined || (foundCircle && c.radius < foundCircle.radius)){
                        minDist = dist;
                        foundCircle = c;
                    }
                }
            })
            return foundCircle;
        }
    }
    var dragging = {
        started : false,
        type : null,
        currentObj : null,  // what we are dragging
        startX : 0,
        startY : 0,
        start(type, obj){
            this.startX = mouse.x;
            this.startY = mouse.y;
            this.started = true;
            this.type = type;
            this.currentObj = obj;
        }
        
    }
    var cursor = "default";
    var overCircle = null;
    function updateDisplay(){
        var x,y, c;
        cursor = "default"
        if(mouse.x >= 0 && mouse.x < canvas.width && mouse.y >= 0 && mouse.y < canvas.height){
            cursor = "crosshair";
        }
        if(mouse.button){  // the button is down
            if(!dragging.started){
                if(overCircle){
                    dragging.start("move",overCircle)
                    overCircle = null;

                }else{
                    dragging.start("create",circles.addCircle(mouse.x, mouse.y, 1));
                }
            }
            c = dragging.currentObj;
            if(dragging.type === "create"){
                x = c.x - mouse.x;
                y = c.y - mouse.y;
                c.radius = Math.sqrt(x * x + y * y);
            }else if(dragging.type === "move"){
                x = dragging.startX - mouse.x;
                y = dragging.startY - mouse.y;
                c.x -= x;
                c.y -= y;
                dragging.startX = mouse.x;
                dragging.startY = mouse.y;
            }
            cursor = "none";
        } else {  // button must be up
            if(dragging.started){ // have we been dragging something.
                dragging.started = false; // drop it
            }
        }
        ctx.strokeStyle = "black";
        circles.draw();
        
        if(!dragging.started){
            c = circles.getClosest(mouse);
            if(c !== undefined){
                cursor = "move";
                ctx.strokeStyle = "red";
                ctx.fillStyle = "rgba(0,255,255,0.1)";
                c.draw();
                ctx.fill();
                overCircle = c;
            }else{
                overCircle = null;
            }
        }
        canvas.style.cursor = cursor;
    }
<div style="font-family:12px arial;">Click drag to create circle</div>

  

请注意,这是在ES6中编写的,如果没有某种类型的编译器,则无法在IE上运行。

答案 2 :(得分:0)

var offsetx,offsety,posx,posy,run;
  window.onmousedown=function(e){
 run=true;
 offsetx=e.clientX;
 offsety=e.clientY;

 };
 window.onmouseup=function(){
 run=false;
 };
window.onmousemove=function(e){
if(run){
 movedx=e.clientX-offsetx;//mouse moved this value
 movedy=e.clientY-offsety;
 posx+=movedx;
 posy+=movedyy;
 offsetx=e.clientX;
 offsety=e.clientY
 //todo draw at posx,posy
 }
 };
 //todo draw circle+set posx,posy to its position