首頁  >  文章  >  web前端  >  canvas API ,通俗的canvas基礎知識(三)

canvas API ,通俗的canvas基礎知識(三)

黄舟
黄舟原創
2017-03-16 13:45:171526瀏覽

全文說到了三角形,圓形等相關圖形的畫法,不熟悉的同學可以出門右轉,先看看前文,接下來繼續我們的圖形-曲線。

學過數學,或是比較了解js 的同學都知道貝塞爾曲線,當然,在數學裡面,這是一門高深的學問,js裡面的貝塞爾曲線一般是用來做動畫的,其實別的地方也有體現,比如說Photoshop裡面的鋼筆工具,CorelDraw裡面的貝塞爾工具等等,canvas中,也是有體現的

##當然,如果是單純的畫一條曲線,也可以用前面的方法:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
    
ctx.arc(100,100,100,0,90*Math.PI/180,false);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(103,103);
ctx.arcTo(183,83,162,182,40);
ctx.stroke();



canvas API ,通俗的canvas基礎知識(三)

#如果要畫一個彎曲的線條就費勁了,這才有下面的主角登場:

quadraticCurveTo(cpx,cpy,x,y)  二次貝塞爾曲線

參數:cpx,cpy 表示第一個控制點,x,y表示結束點

加上起點,就是3個點控制一條曲線,其實這個跟arcTo的用法差不多,不同的地方就是arcTo是需要指定圓弧的半徑的,因為它是2條線中畫一個圓與直線的切點所形成的曲線,那這個二次貝塞爾曲線的畫圖原理是什麼呢?咱們一起來畫一畫:

canvas API ,通俗的canvas基礎知識(三)

二次貝塞爾曲線的大致規律:從起始點出發,曲線越靠近控制點,曲線越陡,然後慢慢遠離控制點,曲線隨即越來越平緩,直到結束點,並且此曲線會與起始點和結束點相切

這個控制點是不是有點像一個磁鐵一樣,吸引著這條曲線的運動,俗話說耳聞不如一見,咱們試一下:

ctx.moveTo(50,50);
ctx.lineTo(70,120);
ctx.lineTo(200,80);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(50,50);
ctx.quadraticCurveTo(70,120,200,80);
ctx.stroke();


canvas API ,通俗的canvas基礎知識(三)

canvas API ,通俗的canvas基礎知識(三)
##看看,是不是這樣的,當然,曲線彎曲的程度是多少是有公式的,但是我們不需要關心,只需要記住一點就夠了:曲線靠近控制點,曲線越陡,遠離控制點,曲線越平,哦了!

再次提示一下,arcTo與quadraticCurveTo的區別,現在是否明白?

現在我們來介紹三次貝塞爾曲線:

bezierCurveTo(cpx1,cpy1,cpx2,cpy2,x,y)  三次貝塞爾曲線

參數: cpx1,cpy1表示第一個控制點,cpx2,cpy2表示第二個控制點 x,y表示結束點

包括起始點一起4個點來決定一條曲線,這個跟二次貝塞爾曲線的原理是一樣一樣的,只是多一個控制點,其精髓還是那句話:曲線靠近控制點,曲線越陡,遠離控制點,曲線越平

先來一個簡單的例子吧(來個U形):


ctx.moveTo(20,20);
ctx.bezierCurveTo(20,100,200,100,200,20);
ctx.stroke();



canvas API ,通俗的canvas基礎知識(三)

canvas API ,通俗的canvas基礎知識(三)
#如上圖,第一張是效果圖,第二張是原理圖,曲線從起始點開始,下方有一個控制點,則曲線越陡,到第二個控制點與曲線的切線的點的位置,因為受2個控制點的影響,曲線開始慢慢變平緩,因為2控制點剛好對稱,所以到中間點,曲線出於水平,然後繼續受2個控制點的作用,其中第二個控制點的作用越來越大,知道第一個控制點與曲線的切線點位置,曲線繼續受到第二個控制點的作用,反向受力,到結束點,額,聽不懂,好吧,不懂就不懂吧,記住那句總結性的話就行了!

