HTML5画布和JavaScript出现异常行为

3

我制作了一些画布,上面绘制了一个圆和许多其他形状。我编写了一个_rotate() 函数,旨在清除(删除内容)画布并调用一个函数来绘制一个正方形。问题是,在点击旋转后,不仅绘制了正方形,还绘制了圆和矩形。我不知道代码可能出了什么问题,因此接受任何建议。 实际代码:

var c = document.getElementById("canv");
var canvas = c.getContext("2d");
//ciarka
function _rotate() {

canvas.clearRect(0, 0, 600, 400);
stvorec(Math.random());
return;
}


function stvorec(param) {
canvas.rotate(param*Math.PI/180);
canvas.fillStyle="green";
canvas.fillRect(350,50,50,50);
canvas.stroke();
}

function ciarka() {
canvas.beginPath();
canvas.moveTo(10,10);
canvas.lineTo(50,30);
canvas.lineTo(100,10);
canvas.lineTo(150,30);
canvas.lineTo(200,10);
canvas.lineTo(250,30);
canvas.lineTo(300,10);
canvas.lineTo(350,30);
canvas.lineTo(400,10);
canvas.lineTo(450,30);
canvas.lineTo(500,10);
canvas.lineTo(550,30);
canvas.lineTo(590,10);
canvas.stroke();
}


//obdlznik
function obdlznik() {
canvas.rect(150,150,100,50);
canvas.strokeStyle="black";
canvas.stroke();
}

//kruh
function kruh() {
canvas.beginPath();
canvas.arc(200,80,50,0,2.0*Math.PI);
canvas.stroke();
}
//stvorec

stvorec();
ciarka();
kruh();
obdlznik();

$("body").on("click", "#rot", function() {
   _rotate(); 
});

Jsfiddle供参考:jsfiddle
2个回答

1

我看到你的代码中有一些小问题:

  • 每次画线或填充之前都要调用beginPath()。
  • 在绘制圆弧时使用closePath(),使其成为一个圆而不是一个360度的圆弧。
  • 确保在进行旋转之前使用.translate设置旋转点。
  • stvorec(Math.random())只能产生0-1度之间的角度。

这不是一个问题,只是一个命名上的奇怪:你将上下文变量称为canvas。上下文变量通常被称为contextctx

我不知道你的设计要求,但这里是一些重构后可以工作的代码:

http://jsfiddle.net/m1erickson/5PuW9/

<!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; }
    canvas{border:1px solid red;}
</style>
<script>
$(function(){

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

    //ciarka
    function _rotate() {
        canvas.clearRect(0, 0, 600, 400);
        stvorec(Math.random()*360);
    }

    function stvorec(param) {
        canvas.save();
        canvas.beginPath();
        canvas.translate(350,50);
        canvas.rotate(param*Math.PI/180);
        canvas.fillStyle="green";
        canvas.fillRect(-25,-25,50,50);
        canvas.restore();
    }

    function ciarka() {
        canvas.beginPath();
        canvas.moveTo(10,10);
        canvas.lineTo(50,30);
        canvas.lineTo(100,10);
        canvas.lineTo(150,30);
        canvas.lineTo(200,10);
        canvas.lineTo(250,30);
        canvas.lineTo(300,10);
        canvas.lineTo(350,30);
        canvas.lineTo(400,10);
        canvas.lineTo(450,30);
        canvas.lineTo(500,10);
        canvas.lineTo(550,30);
        canvas.lineTo(590,10);
        canvas.stroke();
    }

    //obdlznik
    function obdlznik() {
        canvas.beginPath();
        canvas.rect(150,150,100,50);
        canvas.strokeStyle="black";
        canvas.stroke();
    }

    //kruh
    function kruh() {
        canvas.beginPath();
        canvas.arc(200,80,50,0,2.0*Math.PI);
        canvas.closePath();
        canvas.stroke();
    }

    //stvorec
    stvorec();
    ciarka();
    kruh();
    obdlznik();

    $("#test").click(function() {
       _rotate(); 
    });

}); // end $(function(){});
</script>
</head>
<body>
    <button id="test">Test</button><br>
    <canvas id="canv" width=600 height=400></canvas>
</body>
</html>

[增加了关于beginPath和负坐标的解释]

关于context.beginPath()

将beginPath()和fill()/stroke()视为绘制路径命令周围必需的括号。

如果您不在每个新的路径绘制命令中使用beginPath,则下一个fill()/stroke()被调用时,所有先前的路径绘制命令都将被重新执行。

例如:

// circle#1

context.beginPath();
context.arc(100,100,10,0,Math.PI*2);
context.closePath();
context.fillStyle=”green”;
context.fill();

// circle#2  -- Note: no beginPath here

context.arc(200,200,10,0,Math.PI*2);
context.closePath();
context.fillStyle=”green”;
context.fill();

在这个例子中,将绘制圆形#1。但由于圆形#2的代码中没有beginPath,圆形#1将与圆形#2一起被重新绘制。
两个圆都将是绿色的,因为每个beginPath只允许一个样式(最后的fillStyle="green"将被使用,而红色样式将不会被使用)。
关于旋转和负坐标
要进行旋转,您应该告诉上下文想要围绕哪个坐标点旋转。
这被称为设置旋转点。默认情况下,上下文将在画布左上角设置旋转点,因此每次绘图都将围绕画布左上角旋转。
要设置自己的旋转点,您需要context.translate(myCenterX,myCenterY)。
然后所有后续的绘制都将围绕myCenterX,myCenterY进行。
可以将其视为在纸上握住铅笔尖。纸围绕铅笔尖旋转。
现在绘制矩形。
由于上下文从矩形的左上角绘制矩形,所以矩形将围绕其自身的左上角旋转。
要围绕矩形中心旋转,您必须使用负坐标将矩形向左上方拉动,直到矩形位于旋转点上。这意味着您必须使用fillRect(-rectWidth/2,-rectHeight/2)将矩形置于旋转点上方。

谢谢您的建议和帮助,您能否指出为什么每次都需要使用 beginPath() ? 在 stvorec() 函数中,您向 fillRect() 插入了负值,为什么会这样?提前感谢您的回答(我只是想理解而不是抄袭)。 - rsz
我添加了一个解释,说明为什么每组路径绘制命令都需要beginPath,并且为什么需要负坐标来围绕矩形中心旋转。干杯! - markE
感谢您的解释,现在我明白了。好答案! - rsz

0

这里有一个简单的修复方案:

//obdlznik
function obdlznik() {
    canvas.save();
    canvas.beginPath();               // add this and you're good to go!
    canvas.rect(150, 150, 100, 50);
    canvas.strokeStyle = "black";
    canvas.stroke();
    canvas.restore();
}

这里是修改后的fiddle

(在这里严格来说保存/还原并不是必要的)。


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