使用鼠标事件在HTML5画布上绘制圆形/椭圆形

4
我希望我的画布上有类似于Paint中的椭圆选项来绘制图形,我已经部分实现了此功能。问题是我无法获得圆的半径,目前我将其硬编码为15。另外,我想绘制一个椭圆(就像Paint中的那样),而不是精确的圆。
这是我使用鼠标事件在画布上绘制圆形的代码。请帮助我编写代码以满足上述要求。
function tool_circle() {
    var tool = this;
    this.started = false;

    this.mousedown = function (ev) {
        tool.started = true;
        tool.x0 = ev._x;
        tool.y0 = ev._y;
    };

    this.mousemove = function (ev) {
        if (!tool.started) {
            return;
        }

        context.fillStyle = 'red';

        var distance = Math.sqrt(Math.pow(tool.x0 - ev._x, 2) + Math.pow(tool.y0 - ev._y));
        context.beginPath();
    
        context.arc(tool.x0, tool.y0,15, 0, Math.PI * 2, false);
        context.stroke();
        context.fill();
    };

    this.mouseup = function (ev) {
        if (tool.started) {
            tool.mousemove(ev);
            tool.started = false;
            img_update();
        }
    };
}
3个回答

10

我想要类似于markE的答案,但是使用贝塞尔曲线会绘制椭圆,但它不会给你可能需要的精确半径。

为此,需要一个绘制手动椭圆的函数,而且它相当简单 -

这个函数将取一个角落起点和一个终点,并在该边界内绘制一个完全准确的椭圆:

实时演示

Snapshot from demo

function drawEllipse(x1, y1, x2, y2) {

    var radiusX = (x2 - x1) * 0.5,   /// radius for x based on input
        radiusY = (y2 - y1) * 0.5,   /// radius for y based on input
        centerX = x1 + radiusX,      /// calc center
        centerY = y1 + radiusY,
        step = 0.01,                 /// resolution of ellipse
        a = step,                    /// counter
        pi2 = Math.PI * 2 - step;    /// end angle
    
    /// start a new path
    ctx.beginPath();

    /// set start point at angle 0
    ctx.moveTo(centerX + radiusX * Math.cos(0),
               centerY + radiusY * Math.sin(0));

    /// create the ellipse    
    for(; a < pi2; a += step) {
        ctx.lineTo(centerX + radiusX * Math.cos(a),
                   centerY + radiusY * Math.sin(a));
    }
    
    /// close it and stroke it for demo
    ctx.closePath();
    ctx.strokeStyle = '#000';
    ctx.stroke();
}

演示还标记了矩形区域,以显示椭圆确切地位于其中。

绘制

为了处理允许您绘制椭圆的鼠标操作,您可以执行以下操作:

var canvas = document.getElementById('myCanvas'),
    ctx = canvas.getContext('2d'),
    w = canvas.width,
    h = canvas.height,
    x1,                 /// start points
    y1,
    isDown = false;     /// if mouse button is down

/// handle mouse down    
canvas.onmousedown = function(e) {

    /// get corrected mouse position and store as first point
    var rect = canvas.getBoundingClientRect();
    x1 = e.clientX - rect.left;
    y1 = e.clientY - rect.top;
    isDown = true;
}

/// clear isDown flag to stop drawing
canvas.onmouseup = function() {
    isDown = false;
}

/// draw ellipse from start point
canvas.onmousemove = function(e) {

    if (!isDown) return;
    
    var rect = canvas.getBoundingClientRect(),
        x2 = e.clientX - rect.left,
        y2 = e.clientY - rect.top;
    
    /// clear canvas
    ctx.clearRect(0, 0, w, h);

    /// draw ellipse
    drawEllipse(x1, y1, x2, y2);
}

一个技巧是在主画布上面创建一个顶部画布,并在那里进行绘制。当鼠标按钮释放时,将绘制内容转移到主画布上。这样,当绘制新形状时,您就不必重新绘制所有内容。
希望这能帮到你!

1
我看了演示,很不错。但是,它只允许我们在画布上画一个圆圈。我能不能画出我想要的任意数量的圆圈呢? - Pawan
2
@user130004 当然可以,只需添加一个画布,在上面拖动和绘制即可。当鼠标松开时,将该画布绘制回主画布,覆盖已有内容。如果您想重新绘制所有内容,可以将圆形数据(中心x、y和半径)存储在数组中,并从中重新绘制。 - user1693593
2
@user130004 这是以上代码的修改版:http://jsfiddle.net/37vge/21/ - user1693593
1
我正在尝试擦除绘制的圆的某个部分。为此,我编写了以下代码。请检查它并告诉我哪里出错了。如果我的代码太糟糕,请原谅... (http://jsfiddle.net/saipavan579/sqwt2cqp/) - Pawan
1
嗨@KenFyrstenberg,我想在移动应用程序(Phonegap包装)上使用触摸事件做同样的事情。我尝试了你的代码(我也尝试了markE的代码),只是将onmousedown、onmousemove替换为ontouchstart、ontouchmove事件。这些事件已经被触发了,因为我可以看到日志,但它没有绘制椭圆/圆形。我需要为触摸事件做什么特别的吗?提前感谢(我第一次使用Canvas :)) - hashcoder
显示剩余3条评论

