canvas的一些基础理解

canvas

套用官方的介绍
HTML5 元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成.
标签只是图形容器,您必须使用脚本来绘制图形。
你可以通过多种方法使用 canvas 绘制路径,盒、圆、字符以及添加图像。

兼容性

表格中的数字表示支持 元素的第一个浏览器版本号。

image

基本画图

直线

基本直线

知识点:lineWidth 设置线条宽度








ctx.lineWidth = 5;  //增
ctx.moveTo(80, 80);
ctx.lineTo(300, 300);
ctx.stroke();
image

三角形

知识点:closePath 不是结束路径,而是关闭路径,它会试图从当前路径的终点连一条路径到起点,让整个路径闭合起来

有了这玩意,就可以做一个标准的三角形,友情提示可以提高线条宽度,注释ctx.closePath()看看效果

不加ctx.closePath()就会出现如下图这样的效果

ctx.lineWidth = 5;  //增
ctx.moveTo(80, 80);
ctx.lineTo(300, 300);
ctx.lineTo(100, 350);
ctx.closePath();
ctx.stroke();
image

image

不同颜色的线条

知识点:beginPath

每次stroke都会去找最近的beginPath,然后进行绘制

绘制不同样式的时候条

ctx.lineWidth = 20;
ctx.strokeStyle = 'red';
ctx.moveTo(50, 100);
ctx.lineTo(50, 300);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 10;
ctx.strokeStyle = 'blue';
ctx.moveTo(300, 100);
ctx.lineTo(300, 300);
ctx.stroke();
image

image

这段代码显示的一条红线和蓝线,大家会想,这有什么好说的呢,肯定是这样啊。

接下来我们把第二个的context.stroke()注释起来,你会发现最后的结果只是一条蓝线,因为第一个路径没有stroke()就重新开辟了一个路径,使得只能画了第二个路径的直线,大家是不是发现越来越神奇了呢?

紧接着我把不注释context.stroke(),而把context.beginPath()注释起来,你发现什么了?很奇妙,你发现了一条紫线和蓝线,为什么呢?原来是因为没有开辟新的路径,所以第二个stroke()又重新画了第一条直线,并且用了蓝色,而第一个stroke()已经用红色画了第一条直线了,蓝色和红色的结合就变成了紫色。

你想想,如果把beginPath()和stroke()都注释起来会怎么样呢?两条蓝色的直线,you are good

现在应该是stroke是怎么个画法了吧

曲线

常规圆弧线

参数介绍:(原点x轴,原点y轴,半径,起点,终点,true/false)

true 逆时针 , false 顺时针

ctx.beginPath();
ctx.arc(150, 150, 120, 1.5 * Math.PI, 1 * Math.PI, false);    // 顺时针
ctx.stroke();
ctx.closePath();
image

贝塞尔曲线

二次方贝塞尔曲线
二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪:

image

为建构二次贝塞尔曲线,可以中介点Q0和Q1作为由0至1的t:
由P0至P1的连续点Q0,描述一条线性贝塞尔曲线。
由P1至P2的连续点Q1,描述一条线性贝塞尔曲线。
由Q0至Q1的连续点B(t),描述一条二次贝塞尔曲线。

image
ctx.strokeStyle ="#FF5D43";
ctx.beginPath();
ctx.moveTo(0,200);
ctx.quadraticCurveTo(75,50,300,200);
ctx.stroke();
//  ctx.strokeStyle = '#f0f';
//  ctx.beginPath();
//  ctx.moveTo(75,50);
//  ctx.lineTo(0,200);
//  ctx.moveTo(75,50);
//  ctx.lineTo(300,200);
//  ctx.stroke();
image

image
矩形

基本矩形

ctx.fillStyle = '#FF0000';
ctx.fillRect(0, 220, 100, 100);
image

巧妙利用globalCompositeOperation属性复制一个矩形

globalCompositeOperation属性介绍

source-over 默认。在目标图像上显示源图像。

source-atop 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。

source-in 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。

source-out 在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。

destination-over 在源图像上方显示目标图像。

destination-atop 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。

destination-in 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。

destination-out 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。

lighter 显示源图像 + 目标图像。

copy 显示源图像。忽略目标图像。

xor 使用异或操作对源图像与目标图像进行组合。

ctx.fillStyle="red";
ctx.fillRect(20,20,75,50);
ctx.globalCompositeOperation="source-over";
ctx.fillStyle="blue";
ctx.fillRect(50,50,75,50);

效果图


image

空心圆

ctx.beginPath();
ctx.arc(200,200,50,0,360,false);
ctx.lineWidth=5;
ctx.strokeStyle="green";
ctx.stroke();

效果图


image

实心圆

ctx.beginPath();
ctx.arc(200,100,50,0,360,false);
ctx.fillStyle="red"; //填充颜色,默认是黑色
ctx.fill();

效果图


image
文字
ctx.fillStyle = 'red';
ctx.font = "bold 40px '微软雅黑'"; //设置字体
ctx.fillText('hello world', 10, 40); //设置文本内容

效果图


image

如果你想文本自动换行,下面看具体实现方法

/*
str:要绘制的字符串
canvas:canvas对象
initX:绘制字符串起始x坐标
initY:绘制字符串起始y坐标
lineHeight:字行高,自己定义个值即可
*/
function canvasTextAutoLine(str,canvas,initX,initY,lineHeight){
    var ctx = canvas.getContext("2d");
    var lineWidth = 0;
    var canvasWidth = c.width;
    var lastSubStrIndex= 0;
    for(let i=0;icanvasWidth-initX){//减去initX,防止边界出现的问题
            ctx.fillText(str.substring(lastSubStrIndex,i),initX,initY);
            initY+=lineHeight;
            lineWidth=0;
            lastSubStrIndex=i;
        }
        if(i==str.length-1){
            ctx.fillText(str.substring(lastSubStrIndex,i+1),initX,initY);
        }
    }
  }
图片
var imgObj = new Image();
imgObj.src = "https://fe.yingyinglicai.com/h5-projects/blog/canvas/20180529_16.jpg";
// imgObj.crossOrigin = 'anonymous'; 如果跨域时需要加上
// 待图片加载完后,将其显示在canvas上
imgObj.onload = function () { //onload必须使用
    var ctx = canvas.getContext('2d');
    ctx.drawImage(this, 0, 0);
    var base64 = canvas.toDataURL('image/jpeg');
    console.log(base64)
};

效果图


image

帧动画

  • setTimeout

  • setInterval

  • requestAnimationFrame

拿setInterval举个栗子


上面那个小栗子用三个方法都能实现
但是不知道大家是否有这样的感觉,当你使用window.setInterval()或者window.setTimeout()制作出来的动画,一些比较炫酷的效果的时候总是一卡一卡的,这绝对不是大家想看到的。
两个函数的特征决定了它能展示给我们的效果:

(1)其实它们从一生下来,就不是为动画服务的;

(2)它们虽然可以以毫秒作为单位,但是它们永远达不到毫秒的精确性;

(3)它们是死的,并不会考虑动画什么时间绘制才是最佳的,它们比较专一,脑子中只有你给予的那个数字(时间参数)。

所以大家在想,有没有这样一个方法:它们可以根据浏览器的性能自己决定绘制动画的速率。
也许在将来伟大的程序员会将window.setInterval()和window.setTimeout()的孙子打造出这样的性能,但是就现在来说,这个需求完全可以使用requestAnimationFrame()来完成。
requestAnimationFrame()接受一个参数,这个参数就是你要的动画引用(函数名)——为你省去了调试时间参数的烦恼。

注:requestAnimationFrame 在切换游览器的时候会停止动画

即官方说的requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
那就再说下他的其他特点

requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率

在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量

参考小案例

  • 小人遇怪

  • 小鱼吃豆

你可能感兴趣的:(canvas的一些基础理解)