再探canvas(小球实例)

  之前学习过canvas的一些使用,也用过canvas绘制过时钟, 但是很久不用,有些遗忘了,这里做一个简单的回顾。

  

  在web页面创建一个canvas画布非常简单,如下即可:

    <canvas id = "parabolic" width="300" height="300"></canvas>

  注意:canvas元素是一个内联元素。

  

  当然我们也可以修改它的style,如下所示:

        #parabolic {
            display: block;
            background: #ccc;
            margin: 200px auto;
        }

  这样,我们就可以在页面上得到一个灰色的300px的画布了。

  

  由于我们必须在确保使用<canvas>时所有的页面已经完全加载,所以在js中需要添加:

    window.addEventListener("load", function() {
        // Your code ... 
    },false);

  

  接下来我们就可以获得这个canvas元素了:

  var myCanvas = document.getElementById("parabolic");

  然后,为了可以在canvas上画图,我们就得先获取到2d环境:

  var parabolic = myCanvas.getContext("2d");

  ok! 大功告成,一旦获得了2d环境,我们就可以利用canvas的API进行画图了。

  注: canvas常用的API在这里有详细的说明

  比如最简单的 fillStyle用于设置绘制的颜色, fillRect可以绘制矩形。

   parabolic.fillStyle = "#0000ff";
   parabolic.fillRect(20, 40, 50, 100);

  这就会绘制一个颜色是蓝色的在画布中的位置为(20,40)的宽为50,高为100的矩形。如下:

  而如果使用strokeStyle和strokeRect就会画出一个同样大小的矩形,只是外层是边框,没有完全填充:

   

  在Canvas的API中还有一个rect方法,这个和strokeRect有什么区别呢?  其实并没有区别,知识,我们得用两步来完成,如下:

        parabolic.beginPath();
        parabolic.strokeStyle = "blue";
        parabolic.rect(50, 50, 100, 100);
        parabolic.stroke();

  效果如下所示:

  

  于是可以总结区别:

  1.   fillStyle是填充的样式,stroke是绘制的样式
  2.      fillRect是填充矩形,必须喝fillStyle配合使用; strokeRect是绘制矩形,必须和strokeStyle配合使用。

  

到这里,其实我们就已经可以总结出规律了,基本上就是下面两种方式:

方式一:

  • 先确定样式 (fillStyle)
  • 再填充路径  (fillReact)

方式二

  • 先确定样式 (fillStyle)
  • 在确定路径(react)
  • 最后进行填充 (fill)

  而 clearRect()的作用是清除选择矩形中的像素

        parabolic.fillStyle = "#0000ff";
        parabolic.fillRect(20, 40, 50, 100);
        parabolic.clearRect(20,40,50,50);

最终的效果如下所示:

 即我们清除了上面一半的矩形,只剩下下面的矩形了。

    

  如果我们想要绘制一条线,使用lineTo就可以了,如下所示:

        parabolic.beginPath();
     parabolic.lineWidth = "5"; parabolic.strokeStyle = "red"; parabolic.moveTo(200, 100); parabolic.lineTo(300, 0); parabolic.stroke();

  首先,要确定line的宽度,否则最后就什么都看不到了,我们这里是在绘制路径,所以要用stroke, 因为无法填充,所以如果使用fill是得不到结果的。

  moveTo是指将起点移动到的位置,而lineTo是你画的这条线要截至的位置, 之前一直在描述这个路径是怎么样的,最后必须要使用stroke()才能将之绘制出并展示出来。  注意:在每次绘制路径前,我们最好使用 beginPath(), 这是一个好的习惯。

  

  根据上图,可判断canvas的原点是在左上方的,向右为x轴正方向,向下为y轴正方向。 

  如果我们希望制作一个封闭的路径,那么使用closePath往往可以达到我们想要的效果:

        parabolic.beginPath();
        parabolic.lineWidth = "5";
        parabolic.strokeStyle = "red";
        parabolic.moveTo(50, 50);
        parabolic.lineTo(70, 100);
        parabolic.lineTo(90, 50);
        parabolic.closePath();
        parabolic.stroke();

  即我们先从(50, 50)绘制到 (70, 100),然后接着到(90, 50),这时如果stroke我们可以得到一个v形,但是如果我们使用了closePath()方法,这个路径就可以自动闭合了,如下:

    

  显然,这里的基本思路也是 样式 -> 路径 -> 填充 只是调用不同的api而已,其他并没有什么区别。

  哈哈,在canvas中还有一个比较有意思的就是 arc()方法了, 它可以画弧线,接收六个参数,分别是圆心的x、y坐标,半径,起始和终止的弧度,还有一个可选的true/false即顺时针还是逆时针(true是逆时针),如下:

        parabolic.beginPath();
        parabolic.strokeStyle = "red";
        parabolic.arc(150, 150, 100, 0, Math.PI, true);
        parabolic.stroke(); 

  

  我们将绘制弧形的函数进行简单的封装,就可以做出下面的小丑的样式了,

    window.addEventListener("load", function() {
            var myCanvas = document.getElementById("parabolic");
            var parabolic = myCanvas.getContext("2d");

            // 将绘制圆形的函数进行封装,可以减少代码的重复使用
            function paintArc(style, color, x, y, r, start, end, bool) {
                parabolic.beginPath();
                parabolic[style+"Style"] = color;
                parabolic.arc(x, y, r, start, end, bool);
                parabolic[style]();             
            }
            paintArc("fill", "blue", 0, 0, 20, 0, Math.PI, false);
            paintArc("fill", "blue", 300, 0, 20, 0, Math.PI, false);
            paintArc("fill", "blue", 0, 300, 20, 0, Math.PI, true);
            paintArc("fill", "blue", 300, 300, 20, 0, Math.PI, true);
            paintArc("stroke", "red", 150, 150, 100, 0, Math.PI, true);
            paintArc("stroke", "red", 100, 120, 20, 0, 2*Math.PI, true);
            paintArc("fill", "green", 100, 120, 10, 0, 2*Math.PI, true);
            paintArc("stroke", "red", 200, 120, 20, 0, 2*Math.PI, true);
            paintArc("fill", "green", 200, 120, 10, 0, 2*Math.PI, true);
            paintArc("fill", "yellow", 150, 180, 30, 0, 2*Math.PI, true);
            paintArc("fill", "pink", 150, 230, 60, 0, Math.PI, false );

        },false);

  最终效果如下所示:

   

  

  除此之外,我们还可以看到 quadraticCurveTo() 的二次贝塞尔曲线的API,使用起来也不难。

  二次贝塞尔曲线需要两个点,一个是终点,另外一个是控制点,我们从官网上盗图得到:

  这样,我们就可以得到了,如下所示:

    parabolic.beginPath();
    parabolic.moveTo(50, 50);
    parabolic.strokeStyle = "red";
    parabolic.quadraticCurveTo(150, 300, 250, 50);
    parabolic.closePath();
    parabolic.stroke();

  效果如下:

  

  这里封闭是因为我们使用了closePath方法。

  

  三次贝塞尔曲线和二次贝塞尔曲线的区别并不大,只是多了一个控制点,如下所示:

    

  下面我们使用贝塞尔曲线就可以绘制处nike的标志了,  

window.addEventListener("load", function() {

    var myCanvas = document.getElementById("parabolic");
    var parabolic = myCanvas.getContext("2d");
    parabolic.beginPath();
    parabolic.moveTo(20, 50);
    parabolic.bezierCurveTo(0, 150, 80, 110, 280, 10);
    parabolic.bezierCurveTo( 30, 100, 50, 100, 20, 50);
    parabolic.fill();

},false);

  最终效果如下所示:

  这个曲线是贝塞尔曲线的方程,博友写过一篇博客,就是讲解了关于二次贝塞尔曲线和三次贝塞尔曲线的。

 

  

  在API中,我们还可以发现有一个rotate()方法,使用如下:

    drawCurve();
    function drawCurve() {
        parabolic.beginPath();
        parabolic.moveTo(20, 20);
        parabolic.rotate(10*Math.PI/180);
        parabolic.bezierCurveTo(110, 300, 200, 300, 280, 20);
        parabolic.stroke();
    }

  注意:rotate一定要在 绘制之前, 否则是不起效果的, 可以看到我们一般使用  角度*Math.PI/180 的方式进行旋转

  我们还可以使用 rotate的api,如下所示:

    drawCurve();
    function drawCurve() {
        parabolic.beginPath();
        parabolic.moveTo(20, 20);
        parabolic.scale(0.5, 0.5);
        parabolic.bezierCurveTo(110, 300, 200, 300, 280, 20);
        parabolic.stroke();
    }

  最终效果如下:

  不难看出,这时候两边是不一样高的,因为这里的缩放并没有对画布进行缩放, 而只是对最终我们要绘制的路径进行了缩放。即把我们输入的值进行了缩放, 而之前的moteTo里的值始终不改变。

  当然,之前说过,原点默认是在左上方的,但是我们可以使用translate(x, y)方法,将目前处于(0,0)的原点移到(x,y),

  我们还可以在canvas画布中添加字体:

  

        parabolic.beginPath();
        parabolic.font = "25px Arial";
        parabolic.fillText("e瞳网", 20, 100);

  其中,font可以设置字体的大小,样式,而fillText()方法可以填充字体, 我们还可以使用strokeText()方法来填充。

  最后需要介绍的就是 save()方法restore()方法

  其中,前者可以保存当前环境的状态,而后者可以返回之前保存的状态。

  

  

最后,使用canvas练习了一个小的例子,但是对于计时器目前还没有完全的了解。

  可以肯定的是,计时器被输出时是一个数字, 在相同的环境下, 被调用一次, 计时器的这个数字就会加1, 另外, 计时器setInterval(func, time)中的第二个参数一般情况下我们最好不要设置太高,控制台16ms以上是最好的,因为一般的浏览器刷新率为16ms一次,所以这个速度是比较合适的。

  另外,在下面的这个例子中,我使用了这篇博文介绍的贝塞尔函数的计算用于控制小球的运动轨迹,效果如下:


  

 代码在我的github上。

  

  

   

  

  

   

  

  

  

原文地址:https://www.cnblogs.com/zhuzhenwei918/p/6535892.html