全文说到了三角形,圆形等相关图形的画法,不熟悉的同学可以出门右转,先看看前文,接下来继续我们的图形——曲线。

学过数学,或者是比较了解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();

如果要画一个弯弯曲曲的线条就费劲了,这才有下面的主角登场:

quadraticCurveTo(cpx,cpy,x,y)  二次贝塞尔曲线

参数:cpx,cpy 表示第一个控制点,x,y 表示结束点

加上起点,就是3个点控制一条曲线,其实这个跟arcTo的用法差不多,不同的地方就是arcTo是需要指定圆弧的半径的,因为它是2条线中画一个圆与直线的切点形成的曲线,那这个二次贝塞尔曲线的画图原理是什么呢?咱们一起来画一画:

二次贝塞尔曲线的大致规律:从起始点出发,曲线越靠近控制点,曲线越陡,然后慢慢远离控制点,曲线随即越来越平缓,直到结束点,并且此曲线会与起始点和结束点相切

这个控制点是不是有点像一个磁铁一样,吸引着这条曲线的运动,俗话说耳闻不如一见,咱们试一下:

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();

    

看看,是不是这样的,当然,曲线弯曲的程度是多少是有公式的,但是我们不需要关心,只需要记住一点就够了:曲线靠近控制点,曲线越陡,远离控制点,曲线越平,哦了!

再次提示一下,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();

   

如上图,第一张是效果图,第二张是原理图,曲线从起始点开始,下方有一个控制点,则曲线越陡,到第二个控制点与曲线的切线的点的位置,因为受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();

   

当然,三次贝塞尔曲线不知道能画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%);
}

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%);
}

方向可以用角度来定义,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%);
}

   渐变原点为中心,渐变颜色为百分百之间的颜色渐变

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

.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%);
}

  原点左下,渐变形状为椭圆

.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%);
}

    原点左下,渐变形状为圆形

以上只是简单列举一下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一样可以自定义颜色,支持各种颜色格式,支持指定位置(用0-1的数,跟百分比类似),相比css3之下,我觉得canvas的渐变颜色区域更加准确!

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

图可能画的有点蒙啊,解释一下,第一张图表示我们上面的代码显示的效果,渐变方向是(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);

        

可以看出角度是正确的,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);

可以看到,如果图形即使不是一个正方形,径向渐变的圆依然是正圆,与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);

比对上面的图可以看出,下图在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);

效果跟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);

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

咦,什么鬼,算了,如果想要正常的径向渐变,还是同圆心吧,不同圆心太诡异,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);

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

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);

可以看到,第一个没有变透明,后面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();

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

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();

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

为了解决这个问题,我们需要引入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();

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

回到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基础知识(三)的更多相关文章

  1. java 基础知识三 java变量

    java  基础知识 三 变量 1.作用域 {} 包围起来的代码 称之为代码块,在块中声明的变量只能在块中使用 2.常量 就是固定不变的量,一旦被定义,它的值就不能再被改变. 3.变量 变量必须在程序 ...

  2. Python基础知识(三)

    Python基础知识(三) 一丶整型 #二进制转成十进制的方法 # 128 64 32 16 8 4 2 1 1 1 1 1 1 1 例如数字5 : 101 #十进制转成二进制的方法 递归除取余数,从 ...

  3. C# 基础知识 (三).主子对话框数值传递

    在C# winform编程中,我们经常会遇到不同窗口间需要传递数值的问题.比如数据库的应用,主窗口填写内容num1,点击按钮,在弹出的子窗口显示对应num1值;或者在子窗口填写新注册用户名信息,在主窗 ...

  4. 快速掌握JavaScript面试基础知识(三)

    译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...

  5. Dapper基础知识三

    在下刚毕业工作,之前实习有用到Dapper?这几天新项目想用上Dapper,在下比较菜鸟,这块只是个人对Dapper的一种总结. Dapper,当项目在开发的时候,在没有必要使用依赖注入的时候,如何做 ...

  6. Java的基础知识三

    一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...

  7. 菜鸟脱壳之脱壳的基础知识(三)——寻找OEP

    这节我们来讲讲如何寻找一个程序的OEP,即Original Entry Point.一些PE加壳程序在被加密的程序上面加了一个区段(有的壳也会合并区段),当外壳代码执行完毕以后,会跳到程序的本身的代码 ...

  8. Go语言核心36讲(Go语言基础知识三)--学习笔记

    03 | 库源码文件 在我的定义中,库源码文件是不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用(只要遵从 Go 语言规范的话). 这里的"其他代码" ...

  9. 3. K线基础知识三

    1. 阴线 证券市场上指开盘价高于收盘价的K线,K线图上一般用淡蓝色标注,表示股价下跌,当收盘价低于开盘价,也就是股价走势呈下降趋势时,我们称这种形态的K线为阴线. 中间部分实体为蓝色,此时,上影线的 ...

  10. 税收基础知识 > 三税(营业税, 增值税, 所得税) + 关税

    三税:营业税 ▪ 增值税 ▪ 所得税 主要关注:一般纳税人, 起征点不详细描述. 营业税(Business tax) 营业税(Business tax),是对在中国境内提供应税劳务.转让无形资产或销售 ...

随机推荐

  1. dict内部方法

    代码: #dict内部方法 vdic={'name':'kamil','age':23} print(dir(vdic)) vdic1 = vdic.copy()#copy(self):浅拷贝 pri ...

  2. 【bzoj1061】 Noi2008—志愿者招募

    http://www.lydsy.com/JudgeOnline/problem.php?id=1061 (题目链接) 题意 给定n天,第i天需要ai个志愿者,有m类志愿者,每类志愿者工作时间为[l, ...

  3. return view详解

    1.return View(); 返回值 类型:System.Web.Mvc.ViewResult将视图呈现给响应的 View() 结果. 注释 View() 类的此方法重载将返回一个具有空 View ...

  4. php版本的discuzX3.2部署的问题收集

    1.登陆后台老是自动退出是怎么回事? 解决方法:用ftp上線下載文件下在跟目錄/config/config_global.php把$_config['admincp']['checkip']  = 1 ...

  5. POJ3784 Running Median

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1670   Accepted: 823 Description For th ...

  6. HackerRank and MiniMax

    传送门 Sherlock and MiniMax Authored by darkshadows on May 07 2014 Problem Statement Watson gives Sherl ...

  7. linux java cpu 100%

    1.用top找到最耗资源的进程id [ bin]# toptop - 16:56:14 up 119 days, 6:17, 7 users, load average: 2.04, 2.07, 2. ...

  8. std::stringstream

    使用 std::stringstream,小心 内存! 适时 清空 缓冲 …… 2007年12月14日 星期五 : stringstream是个好东西,网上有不少文章,讨论如何用它实现各种数据类型的转 ...

  9. IF IE

    1. <!--[if !IE]><!--> 除IE外都可识别 <!--<![endif]-->2. <!--[if IE]> 所有的IE可识别 & ...

  10. ios开发 网络编程浅析(一)

    iphone包含了很多框架和库,从底层的套接字到不同层次的封装,可以方便地给程序添加网络功能. (1)BSD套接字.最底层的套接字,这是Unix网络开发常用的API.如果从其他系统移植程序,而程序用的 ...