4
这是一个关于如何拖动绘制椭圆形的示例。
示例链接:http://jsfiddle.net/m1erickson/3SFJy/

enter image description here

使用2条贝塞尔曲线绘制椭圆的示例代码:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
body{ background-color: ivory; padding:0px;}
#canvas{ border:1px solid blue; }
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;
    var startX;
    var startY;
    var isDown=false;

    function drawOval(x,y){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.beginPath();
        ctx.moveTo(startX, startY + (y-startY)/2);
        ctx.bezierCurveTo(startX, startY, x, startY, x, startY + (y-startY)/2);
        ctx.bezierCurveTo(x, y, startX, y, startX, startY + (y-startY)/2);
        ctx.closePath();
        ctx.stroke();
    }

    function handleMouseDown(e){
      e.preventDefault();
      e.stopPropagation();
      startX=parseInt(e.clientX-offsetX);
      startY=parseInt(e.clientY-offsetY);
      isDown=true;
    }

    function handleMouseUp(e){
      if(!isDown){ return; }
      e.preventDefault();
      e.stopPropagation();
      isDown=false;
    }

    function handleMouseOut(e){
      if(!isDown){ return; }
      e.preventDefault();
      e.stopPropagation();
      isDown=false;
    }

    function handleMouseMove(e){
      if(!isDown){ return; }
      e.preventDefault();
      e.stopPropagation();
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      drawOval(mouseX,mouseY);
    }

    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});

}); // end $(function(){});
</script>
</head>
<body>
    <h4>Drag to create a circle or oval</h4>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

嗨@markE,我想在移动应用程序(Phonegap包装)中对触摸事件执行相同的操作。我尝试了您的代码,只是将mouseup,mousemove替换为touchstart,touchmove事件。我可以看到日志已触发这些事件,但它却没有绘制椭圆/圆形。我需要为触摸事件做任何特殊的操作吗?谢谢(这是我第一次使用画布 :)) - hashcoder

2
这是我用鼠标拖动在画布上绘制椭圆的方法。
它使用半径为1,但动态缩放来实现椭圆效果 :)

https://jsfiddle.net/richardcwc/wdf9cocz/

//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var scribble_canvasx = $(canvas).offset().left;
var scribble_canvasy = $(canvas).offset().top;
var scribble_last_mousex = scribble_last_mousey = 0;
var scribble_mousex = scribble_mousey = 0;
var scribble_mousedown = false;

//Mousedown
$(canvas).on('mousedown', function(e) {
    scribble_last_mousex = parseInt(e.clientX-scribble_canvasx);
 scribble_last_mousey = parseInt(e.clientY-scribble_canvasy);
    scribble_mousedown = true;
});

//Mouseup
$(canvas).on('mouseup', function(e) {
    scribble_mousedown = false;
});

//Mousemove
$(canvas).on('mousemove', function(e) {
    scribble_mousex = parseInt(e.clientX-scribble_canvasx);
 scribble_mousey = parseInt(e.clientY-scribble_canvasy);
    if(scribble_mousedown) {
        ctx.clearRect(0,0,canvas.width,canvas.height); //clear canvas
        //Save
        ctx.save();
        ctx.beginPath();
        //Dynamic scaling
        var scalex = 1*((scribble_mousex-scribble_last_mousex)/2);
        var scaley = 1*((scribble_mousey-scribble_last_mousey)/2);
        ctx.scale(scalex,scaley);
        //Create ellipse
        var centerx = (scribble_last_mousex/scalex)+1;
        var centery = (scribble_last_mousey/scaley)+1;
        ctx.arc(centerx, centery, 1, 0, 2*Math.PI);
        //Restore and draw
        ctx.restore();
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 5;
        ctx.stroke();
    }
    //Output
    $('#output').html('current: '+scribble_mousex+', '+scribble_mousey+'<br/>last: '+scribble_last_mousex+', '+scribble_last_mousey+'<br/>mousedown: '+scribble_mousedown);
});
canvas {
    cursor: crosshair;
    border: 1px solid #000000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="800" height="500"></canvas>
<div id="output"></div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接