javascript - 如何用鼠标(按住时)从 Canvas 上移动一个圆圈?

标签 javascript canvas html5-canvas

我有这个问题,我不知道如何处理它。 我花了将近 2 个小时才修复它。我尝试了各种方法,但没有运气。如果这是一个无聊的问题,请原谅我并深入解释你所做的每一步。

我创建了一个 Canvas ,并在其中画了一个圆圈。现在,我想用鼠标移动圆圈,但鼠标按住。有什么办法可以做到,还是我只是在浪费时间? 这是代码:

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

我尝试过的一些方法。这些留在备注/**/。其他的我都删掉了。

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);
    }   
}

最佳答案

动画和用户界面

当您在 Canvas 上拖动任何东西时,您实际上是在为 Canvas 设置动画。为获得最佳结果,您需要停止思考如何使用 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 函数,它获取鼠标下方的圆圈。它将返回鼠标下的最小圆圈。如果鼠标下没有任何内容,它将返回未定义。

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;
    }
}

如何拖动。

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

您最终可能会遇到许多不同的拖动类型事件。拖动以创建、拖动以移动、将某物拖到 Canvas 上或操作自定义呈现的 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>

Note this is written in ES6 and will not run on IE without some type of compiler.

关于javascript - 如何用鼠标(按住时)从 Canvas 上移动一个圆圈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41333845/

相关文章:

javascript - 为什么状态设置正确后却没有声明?

javascript - 无法在NodeJS中下载页面

jquery - 签名未在 html5 中的 Canvas 元素上显示

html5-canvas - 开放层 3 : Scaling canvas with text with 125% DPI since v4. x

javascript - 在javascript中减少文件名的最优化或最简单的方法是什么

javascript - HTML5 Canvas 图像对象不起作用

javascript - 具有半径(模糊?)的颜色选择器算法

java - 如何停止或暂停 Canvas 上的绘图

javascript - Canvas 中的旋转动画

javascript - 如果启用了 JavaScript,如何在网页上生成不同的链接?