CanvasDay01: 绘图流程 绘制图形 绘制七巧板 实现电子时钟 倒计时

目录

0x01 Canvas的绘图流程

0x01 绘制线段:

0x03 绘制多边形:

0x04 绘制多个图形:

0x05 绘制点:

0x06 绘制弧线:

0x07 绘制圆形:

0x08 Demo:绘制七巧板

0x09 实现电子时钟:

0x0A 总结:

 


0x01 Canvas的绘图流程

  1. 编写canvas标签,注意指定宽高,如果不指定宽高,默认是300px宽,150px高。不建议使用css的方式,例如style 属性来设置canvas标签的宽高,因为css设置的只是canvas显示的大小。不能设置canvas画布分辨率的大小。采用width 和 height属性设置大小,相当于同时将两者设置了出来,是复合W3C的标准的。注意:数字之后是没有单位的。
  2. 获取 canvas DOM对象
  3. 获取 canvas 对象,即绘图的上下文环境
  4. 设置绘图属性
  5. 调用绘图API 例如 stroke fill 这些函数

案例:

准备工作:

  <canvas id="canvas" width='800' height='800'><strong>尊敬的用户,您的浏览器并不支持canvas,请更换浏览器!</strong></canvas>
    <script>
    
        const canvas = document.getElementById("canvas");
        //获取到canvas对象
        const ctx = canvas.getContext('2d');
        //如果标签中没有设置大小,也可以通过js的方式来指定canvas的大小
        //canvas.width = 800;
        //canvas.height = 800;

在canvas标签之间可以写上当用户的浏览器不支持canvas时,你希望用户看到什么。

当用户的浏览器支持canvas时,canvas标签之间的html代码将会完全被浏览器忽略掉。

也可以在js中实现这一点,如果canvas.getContext('2d')为空,那么就说明浏览器不支持canvas

    <script>
        var canvas = document.getElementById("canvas");
        if(canvas.getContext('2d')){
            var context = canvas.getContext('2d');
            //使用context绘图
        }else{
            alert("当前浏览器不支持canvas,请更换浏览器后再试");
        }
    
    </script>

canvas中的绘图是一种基于状态的绘图,即先设置绘图的状态、绘图的属性,然后再调用api进行具体的绘制

0x02 绘制线段:

多个lineTo连用,即可连续绘制线段
这里就可以体现出 canvas是一种基于状态的绘图,而不是一种基于对象的绘图

如果是基于对象的绘图,那么我们就应该创建出一个line对象,然后对line对象进行设置

而canvas对线条的种种设置都是对ctx对象的属性的设置。

        //开始绘制线段
        ctx.beginPath();
        //设置线条的宽度,单位px
        ctx.lineWidth = 1;
        //设置线的颜色,可以使用16进制,但是需要放在一个字符串中
        ctx.strokeStyle = 'blue';
        //线的起点
        ctx.moveTo(100,100);
        //中间点坐标
        ctx.lineTo(250,250);
        //终点坐标
        ctx.lineTo(300,100);
        //绘制线段
        ctx.stroke();

0x03 绘制多边形:

起点坐标 = 终点坐标 ,即可绘制多边形。

ctx.fillStyle = ’rgb(0,0,0)‘ 设置多边形的填充色

ctx.fill() 进行填充

            context.moveTo(512,200);
            context.lineTo(612,300);
            context.lineTo(500,500);
            context.lineTo(512,200);
            //填充颜色
            context.fillStyle = 'rgb(2,100,30)';
            context.fill();
            //绘制边框
            //也可以不调用一下方法,这样多边形就没有边框了
            context.lineWidth = 5;
            context.strokeStyle = '#005588';
            context.stroke();

0x04 绘制多个图形:

因为canvas是基于状态的,所以为了不让给上一个图形设置的状态 影响到 给下一个图形设置的状态,我们可以用

context.beginPath()  重新规划一个路径

context.closePath()  结束当前的路径(在api函数之前调用)

