• canvas的用法


    包括:
    介绍。
    基础入门。(兼容性。获取canvas上下文。绘制直线/描边,填充内容。绘制表格。)
    canvas是基于状态的绘图。
    绘制矩形。
    绘制圆形。
    绘制文本。
    绘制图片。
    阴影。
    渐变。
    绘制背景图。
    变换。



    介绍:
    HTML5的新标签<canvas></canvas>

    canvas标签通过JavaScript在网页上绘制图像,本身不具备绘图功能。
    canvas拥有多种绘制路径,矩形,圆形,字符以及添加图像的方法。

    canvas可以做游戏,图表,广告等等。

    基础入门。
    设置canvas的宽和高是通过canvas标签的属性来设置,不要用css设置。

    <canvas id="canvas" width="400" height="400"></canvas>
    


    还可以在JS中通过属性指定。

    var canvas = document.getElementById('canvas');  //获取canvas标签
    canvas.width=400;
    canvas.height=400;
    



    兼容性。
    当浏览器不支持canvas时,在canvas标签内编写提示文字。

    <canvas>你的浏览器不支持canvas,请更换浏览器后再试。</canvas>
    


    (当浏览器支持canvas时,canvas会执行相关JS代码)

    获取canvas上下文。
    canvas上下文就是绘制工具的集合。

    写在canvas标签后,或者window.onload = function(){}

    var canvas = document.getElementById('canvas');  //获取canvas标签
    var ctx= canvas.getContext('2d');  //获取canvas的上下文  3D=>'webgl'
    



    得到canvas上下文后就可以开始绘制了。
    canvas的坐标系和浏览器的坐标系一致。都是左上角为(0,0)。

    绘制直线/描边,填充内容。

    ctx.moveTo(100,100);  //画笔移动到(100,100)
    ctx.lineTo(200,100);  //画一条直线到(200,100)
    ctx.lineTo(100,200);  //画一条直线到(100,200)
    //到此为止,只绘制了路径,还没描线。
    
    ctx.stroke();  //描线。
    




    将路径闭合。

    ctx.moveTo(100,100);  //画笔移动到(100,100)
    ctx.lineTo(200,100);  //画一条直线到(200,100)
    ctx.lineTo(100,200);  //画一条直线到(100,200)
    
    ctx.lineTo(100,100);  //画一条直线到(100,100)
    
    ctx.stroke();  //描线。
    



    还可以使用ctx.closePath()来闭合路径。

    ctx.moveTo(100,100);  //画笔移动到(100,100)
    //必须要写moveTo(),否则画笔没有位置。
    //没有写moveTo()直接写lineTo()的话,画笔的位置直接到lineTo()的位置,相当于moveTo()
    
    ctx.lineTo(200,100);  //画一条直线到(200,100)
    ctx.lineTo(100,200);  //画一条直线到(100,200)
    
    ctx.closePath()  //闭合路径
    
    ctx.stroke();  //描线。
    


    都是一样的效果:


    修改描边的样式。在stroke()描边之前设置描边样式。

    ctx.lineWidth = 4;  //线宽
    ctx.strokeStyle = 'red';  //颜色
    




    填充内容。默认是黑色。修改填充的样式。

    ctx.fill();
    ctx.fillStyle = 'blue';  //颜色
    




    问题:填充后,为什么描边变细?
    答案:描边是2像素在里面2像素在外边。填充后,里面的2像素被遮盖住了。


    绘制表格。

    var rectH = 10;  //行高
    var rectW = 10;  //列宽
    //绘制横线:
    for(var i = 0;i< canvas.width / rectH;i++){
    	ctx.moveTo(0,i*rectH);
    	ctx.lineTo(canvas.width,i*rectH);
    	//绘制竖线
    	for(var j = 0;j<canvas.height / rectW;j++){
    		ctx.moveTo(j*rectW,0);
    		ctx.lineTo(j*rectW,canvas.height);
    	}
    }
    ctx.lineWidth = 0.5;
    ctx.strokeStyle = '#ccc';
    ctx.stroke();
    



    在这基础上就可以绘制折线图了。

    绘制箭头的思路:

    绘制数据的思路:

    var x0 = 100,y0 = 500;
    var maxHeight = 300;
    var arrowWidth = 10;// 箭头宽度
    
    // 绘制x轴
    ctx.beginPath();
    ctx.strokeStyle = "blue";
    ctx.moveTo(x0, y0);
    ctx.lineTo(500, 500);
    
    // 箭头
    ctx.lineTo(500-arrowWidth, 500-arrowWidth);
    ctx.moveTo(500, 500);
    ctx.lineTo(500-arrowWidth, 500+arrowWidth);
    ctx.stroke();
    
    //绘制y轴
    ctx.beginPath();
    ctx.strokeStyle = "purple";
    ctx.moveTo(x0, y0);
    ctx.lineTo(100, 100);
    
    // 箭头
    ctx.lineTo(100-arrowWidth, 100+arrowWidth);
    ctx.moveTo(100, 100);
    ctx.lineTo(100+arrowWidth, 100+arrowWidth);
    ctx.stroke();
    
    // 绘制线段
    var data = [.4 ,.5 ,.8 ,.7];
    var pointWidth = 400 / (data.length + 1);
    ctx.beginPath();
    ctx.strokeStyle = "red";
    for(var i=0;i<data.length;i++){
    	var x =x0 +(i + 1) * pointWidth;
    	var y =y0 - data[i] * maxHeight;
    	ctx.lineTo(x, y);
    }
    ctx.stroke();
    






    canvas是基于状态的绘图。
    比如绘制两条不同颜色的直线。

    ctx.strokeStyle = 'red';
    ctx.lineWidth=5;
    
    //第一条线
    ctx.moveTo(100,100);
    ctx.lineTo(300,100);
    ctx.stroke();
    
    //第二条线
    ctx.strokeStyle = 'blue';
    ctx.moveTo(100,200);
    ctx.lineTo(300,200);
    ctx.stroke();
    





    问题:两条线颜色为什么一样?
    解决方法:canvas是基于状态的绘图。设置ctx.beginPath()即可。

    //第二条线
    ctx.beginPath();
    
    ctx.strokeStyle = 'blue';
    ctx.moveTo(100,200);
    ctx.lineTo(300,200);
    ctx.stroke();
    





    beginPath()相当于开启新状态。

    绘制第一条线的时候也可以beginPath(),默认一开始就有状态。
    绘制第二条线的新状态,可以继承之前的状态的样式,但是当前的状态设置的所有样式,只能作用于当前的状态。

    绘制矩形。
    绘制矩形:ctx.rect(x,y,w,h);  //左上角坐标(x,y),w宽,h高。

    ctx.rect(50,50,50,50);
    ctx.stroke();
    
    ctx.strokeRect(120,120,50,50);  //和上面写法的效果一致。
    
    ctx.fillRect(190,190,50,50);  //如果是fill(填充),会自动闭合路径。
    





    清除矩形:相当于橡皮擦。
    ctx.clearRect(x,y,w,h);

    ctx.clearRect(195,195,30,30);
    





    绘制圆形。

    ctx.arc(x,y,r,startAngle,endAngle,counterclockwise); 
    //圆心坐标(x,y),半径r,开始弧度,结束弧度,顺时针/逆时针(默认顺时针如图)
    





    换算公式:rad = deg*Math.PI/180;

    绘制0-30°的圆弧。

    ctx.arc(100,100,100,0,30*Math.PI/180,false);





    加上closePath()会闭合路径。



    如果要连接到圆心点。先moveTo(圆心点)即可。

    ctx.moveTo(100,100)
    ctx.arc(100,100,100,0,30*Math.PI/180,false);
    ctx.closePath();
    ctx.stroke();
    





    绘制饼状图的其中一个扇形。

    ctx.moveTo(200,200);
    ctx.fillStyle = 'red'
    ctx.arc(200,200,100,-90*Math.PI/180,-30*Math.PI/180,false);
    ctx.fill()
    





    假设有这样一个JSON数据。

    var data =[{
    "value":.2,
    "color":"red",
    "title":"应届生"
    },{
    "value":.3,
    "color":"blue",
    "title":"社会招生"
    },{
    "value":.4,
    "color":"green",
    "title":"推荐"
    },{
    "value":.1,
    "color":"yellow",
    "title":"公开课"
    }];
    



    将数据变成饼状图。
    value代表占比。从-90°开始绘制。

    //先画扇形。
    var tempAngle = -90;  //从-90度开始
    var x0 = 150,y0 = 150,radius = 100;  //圆心和半径
    for(var i=0;i<data.length;i++){
    	ctx.beginPath();
    	ctx.moveTo(x0,y0);  //圆心
    	var angle = data[i].value*360;  //当前扇形的角度
    	ctx.fillStyle = data[i].color;
    
    	var startAngle = tempAngle * Math.PI/180;  //开始角度
    	var endAngle = (tempAngle + angle) * Math.PI/180;  //结束角度
    
    	ctx.arc(x0,y0,radius,startAngle,endAngle)
    	ctx.fill();
    
    	tempAngle+=angle;
    }
    





    绘制文本。
    ctx.strokeText('hello',450,400); //文字,坐标
    ctx.fillText('hello',150,100);

    ctx.moveTo(300,300);
    ctx.fillStyle = 'purple';
    ctx.font = '20px 微软雅黑';
    ctx.textBaseline = "bottom";  //基线
    ctx.textAlign = "left";
    // ctx.strokeText('hello',450,400);  //空心文字
    ctx.fillText('hello',100,300);  //实体填充文字
    




    行高行距的概念:


    对齐方式:


    饼状图的文字。假设,在角度的一半绘制一条直线出来,写上XX%,思路如图。



    绘制完饼状图后,fill前。绘制文字。

    // 绘制文字
    	var txt = data[i].value * 100 +'%';
    	var x,y;
    	var textAngle = tempAngle + 1/2 *angle;
    	x=x0+Math.cos(textAngle * Math.PI / 180) * (radius + 20);
    	y=y0+Math.sin(textAngle * Math.PI / 180) * (radius + 20);
    
    // 左侧文字太长会越过饼状图。
    	if(textAngle>90&&textAngle<270){
    		ctx.textAlign = "end"
    	}
    
    	ctx.fillText(txt,x,y);
    



    文字会超过:

    文字不会超过:

    ctx.measureText()  //measure测量;返回文本的宽度
    




    绘制图片:

    ctx.drawImage(img,x,y);  //img是图片的DOM对象。  绘制的坐标。
    


    // 创建图片的dom对象
    var img = new Image();
    img.src='img.jpg';  //只要设置了src属性,当前img对象立即去加载图片。
    
    img.onload = function(){
    	// 图片加载完成后,绘制图片
    	ctx.drawImage(img,100,100);
    }
    



    以上的img和以下的方法获得的img是一样的,都是dom对象。

    var img2 = document.getElementById('imagedemo');
    





    恶搞✧(≖ ◡ ≖✿)

    for(var i=0;i<10;i++){
    	ctx.drawImage(img,100+i*10,100+i*10);
    }
    





    还可以设置宽高,不设置的时候是图片的默认宽高。

    ctx.drawImage(img,x,y,w,h);  //w,h,宽高。
    


    ctx.drawImage(img,100,100,50,50);
    



    被拉伸:


    如果要保持宽高比,则 原来的高度 / 原来的宽度 = 绘制的高度 / 绘制的宽度 。
    假设已知绘制的宽度,则绘制的高度为 原来的高度*绘制的宽度/原来的宽度。

    var ow = img.width;
    var oh = img.height;
    ctx.drawImage(img,100,100,200,200*oh/ow);
    





    绘制图片裁剪区域。

    ctx.drawImage(img,sx,sy,sw,sh,x,y,w,h)  //截取的坐标。截取的宽高。绘制的坐标。绘制的宽高。
    



    比如:网上找的一张图:截取出第一个人物:

    ctx.drawImage(img,195,26,276,377,100,100,200,300);
    




    逐帧动画/序列帧动画:定时器。


    先做第一行的动作。

    var frameIndex = 0; //帧数
    setInterval(function(){
    	// 清除之前的内容
    	ctx.clearRect(0, 0, canvas.width, canvas.height);
    	// 如果通过代码重新设置canvas画布的宽高,canvas画布里的所有内容都被清空。(不建议使用)
    	//canvas.width = canvas.width;
    	ctx.drawImage(
    		img,
    		frameIndex*53.25,  //截取的坐标
    		0,  //截取的坐标
    		53.25,  //截取的宽
    		92.75,  //截取的高
    		200,  //绘制的坐标
    		200,  //绘制的坐标
    		53.25*2,  //绘制的宽
    		92.75*2  //绘制的高
    		)
    	frameIndex++;
    	frameIndex%=4;  //取余  4%4=0
    },1000/10);  //1秒10帧。
    





    添加四个方向的按钮。

    <button id="btn-left">left</button>
    <button id="btn-right">right</button>
    <button id="btn-top">top</button>
    <button id="btn-down">down</button>
    
    var btnLeft = document.getElementById('btn-left')
    var btnRight = document.getElementById('btn-right')
    var btnTop = document.getElementById('btn-top')
    var btnDown = document.getElementById('btn-down')
    




    在绘制图片前(new Image()前)。设置方向。相当于图片的第一行。代表向下走。

    var dirIndex = 0;
    


    然后将截取的y坐标改为dirIndex*92.75,绑定相关的按钮事件。

    btnLeft.onclick = function(){
    	dirIndex =1 ;  //第2行
    }
    btnRight.onclick = function(){
    	dirIndex =2 ;  //第3行
    }
    btnTop.onclick = function(){
    	dirIndex =3 ;  //第4行
    }
    btnDown.onclick = function(){
    	dirIndex =0 ;  //第1行
    }
    



    点击效果:



    阴影

    // 设置阴影
    ctx.fillStyle = 'red';
    ctx.shadowColor = 'teal';  //颜色
    ctx.shadowBlur = 10;  //模糊 ( 大于1 )
    ctx.shadowOffsetX = 10;  //偏移
    ctx.shadowOffsetY = 10;
    ctx.fillRect(100,100,100,100);
    




    渐变

    // 线性渐变
    var grd = ctx.createLinearGradient(0, 0, 170, 0);
    grd.addColorStop(0,"black");
    grd.addColorStop(0.5,"red");
    grd.addColorStop(1,'white');
    ctx.fillStyle = grd;
    ctx.fillRect(0, 0, 300, 300);
    



    // 圆形渐变
    var rlg = ctx.createRadialGradient(300, 300, 10, 300, 300, 200);
    rlg.addColorStop(0,"white");
    rlg.addColorStop(0.5,"red");
    rlg.addColorStop(1,'black');
    ctx.fillStyle = rlg;
    ctx.fillRect(100, 100, 400, 400);
    




    绘制背景图

    var pat = ctx.createPattern(img,repeat);  //img是DOM对象
    ctx.rect(0, 0, 150, 100);
    ctx.fillStyle = pat;
    ctx.fill();
    



    变换

    ctx.scale(Scale Width, Scale Height);  //缩放当前绘图  1为100%
    ctx.translate(X, Y);  //位移画布
    ctx.rotate(Rotate Angle);  //旋转当前绘图
    ctx.save();  //保存当前环境的状态
    ctx.restore();  //返回之前保存过的路径状态和属性
    ctx.globalAlpha = "Value between 0 & 1";  //绘制环境的透明度
    



    例子:

    // 状态1
    ctx.fillStyle = "red";
    ctx.fillRect(10, 10, 100, 100);
    
    ctx.save();  //保存状态
    ctx.translate(200, 200);  //把当前画布移动到200,200的位置
    ctx.rotate(30* Math.PI / 180);  //旋转
    ctx.scale(2, 2);  //缩放
    ctx.globalAlpha = ".3";  //透明度
    
    ctx.moveTo(0, 0);
    ctx.lineTo(400, 0);
    ctx.moveTo(0, 0);
    ctx.lineTo(0, 400);
    ctx.stroke();
    ctx.fillRect(10, 10, 40, 40);
    ctx.restore();  //返回之前保存过的路径状态和属性
    
    ctx.fillRect(400, 400, 100, 100);
    



  • 相关阅读:
    hash的简单应用
    2273: 蓝桥杯第八届省赛正则问题
    Git 消息提交规范
    监听数据变化
    微信小程序组件传参和交互
    vue3 setup语法糖
    GitBook——制作精美的电子书、产品文档、知识库、个人项目、API文档等,并「分享」
    ps镜像翻转单个图层
    windows10或server没有「启动快速启动」的选项的解决方案
    win10驱动程序安装失败的解决方案(禁用驱动程序强制签名)
  • 原文地址:https://www.cnblogs.com/hiuman/p/7347394.html
Copyright © 2020-2023  润新知