canvas[学习笔记二]

189 阅读1分钟

霓虹灯

<script>
    const canvas=document.getElementById('canvas');
    //canvas充满窗口
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    //画笔
    const  ctx=canvas.getContext('2d');

    //颜色数组
    const colors=['red','yellow'];

    function draw(){
        //保存上下文对象的状态
        ctx.save();
        //偏移坐标系
        ctx.translate(300,400);
        //开始绘制路径
        ctx.beginPath();
        //向路径集合中添加子路径
        //绘制心形子路径
        //设置路径起点
        ctx.moveTo(0,0);
        //用两个三次贝塞尔曲线组成爱心
        ctx.bezierCurveTo(
            -200,-50,
            -180,-300,
            0,-200
        );
        ctx.bezierCurveTo(180,-300,200,-50,0,0);
        //设置描边宽度
        ctx.lineWidth=10;
        //虚线
        ctx.setLineDash([30]);
        /*虚线1*/
        //描边颜色
        ctx.strokeStyle=colors[0];
        //以描边的方式显示路径
        ctx.stroke();
        /*虚线2*/
        ctx.strokeStyle=colors[1];
        ctx.lineDashOffset=30;
        ctx.stroke();
        //将上下文对象的状态恢复到上一次保存时的状态
        ctx.restore();
    }

    draw();
    setInterval(function(){
        colors.reverse();
        draw();
    },200)

</script>

霓虹灯.gif

霓虹灯光晕效果

<script>
    const canvas=document.getElementById('canvas');
    //canvas充满窗口
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    //画笔
    const  ctx=canvas.getContext('2d');

    //颜色数组
    const colors=['red','yellow'];

    function draw(){
        //保存上下文对象的状态
        ctx.save();
        //偏移坐标系
        ctx.translate(300,400);
        //开始绘制路径
        ctx.beginPath();
        //向路径集合中添加子路径
        //绘制心形子路径
        //设置路径起点
        ctx.moveTo(0,0);
        //用两个三次贝塞尔曲线组成爱心
        ctx.bezierCurveTo(
            -200,-50,
            -180,-300,
            0,-200
        );
        ctx.bezierCurveTo(180,-300,200,-50,0,0);
        //设置描边宽度
        ctx.lineWidth=10;
        //虚线
        ctx.setLineDash([30]);
        /*虚线1*/
        //描边颜色
        ctx.strokeStyle=colors[0];
        //以描边的方式显示路径
        ctx.stroke();
        /*虚线2*/
        ctx.strokeStyle=colors[1];
        ctx.lineDashOffset=30;
        //投影-光晕
        ctx.shadowColor='orange';
        for(let i=50;i>5;i-=5){
            ctx.shadowBlur=i;
            ctx.stroke();
        }
        //将上下文对象的状态恢复到上一次保存时的状态
        ctx.restore();
    }
    draw();
    setInterval(function(){
        // 必须清理画布,避免颜色一层层叠加
        ctx.clearRect(0,0,canvas.width,canvas.height);
        colors.reverse();
        draw();
    },200)
</script>

霓虹灯光晕效果.gif

文字编辑-布艺效果

<script>
    const canvas=document.getElementById('canvas');
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    const ctx=canvas.getContext('2d');

    /*文字内容*/
    const text='canvas';

    /*文字位置*/
    const [x,y]=[50,200];

    /*投影,颜色:rgba(0,0,0,0.6),垂直偏移:2,模糊:4*/
    ctx.shadowColor='rgba(0,0,0,0.6)';
    ctx.shadowOffsetY=2;
    ctx.shadowBlur=4;

    /*字体属性,文字加粗,大小:200px,字体:arial*/
    ctx.font='bold 200px arial';

    /*填充文字,颜色:#a76921*/
    ctx.fillStyle='#a76921';
    ctx.fillText(text,x,y);

    /*实线描边文字,颜色:#f0d5ac,线宽:8*/
    ctx.strokeStyle='#f0d5ac';
    ctx.lineWidth=8;
    ctx.strokeText(text,x,y);

    /*虚线描边文字,颜色:#333,线宽:1,线段长度[5,3]*/
    ctx.strokeStyle='#333';
    ctx.lineWidth=1;
    ctx.setLineDash([5,3]);
    ctx.strokeText(text,x,y);

</script>

布艺效果.jpg

图像

像素级操作图像

1、图像置灰

<script>
    const canvas=document.getElementById('canvas');
    //canvas充满窗口
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    const ctx=canvas.getContext('2d');

    const img=new Image();
    img.src='./images/river.jpg';
    img.onload=draw;

    /*灰度算法: 0.299*r+0.587*g+0.114*b */

    function draw(){
        //图像尺寸
        const {width,height}=img;

        /*1.在canvas 中绘制图像*/
        ctx.drawImage(img,0,0);

        /*2.从canvas 中获取图像的ImageData*/
        const imgDt=ctx.getImageData(0,0,width,height);
        const data=imgDt.data;

        /*像素遍历*/
        for(let i=0;i<data.length;i+=4){
            const [r,g,b]=[
                data[i],
                data[i+1],
                data[i+2],
            ]
            const lm=0.299*r+0.587*g+0.114*b;
            data[i]=lm;
            // data[i+1]=lm;
            // data[i+2]=lm;
        }

        /*3.在canvas 中显示ImageData*/
        ctx.putImageData(imgDt,0,height);

    }

</script>

图像置灰.jpg

2、马赛克效果

<script>
    const canvas=document.getElementById('canvas');
    //canvas充满窗口
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    const ctx=canvas.getContext('2d');

    //图像源
    const img=new Image();
    img.src='./images/wns.jpg';
    img.onload=render;

    //色块尺寸
    let size=0.5;

    function render() {
        //图像尺寸
        const {width,height}=img;

        /*1.在canvas 中绘制图像*/
        ctx.drawImage(img,0,0);

        /*2.从canvas 中获取图像的ImageData*/
        const imgDt=ctx.getImageData(0,0,width,height);
        const data=imgDt.data;

        /*行列遍历*/
        for(let y=0;y<height;y+=size){
            for(let x=0;x<width;x+=size){
                // 取另外的点的 RGB 值
                const i=(y*width+x)*4;
                const [r,g,b]=[
                    data[i],
                    data[i+1],
                    data[i+2],
                ]
                ctx.fillStyle=`RGB(${r},${g},${b})`;
                ctx.fillRect(x,y,size,size);
            }
        }
    }
</script>

马赛克效果.jpg

变换

1、时钟

时钟结构图.jpg

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>时钟</title>
    <style>
        body{margin: 0;overflow: hidden}
        #canvas{background: antiquewhite;}
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    const canvas=document.getElementById('canvas');
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;

    const ctx=canvas.getContext('2d');

    //色系
    const [redA,redB,yellow]=['#db655c','#d63d46','#9f8d7d'];

    //一圈的弧度
    const c=Math.PI*2;

    //canvas 宽高
    const {width,height}=canvas;

    !(function render(){
        ctx.clearRect(0,0,width,height);
        //绘图
        draw();
        requestAnimationFrame(render);
    })()

    function draw(){
        //保存状态
        ctx.save();

        //整体偏移
        ctx.translate(width/2,height/2);

        //整体旋转
        ctx.rotate(-Math.PI/2);

        // 钟表的表框-圆弧路径
        //内框 - 145,20
        //外框 - 155,9
        ctx.save();
        ctx.lineWidth=20;
        ctx.strokeStyle=redA;
        ctx.beginPath();
        ctx.arc(0,0,145,0,c);
        ctx.stroke();
        ctx.restore();
        
        ctx.save();
        ctx.lineWidth=9;
        ctx.strokeStyle=redB;
        ctx.beginPath();
        ctx.arc(0,0,155,0,c);
        ctx.stroke();
        ctx.restore();

        //15分钟一个的刻度 - 直线 - 90,120,4,15
        ctx.save();
        ctx.strokeStyle=redB;
        ctx.lineWidth=15;
        ctx.beginPath();
        for(let i=0;i<4;i++){
            ctx.moveTo(90,0);
            ctx.lineTo(120,0);
            ctx.rotate(c/4);
        }
        ctx.stroke();
        ctx.restore();

        //5分钟一个的刻度 - 直线 - 90,120,12,6
        ctx.save();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=6;
        ctx.beginPath();
        for(let i=0;i<12;i++){
            if(i%3){
                ctx.moveTo(90,0);
                ctx.lineTo(120,0);
            }
            ctx.rotate(c/12);
        }
        ctx.stroke();
        ctx.restore();

        //1分钟一个的刻度 - 直线 - 118,120,60,3
        ctx.save();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=3;
        ctx.beginPath();
        for(let i=0;i<60;i++){
            if(i%5){
                ctx.moveTo(118,0);
                ctx.lineTo(120,0);
            }
            ctx.rotate(c/60);
        }
        ctx.stroke();
        ctx.restore();


        //基于当前时间获取时、分、秒针的弧度
        const {rh,rm,rs}=getRadian();

        // 时针 - 直线 - -20,65,9
        ctx.save();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=9;
        ctx.rotate(rh);
        ctx.beginPath();
        ctx.moveTo(-20,0);
        ctx.lineTo(65,0);
        ctx.stroke();
        ctx.restore();

        // 分针 - 直线 - -28,80,4
        ctx.save();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=4;
        ctx.rotate(rm);
        ctx.beginPath();
        ctx.moveTo(-28,0);
        ctx.lineTo(80,0);
        ctx.stroke();
        ctx.restore();

        // 秒针 - 直线 - -30,88,2
        ctx.save();
        ctx.strokeStyle=redB;
        ctx.lineWidth=2;
        ctx.rotate(rs);
        ctx.beginPath();
        ctx.moveTo(-30,0);
        ctx.lineTo(88,0);
        ctx.stroke();
        ctx.restore();


        //圆弧 10
        ctx.save();
        ctx.fillStyle=redB;
        ctx.beginPath();
        ctx.arc(0,0,10,0,c);
        ctx.fill();
        ctx.restore();

        //还原上一次save 的状态
        ctx.restore();
    }

    //基于当前时间获取时、分、秒针的弧度
    function getRadian(){
        /*获取当前时间的时分秒*/
        //获取当前时间
        const date=new Date();
        //当前小时 getHours
        let h=date.getHours();
        if(h>12){h-=12}
        //当前分钟 getMinutes
        let m=date.getMinutes();
        //当前秒 getSeconds
        let s=date.getSeconds();

        /*基于时间比,计算指针在圆周中的弧度*/
        //时针旋转弧度
        const rh=c*h/12+c*m/12/60+c*s/12/60/60;
        //分针旋转弧度
        const rm=c*m/60+c*s/60/60;
        //秒针旋转弧度 30 30/60=0.5 0.5*Math.PI*2
        const rs=c*s/60;

        //返回三个弧度
        return {rh,rm,rs};
    }

</script>
</body>
</html>

时钟简版.gif