将上个图形的状态设置包裹起来,表明其中的状态只对该图形起作用。

注意:

当我们绘制的路径是一条不封闭的路径时,如果我们调用了closePath(),那么closePath()会自动将我们绘制的路径的首尾用线段连接起来。

所以可以只使用beginPath() 而不使用closePath();

           var context = canvas.getContext('2d');
            //使用context绘图
            context.beginPath();

            context.moveTo(512,200);
            context.lineTo(612,300);
            context.lineTo(500,500);
            context.lineTo(512,200);
           
            context.fillStyle = 'rgb(2,100,30)';
            context.fill();

            context.lineWidth = 5;
            context.strokeStyle = '#005588';
            context.closePath();
            
            context.stroke();

      

            context.beginPath();

            context.moveTo(200,100);
            context.lineTo(700,600);
            context.strokeStyle='black';
            context.closePath();
            context.stroke();
            
           

0x05 绘制点:

        //绘制点

        ctx.beginPath();
        ctx.lineWidth = 1;
        //线的颜色
        ctx.strokeStyle = 'blue';
        //线的起点
        ctx.moveTo(500,100);
        //终点坐标
        ctx.lineTo(501,101);
        //绘制线段
        ctx.stroke(); 

0x06 绘制弧线:

    /**
    * 绘制弧线
    * @ centerx 圆心x坐标
    * @ centery 圆心y坐标
    * @ radius 半径
    * @ startingAngle 起始弧度,0弧度为x轴正半轴的位置
    * @ endingAngle 终点弧度
    * @ false 顺时针绘制 true 逆时针绘制
    */
    context.arc(
        centerx,centery,radius,
        startingAngle,endingAngle,
        anticlockwise = false
    )

因为canvas是以向下为y轴的正半轴,所以90度位于正下方。

绘制弧形:调用closePath() 和不调用closePath() 的区别

效果:

代码:

<script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    for(var i=1;i<10;i++){
        ctx.beginPath();
        ctx.arc(50+100*i,100,30,0,2*Math.PI*i/10);

        ctx.lineWidth= 1;
        ctx.strokeStyle = '#003388';
        ctx.stroke();
    }
    for(var i=1;i<10;i++){
        ctx.beginPath();
        ctx.arc(50+100*i,200,30,0,2*Math.PI*i/10);
        
        ctx.lineWidth= 1;
        ctx.strokeStyle = '#003388';
        ctx.closePath();
        ctx.stroke();
     
    }
</script>

0x07 绘制圆形:

        //绘制圆形
        ctx.beginPath();
        ctx.lineWidth = 2;
        ctx.strokeStyle = 'green';
        ctx.fillStyle = 'red';
        /**
        *@ x轴坐标
        *@ y轴坐标
        *@ 半径
        *@ 起始角度
        *@ 结束角度
        */
        ctx.arc(400,400,50,0,2 * Math.PI);
        // 绘制圆形的边框
        ctx.stroke();
        // 绘制圆形的填充色
        ctx.fill();

0x08 Demo:绘制七巧板

效果:

思路:

先准备好七巧板每一片的各个点的坐标和颜色

然后写一个draw函数,作用是画出一片。

然后只要循环调用draw函数即可

代码:

 <canvas id="canvas" width='1024' height='600'></canvas>
    <script>
        function draw(piece,cxt){
            cxt.beginPath();
            cxt.moveTo(piece.p[0].x,piece.p[0].y);
            for(var i =1;i<piece.p.length;i++){
                cxt.lineTo(piece.p[i].x,piece.p[i].y);
            }
            cxt.fillStyle = piece.color;
            cxt.fill();
            cxt.closePath();
        }
        var tangram =[
            {
                p:[
                    {x:0,y:0},
                    {x:800,y:0},
                    {x:400,y:400}
                ],
                color:'#caff67'
            },
            {
                p:[
                    {x:0,y:0},
                    {x:400,y:400},
                    {x:0,y:800}
                ],
                color:'#67becf'
            },
            {
                p:[
                    {x:800,y:0},
                    {x:800,y:400},
                    {x:600,y:600},
                    {x:600,y:200}
                ],
                color:'#ef3d61'
            },
            {
                p:[
                    {x:600,y:200},
                    {x:600,y:600},
                    {x:400,y:400}
                ],
                color:'#f9f51a'
            },
            {
                p:[
                    {x:400,y:400},
                    {x:600,y:600},
                    {x:400,y:800},
                    {x:200,y:600}
                ],
                color:'#a594c0'
            },
            {
                p:[
                    {x:200,y:600},
                    {x:400,y:800},
                    {x:0,y:800}
                ],
                color:'#fa8ecc'
            },
            {
                p:[
                    {x:800,y:400},
                    {x:800,y:800},
                    {x:400,y:800}
                ],
                color:'#f6ca29'
            }
        ]
        var canvas = document.getElementById("canvas");
        if(canvas.getContext('2d')){
            var context = canvas.getContext('2d');
            for(var i =0;i<tangram.length;i++){
                draw(tangram[i],context);
            }
        }else{
            alert("当前浏览器不支持canvas,请更换浏览器后再试");
        }

    </script>

0x09 实现电子时钟:

效果:

思路:

格子系统

代码:

digit.js

digit =
    [
        [
            [0,0,1,1,1,0,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,0,1,1,0],
            [0,0,1,1,1,0,0]
        ],//0
        [
            [0,0,0,1,1,0,0],
            [0,1,1,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [1,1,1,1,1,1,1]
        ],//1
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,1,1],
            [1,1,1,1,1,1,1]
        ],//2
        [
            [1,1,1,1,1,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//3
        [
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,0],
            [0,0,1,1,1,1,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,1,1,0],
            [1,1,1,1,1,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,1]
        ],//4
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,1,1,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//5
        [
            [0,0,0,0,1,1,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//6
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0]
        ],//7
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//8
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,1,1,0,0,0,0]
        ],//9
        [
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0]
        ]//:
    ];

时钟.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="js/digit.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <style>
        /* #canvas{
            border:1px solid black;
            position:absolute;
            left:50%;
            top:50%;
            transform: translate(-50%,-50%);
        } */
        html,body{
            height:100%;
        }
    </style>
</head>
<body>
        <canvas id='canvas' style='height:100%' ></canvas>

   
</body>
<script>
var WINDOW_WIDTH = 1024;
var WINDOW_HEIGHT = 768;
//小球半径
var RADIUS = 8;
//每个数字距离画布上边距的距离
var MARGIN_TOP = 60;
//第一个数字距离画布左边距的距离
var MARGIN_LEFT = 30;
//倒计时
//js中月份比较特别,0表示1月,所以6月就是5
// const endTime = new Date(2020,5,18,12,0,0);
//1小时学习计时器
//当前时间
var endTime = new Date();
//当前时间之后的一个小时
//setTime接收的参数为时间戳,getTime获取当前时间的时间戳
endTime.setTime(endTime.getTime() + 3600000)
var curShowTimeSeconds = 0;

var balls = [];
const colors = [
    '#33B5E5','#0099CC','#AA66CC','#9933CC','#99CC00',
    '#669900','#FFBB33','#FF8800','#FF4444','#C000C'
]