其經典例子莫過於正弦圖(用2個貝塞爾曲線,一個正U一個反U):


ctx.beginPath();
ctx.moveTo(20,150);
ctx.bezierCurveTo(20,50,150,50,150,150);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(150,150);
ctx.bezierCurveTo(150,250,280,250,280,150);
ctx.stroke();



canvas API ,通俗的canvas基礎知識(三)

canvas API ,通俗的canvas基礎知識(三)
#當然,三次貝塞爾曲線不知道能畫U形圖,而是任意曲線都能畫,只有你想不到,沒有畫不了的,哈哈,腦洞打大一下吧!



都說作畫作畫的,不能老是做水墨畫啊,我喜歡顏色,五彩繽紛的顏色,恩,canvas也是可以的,canvas和css3一樣都可以設定漸變,這樣一來,看到彩虹是不是就不遠了,嘻嘻

我們先看看css3的漸變是怎麼設定的,然後對比一下canvas的漸變,我們都知道漸變分為線性漸變和徑向漸變,我們一一比較:

線性漸變:


.box1{
    width:500px;
    height:50px;
    background: -webkit-linear-gradient(left, red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%);
}



canvas API ,通俗的canvas基礎知識(三)

css3可以指定颜色,支持各种颜色格式,且可以指定颜色所在位置,不仅如此,css3还可以指定渐变的方向:

.box1{
    width:500px;
    height:50px;
    background: -webkit-linear-gradient(45deg , red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%);
}



canvas API ,通俗的canvas基礎知識(三)

方向可以用角度来定义,45度角是从左下到右上进行渐变

径向渐变:

.box2{
    width:300px;
    height:200px;
    background:-webkit-radial-gradient(red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%);
}



canvas API ,通俗的canvas基礎知識(三)
  渐变原点为中心,渐变颜色为百分百之间的颜色渐变

可以看出,径向渐变是以中心为原点,一圈一圈向外扩散,同样支持自定义颜色,支持各种颜色格式,支持指定位置,也是可以设置原点和渐变方式(圆形,椭圆):

.box2{
    width:300px;
    height:200px;
    background:-webkit-radial-gradient(bottom left, ellipse,red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%);
}



canvas API ,通俗的canvas基礎知識(三)
 原点左下,渐变形状为椭圆