2、时钟版本-2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>时钟</title>
    <style>
        body{margin: 0;overflow: hidden}
        #canvas{background: antiquewhite;}
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    const canvas=document.getElementById('canvas');
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;

    const ctx=canvas.getContext('2d');

    //色系
    const [redA,redB,yellow]=['#db655c','#d63d46','#9f8d7d'];

    //一圈的弧度
    const c=Math.PI*2;

    //canvas 宽高
    const {width,height}=canvas;

    //图像源
    const img=new Image();
    img.src='./mat.jpg';

    !(function render(){

        ctx.clearRect(0,0,canvas.width,canvas.height);
        //绘图
        draw();
        requestAnimationFrame(render);
    })()

    function draw(){
        //保存状态
        ctx.save();

        //整体偏移
        ctx.translate(width/2,height/2);

        //整体旋转
        ctx.rotate(-Math.PI/2);

        // 钟表的表框-圆弧路径
        //内框 - 145,20
        const pat=ctx.createPattern(img,'repeat');
        ctx.save();
        ctx.beginPath();
        ctx.arc(0,0,145,0,c);
        ctx.strokeStyle=redA;
        ctx.fillStyle=pat;
        ctx.lineWidth=20;
        ctx.stroke();
        ctx.fill();
        ctx.restore();

        //外框 - 155,9
        ctx.save();
        ctx.beginPath();
        ctx.arc(0,0,155,0,c);
        ctx.strokeStyle=redB;
        ctx.lineWidth=9;
        ctx.stroke();
        ctx.restore();

        //15分钟一个的刻度 - 直线 - 90,120,4,15
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle=redB;
        ctx.lineWidth=15;
        for(let i=0;i<4;i++){
            ctx.moveTo(90,0);
            ctx.lineTo(120,0);
            ctx.stroke();
            ctx.rotate(c/4);
        }
        ctx.restore();

        //5分钟一个的刻度 - 直线 - 90,120,12,6
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=6;
        for(let i=0;i<12;i++){
            if(i%3){
                ctx.moveTo(90,0);
                ctx.lineTo(120,0);
                ctx.stroke();
            }
            ctx.rotate(c/12);
        }
        ctx.restore();

        //1分钟一个的刻度 - 直线 - 118,120,60,3
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=3;
        for(let i=0;i<60;i++){
            if(i%5){
                ctx.moveTo(118,0);
                ctx.lineTo(120,0);
                ctx.stroke();
            }
            ctx.rotate(c/60);
        }
        ctx.restore();


        //基于当前时间获取时、分、秒针的弧度
        const {rh,rm,rs,time}=getRadian();

        // 时针 - 直线 - -20,65,9
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=9;
        ctx.rotate(rh);
        ctx.moveTo(-20,0);
        ctx.lineTo(65,0);
        ctx.stroke();
        ctx.restore();

        // 分针 - 直线 - -28,80,4
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle=yellow;
        ctx.lineWidth=4;
        ctx.rotate(rm);
        ctx.moveTo(-28,0);
        ctx.lineTo(80,0);
        ctx.stroke();
        ctx.restore();

        // 秒针 - 直线 - -30,88,2
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle=redB;
        ctx.lineWidth=2;
        ctx.rotate(rs);
        ctx.moveTo(-30,0);
        ctx.lineTo(88,0);
        ctx.stroke();
        ctx.restore();

        //圆弧 10
        ctx.save();
        ctx.beginPath();
        ctx.fillStyle=redB;
        ctx.arc(0,0,10,0,c);
        ctx.fill();
        ctx.restore();

        //还原上一次save 的状态
        ctx.restore();

        //文字时间
        ctx.save();
        ctx.font='bold 150px Arial';
        ctx.textAlign='center';
        ctx.translate(width/2,50);

        //投影
        ctx.shadowColor='rgba(0,0,0,0.6)';
        ctx.shadowOffsetY=2;
        ctx.shadowBlur=4;

        //实体文字
        ctx.fillStyle='#a76921';
        ctx.fillText(time,0,150);

        //实体描边文字
        ctx.strokeStyle='#f0d5ac';
        ctx.lineWidth=6;
        ctx.strokeText(time,0,150);

        //虚线描边
        ctx.strokeStyle='#333';
        ctx.lineWidth=1;
        ctx.setLineDash([5,3]);
        ctx.strokeText(time,0,150);
        ctx.restore();
    }

    //基于当前时间获取时、分、秒针的弧度
    function getRadian(){
        /*获取当前时间的时分秒*/
        //获取当前时间
        const date=new Date();
        //当前小时 getHours
        let h=date.getHours();
        if(h>12){h-=12}
        //当前分钟 getMinutes
        let m=date.getMinutes();
        //当前秒 getSeconds
        let s=date.getSeconds();

        /*基于时间比,计算指针在圆周中的弧度*/
        //时针旋转弧度
        const rh=c*h/12+c*m/12/60+c*s/12/60/60;
        //分针旋转弧度
        const rm=c*m/60+c*s/60/60;
        //秒针旋转弧度
        const rs=c*s/60;

        if(h<10){h='0'+h}
        if(m<10){m='0'+m}
        if(s<10){s='0'+s}
        const time=h+':'+m+':'+s;

        //返回三个弧度
        return {rh,rm,rs,time};
    }

</script>
</body>
</html>

时钟升级版.gif