//返回当前应该显示时间,即倒计时时间,单位为秒
function getCurrentShowTimeSeconds(){
    var curTime = new Date();
    var ret = curTime.getHours() * 3600 + curTime.getMinutes() *60 +curTime.getSeconds();
    // 保证倒计时结束时屏幕显示为0
    return ret >= 0? ret:0;
}
//负责数据的改变
function update(){
    var nextShowTimeSeconds = getCurrentShowTimeSeconds();
    var next_hours = parseInt(nextShowTimeSeconds/3600);
    var next_minutes = parseInt((nextShowTimeSeconds - next_hours*3600)/60);
    var next_seconds = parseInt(nextShowTimeSeconds % 60); 
    var cur_hours = parseInt(curShowTimeSeconds/3600);
    var cur_minutes = parseInt((curShowTimeSeconds - cur_hours*3600)/60);
    var cur_seconds = parseInt(curShowTimeSeconds % 60);
    if(next_seconds != cur_seconds){
        //如果小时的十位数发生了改变,就在十位数所在的位置添加小球
        if(parseInt(cur_hours/10)!=parseInt(next_hours/10)){
            addBalls(MARGIN_LEFT+0,MARGIN_TOP,parseInt(cur_hours/10));
        }
        //如果小时的个位数发生了变化,就在个位数所在的位置添加小球
        if(parseInt(cur_hours%10)!=parseInt(next_hours%10)){
            addBalls(MARGIN_LEFT+15*(RADIUS+1),MARGIN_TOP,parseInt(cur_hours%10));
        }
        //对分钟
        if(parseInt(cur_minutes/10)!=parseInt(next_minutes/10)){
            addBalls(MARGIN_LEFT+39*(RADIUS+1),MARGIN_TOP,parseInt(cur_minutes/10));
        }
        if(parseInt(cur_minutes%10)!=parseInt(next_minutes%10)){
            addBalls(MARGIN_LEFT+54*(RADIUS+1),MARGIN_TOP,parseInt(cur_minutes%10));
        }
        //对秒钟
        if(parseInt(cur_seconds/10)!=parseInt(next_seconds/10)){
            addBalls(MARGIN_LEFT+78*(RADIUS+1),MARGIN_TOP,parseInt(cur_seconds/10));
        }
        if(parseInt(cur_seconds%10)!=parseInt(next_seconds%10)){
            addBalls(MARGIN_LEFT+93*(RADIUS+1),MARGIN_TOP,parseInt(cur_seconds%10));
        }
        curShowTimeSeconds = nextShowTimeSeconds;
    }
    //对已经存在彩色小球进行更新
    updateBalls();
}
//更新小球数组中 小球信息
function updateBalls(){
    for(var i=0;i<balls.length;i++){
        balls[i].centerX += balls[i].vx;
        balls[i].centerY += balls[i].vy;
        balls[i].vy += balls[i].g;
            //地板的碰撞检测
        if(balls[i].centerY >= WINDOW_HEIGHT-RADIUS){
            balls[i].centerY = WINDOW_HEIGHT - RADIUS;
            balls[i].vy = - balls[i].vy * 0.75;
        }
    }
    //记录画布中的小球的数量
    var cnt =0;
    for(var i=0;i<balls.length;i++){
        if(balls[i].centerX + RADIUS >0 && balls[i].centerX + RADIUS <WINDOW_WIDTH){
            balls[cnt++] = balls[i];
        }
    }
    
    //将画布外的小球从balls中删除
    while(balls.length > Math.min(300,cnt)){
        balls.pop();
    }

}
//添加彩色的小球
function addBalls(x,y,num){
    for(var i =0;i<digit[num].length;i++){
        for(var j=0;j<digit[num][j].length;j++){
            if(1 == digit[num][i][j]){
                var aBall ={
                    centerX:x + j*2*(RADIUS+1)+(RADIUS+1),
                    centerY:y + i*2*(RADIUS+1)+(RADIUS+1),
                    g:1.5+Math.random(),//1.5+ 0到1之间的一个随机数
                    vx:Math.pow(-1,Math.ceil(Math.random()*1000))*4,//-1的多少次方 * 4  多少次方呢 在0到1000之前取一个随机数然后取整,最终结果为正负四
                    vy:-5,
                    color:colors[Math.floor(Math.random()*colors.length)] //随机索引 0-9 来 获取随机颜色
                }
                balls.push(aBall)
                
            }
        }
    } 
}
/*
绘制数字
x 数字盒子 左上角 的x坐标
y 数字盒子 左上角 的y坐标
num 绘制什么数字
ctx canvas对象
*/
function renderDigit(x,y,num,ctx){
    ctx.fillStyle = 'rgb(0,102,153)';
    for(var i =0;i<digit[num].length;i++){
        for(var j=0;j<digit[num][j].length;j++){
            if(1 == digit[num][i][j]){
                //绘制小球
                ctx.beginPath();
                //算出小球球心的位置
                var centerX = x + j*2*(RADIUS+1)+(RADIUS+1);
                var centerY = y + i*2*(RADIUS+1)+(RADIUS+1);
                ctx.arc(centerX,centerY,RADIUS,0,2*Math.PI);
                ctx.closePath();
                ctx.fill();
            }
        }
    }
}

//绘制函数(绘制数字,绘制彩色小球)
function render(ctx){
    // 防止之前的图像 和 新的图像叠加在一起
    //对一个矩形空间的图像进行刷新
    ctx.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);
    var date = new Date();
    var hours = parseInt(curShowTimeSeconds/3600);
    var minutes = parseInt((curShowTimeSeconds - hours*3600)/60);
    var seconds = parseInt(curShowTimeSeconds % 60);
    //绘制小时
    renderDigit(MARGIN_LEFT,MARGIN_TOP,parseInt(hours/10),ctx);
    renderDigit(MARGIN_LEFT + 15*(RADIUS+1),MARGIN_TOP,parseInt(hours%10),ctx);
    //绘制冒号
    renderDigit(MARGIN_LEFT + 30*(RADIUS+1),MARGIN_TOP,10,ctx);
    //绘制分钟
    renderDigit(MARGIN_LEFT + 39*(RADIUS+1),MARGIN_TOP,parseInt(minutes/10),ctx);
    renderDigit(MARGIN_LEFT + 54*(RADIUS+1),MARGIN_TOP,parseInt(minutes%10),ctx);
    //绘制冒号
    renderDigit(MARGIN_LEFT + 69*(RADIUS+1),MARGIN_TOP,10,ctx);
    //绘制秒钟
    renderDigit(MARGIN_LEFT + 78*(RADIUS+1),MARGIN_TOP,parseInt(seconds/10),ctx);
    renderDigit(MARGIN_LEFT + 93*(RADIUS+1),MARGIN_TOP,parseInt(seconds%10),ctx);

    //遍历小球数组,绘制每个小球
    for(var i =0;i<balls.length;i++){
        ctx.fillStyle = balls[i].color;

        ctx.beginPath();
        ctx.arc(balls[i].centerX,balls[i].centerY,RADIUS,0,2*Math.PI,true);
        ctx.closePath();

        ctx.fill();
    }
}

window.onload = function(){

    //必须先设置body的高度为100%,才能获得正确的高度
    WINDOW_WIDTH =document.body.clientWidth;
    WINDOW_HEIGHT =document.body.clientHeight;
    // console.log(document.body.clientHeight);
    MARGIN_LEFT = Math.round(WINDOW_WIDTH / 10);
    MARGIN_TOP = Math.round(WINDOW_HEIGHT /5);
    //因为最后一个数字距离左边距的位置是93倍的(RADIUS+1)一个数字占据的空间是15个(RADIUS+1)
    RADIUS = Math.round(WINDOW_WIDTH * 8/10 /108 )-1

    var canvas = document.getElementById('canvas');
    canvas.width = WINDOW_WIDTH;
    canvas.height = WINDOW_HEIGHT;
    ctx = canvas.getContext('2d');
    curShowTimeSeconds = getCurrentShowTimeSeconds();
    /**
     * 每帧要执行的函数
     * 每帧多少毫秒,帧率 =  1s / 每帧的时间
    */
    setInterval(
        function(){
            render(ctx);
            update();
        },20
    )
 
}
</script>
</html>

0x0A 总结:

context.clearRect(x,y,width,height) 清除一个矩形区域的内容

context.canvas 得到context上下文属于的画布

context.canvas.wdith  获取画布的大小

context.canvas.height

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值