.box2{
    width:300px;
    height:200px;
    background:-webkit-radial-gradient(bottom left, circle,red 0%, #0F0 20%,rgb(51,102,255) 50%, rgba(204,255,0,0.8) 100%);
}



canvas API ,通俗的canvas基礎知識(三)
   原点左下,渐变形状为圆形

以上只是简单列举一下css3的渐变样式,当然css3的渐变肯定不止这些,这里主要是想对比一下canvas的渐变样式,顺便科普一下!

canvas的渐变相对要简单一些,没有那么多的花花肠子:

createLinearGradient(x1,y1,x2,y2)   创建线性渐变

参数:x1,y1 表示渐变起始点   x2,y2 表示渐变结束点

createRadialGradient(x1,y1,r1,x2,y2,r2)  创建径向渐变

参数:x1,y1 表示渐变开始圆心坐标,r1表示渐变开始圆的半径  x2,y2 表示渐变结束圆心坐标,r2表示渐变结束圆的半径

gradient.addColorStop(stop,color)  规定gradient 对象中的颜色和位置

参数: stop 取值0-1之间,表示渐变中开始与结束之间的位置   color表示渐变颜色

注意,这里添加渐变颜色的对象并不是context,而是gradient

怎么用呢?看线性渐变一个小例子:

这个科普一个误区:

ctx.fillRect(50,50,200,50);
var line = ctx.createLinearGradient(50,50,200,50);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');





这样是错误的,什么都出不来!

照理说,应该是先创建一个图像,然后给这个图形加渐变色,一般的规律都如此,比如画画,比如css,但是canvas不一样,重点来了:canvas凡是设置样式的,必须放在绘图前面 ,怎么理解这句话?

绘图的方法: fill() , fillRect() , stroke() , strokeRect() , rect()

那设置:比如文字类字体,字体大小,字体颜色,字体阴影,渐变色  路径类如线条,矩形,圆形,背景,渐变等等

所以正确的格式是:

var line = ctx.createLinearGradient(50,50,200,50);
        line.addColorStop(0,'red');
        line.addColorStop(0.2 ,'#0F0');
        line.addColorStop(0.5 ,'rgb(51,102,255)');
        line.addColorStop(1 ,'rgba(204,255,0,0.8)');
        
        ctx.fillStyle = line;
        ctx.fillRect(50,50,200,50);



canvas API ,通俗的canvas基礎知識(三)

可以看到canvas一样可以自定义颜色,支持各种颜色格式,支持指定位置(用0-1的数,跟百分比类似),相比css3之下,我觉得canvas的渐变颜色区域更加准确!

好了,既然canvas能像css3一样设置渐变样式,那可不可以设置方向呢?怎么设?以上面的代码为例,我们画一张图:

canvas API ,通俗的canvas基礎知識(三)

图可能画的有点蒙啊,解释一下,第一张图表示我们上面的代码显示的效果,渐变方向是(50,50)——> (200,50),是一条水平线,表示方向是从左到右渐变,要是我将坐标方向设为(50,50)——> (200,100),如第二张图,那么渐变是否是从左上角到右下角呢?我们试一下:

var line = ctx.createLinearGradient(50,50,200,100);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');
        
ctx.fillStyle = line;
ctx.fillRect(50,50,200,50);



canvas API ,通俗的canvas基礎知識(三)

canvas API ,通俗的canvas基礎知識(三)

可以看出角度是正确的,createLinearGradient的2个坐标就是为了指明渐变方向的,那么canvas的径向渐变会不会跟css3相同呢?我们写一个小例子:

var line = ctx.createRadialGradient(150,150,0,150,150,200);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');
        
ctx.fillStyle = line;
ctx.fillRect(50,50,200,150);



canvas API ,通俗的canvas基礎知識(三)

可以看到,如果图形即使不是一个正方形,径向渐变的圆依然是正圆,与css3的渐变机制有点不一样(css3是椭圆,看上面的css3图),并且渐变区域是根据两个圆来决定的,我们改一下这2个圆的区域看看:

var line = ctx.createRadialGradient(150,150,50,150,150,100);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');
        
ctx.fillStyle = line;
ctx.fillRect(50,50,200,150);



canvas API ,通俗的canvas基礎知識(三)

比对上面的图可以看出,下图在50-100的区间里是有渐变的,其他地方则是首尾的颜色填充,那么圆心在边角上呢?

var line = ctx.createRadialGradient(50,50,0,50,50,200);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');
        
ctx.fillStyle = line;
ctx.fillRect(50,50,200,150);



canvas API ,通俗的canvas基礎知識(三)

效果跟css3的样式一样,颜色更加准确,如果2圆的圆心不一样,会有什么反应?

var line = ctx.createRadialGradient(50,50,0,150,150,200);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');
        
ctx.fillStyle = line;
ctx.fillRect(50,50,200,150);


一个圆心在左上角,一个圆心在中间

canvas API ,通俗的canvas基礎知識(三)

咦,什么鬼,算了,如果想要正常的径向渐变,还是同圆心吧,不同圆心太诡异,hold 不住啊!

那径向渐变能像css3一样可以设置椭圆吗?咳咳,我只能借用一句台词:臣妾做不到啊!

我们来一个炫酷的渐变应用:

ctx.font = "40px 微软雅黑";
var line = ctx.createLinearGradient(10,100,200,100);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');
ctx.fillStyle = line;
ctx.fillText("狂拽炫酷吊炸天",10,100);


canvas API ,通俗的canvas基礎知識(三)

下面我们介绍另外一个与颜色有关的属性——透明!

globalAlpha = num    参数:num取值0-1之间   设置或返回绘图的当前透明值

有同学会问,按这个词的意思是全局透明,那么它是全局的吗?我也想问,那我们试一下:

ctx.fillStyle = "red";
ctx.fillRect(50,50,100,100);
ctx.globalAlpha = 0.5;
ctx.fillStyle = "green";
ctx.fillRect(100,100,100,100);
ctx.fillStyle = "blue";
ctx.fillRect(150,150,100,100);



canvas API ,通俗的canvas基礎知識(三)

可以看到,第一个没有变透明,后面2个变透明了,那么如果我将路径闭合,是否还会出现这样的效果:

ctx.beginPath();
ctx.fillStyle = "red";
ctx.fillRect(50,50,100,100);
ctx.closePath();
        
ctx.globalAlpha = 0.5;
        
ctx.beginPath();
ctx.fillStyle = "green";
ctx.fillRect(100,100,100,100);
ctx.closePath();
        
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(150,150,100,100);
ctx.closePath();


canvas API ,通俗的canvas基礎知識(三)

结果是一样的,说明它是”人“如其名啊,果然是全局的,那么将它放入闭合路径中,会污染其他的路径吗?

ctx.beginPath();
ctx.globalAlpha = 0.5;
ctx.fillStyle = "red";
ctx.fillRect(50,50,100,100);
ctx.closePath();

ctx.beginPath();
ctx.fillStyle = "green";
ctx.fillRect(100,100,100,100);
ctx.closePath();
        
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(150,150,100,100);
ctx.closePath();



canvas API ,通俗的canvas基礎知識(三)

马蛋,简直是严重的核污染啊,穿透力如此之强?如果我只想让第一个透明,后面的不透明?我要怎么弄呢?

为了解决这个问题,我们需要引入2个方法,同样是一对活宝啊:

context.save()   保存当前环境的状态

context.restore() 返回之前保存过的路径状态和属性

怎么理解这对活宝呢?

可以这么理解:当设置了save()方法,就相当于将后面的绘图放在一个堆栈中,与世隔绝,知道看到restore(),就返回到原来的位置,举个例子哈,就像是一堆糖果,save()就是将一部分糖果装进盒子,restore()就是封闭盒子,继续捡糖果,但是盒子里的糖果就不会与其他糖果混合了,恩,可以这么理解,特别注意的是,restore()方法必须要有save()才起作用,你想啊,都没有盒子装糖果,怎么能封闭盒子呢

那咱们来看看他们的神奇技能:

ctx.save();
ctx.beginPath();
ctx.globalAlpha = 0.5;
ctx.fillStyle = "red";
ctx.fillRect(50,50,100,100);
ctx.closePath();
ctx.restore();

ctx.beginPath();
ctx.fillStyle = "green";
ctx.fillRect(100,100,100,100);
ctx.closePath();
        
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(150,150,100,100);
ctx.closePath();



canvas API ,通俗的canvas基礎知識(三)

哎呀,瞬间觉得整个世界都完美了!在面对凶悍的全局变量,属性或方法时,我们可以用上面的这对活宝来避免它们对我们需要的部分的侵害(这里为什么要说侵害,会让人想污的),确实是好技能!

回到globalAlpha,它的用处还是有很多的,路径,图形,文字都可以设置透明,那我们来一个文字透明看看:

ctx.font = "40px 微软雅黑";
var line = ctx.createLinearGradient(10,100,200,100);
line.addColorStop(0,'red');
line.addColorStop(0.2 ,'#0F0');
line.addColorStop(0.5 ,'rgb(51,102,255)');
line.addColorStop(1 ,'rgba(204,255,0,0.8)');
ctx.fillStyle = line;
ctx.globalAlpha = 0.3;
ctx.fillText("狂拽炫酷吊炸天",10,100);


canvas API ,通俗的canvas基礎知識(三)

恩,今天就介绍到这里吧,后面的内容比较复杂,需要认真的准备,就这样吧!

 以上就是canvas API ,通俗的canvas基础知识(三) 的内容,更多相关内容请关注PHP中文网(www.php.cn)!


相关文章:

canvas API 介绍,常见的canvas基础知识(一)

canvas API 介绍,常见的canvas基础知识(二)

canvas API 介绍,常见的canvas基础知识(四)

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn