热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

突袭HTML5之Canvas2D入门3-变换与组合

知识准备-坐标系在真正开始总结变换之前,我们需要先了解一下canvas的相关坐标系知识。“像素坐标系”:在HTML中,我们会设置canvas的属性:width和height,它们是以像素为

知识准备 - 坐标系

  在真正开始总结变换之前,我们需要先了解一下canvas的相关坐标系知识。

像素坐标系”:在HTML中,我们会设置canvas的属性:width和height,它们是以像素为单位的,它们描述了canvas最终的呈现区域,我形象称之为“像素坐标”(自创,不是很贴切,行家别见笑),这个坐标系原点在canvas的左上角,这个坐标系当canvas创建完成以后,就不会变了(当然了,修改width与height的时候会变的),原点一直位于左上角;x与y各有多少像素,都已经由width和height决定了。说白了,这个东西就像画画时的画布,你给多大就多大。

网格坐标系”:在绘图的时候使用的坐标系。我们绘图时所有的单位使用的并不是像素坐标,而是这个称为网格的坐标系。为了在有限的画布内,画出各种比例的图形,我们可能就要对这个坐标系进行各种变换(平移、旋转、缩放)。所以后面总结的各种变换都是针对网格坐标系的。

  这两个坐标系的关系其实就像显示器与桌面的关系一样,显示器就相当于像素坐标系,它的点都是固定的,造出来什么样就什么样。桌面就像是网格坐标系,我们可以随时移动,旋转桌面,修改桌面分辨率来看更多的内容。

  在canvas中,默认情况下,网格坐标与像素坐标是一一对应的,原点都在左上角,每1个网格单位对应1个像素单位。canvas里的所有物体的位置都是相对于网格坐标的原点而言的。如下面图中所示,默认情况下,蓝色方块的位置就是距左边x单位和距上边y单位(坐标(x, y))。

知识准备 - 状态保持

  在正式介绍变形之前,还需要先了解一下两个绘制复杂图形就必不可少的方法,这两个方法在变形中应用的相当广泛。

  保存状态:context.save()
  恢复状态:context.restore()

  save和restore方法是用来保存和恢复canvas状态的,都没有参数。canvas的状态就是当前画面应用的所有样式和变形的一个快照。

  canvas状态是以堆(stack)的方式保存的,每一次调用save方法,当前的状态就会被推入堆中保存起来。这种状态包括:

  • 当前应用的变形(即移动,旋转和缩放);
  • 所有样式:strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值;
  • 当前的裁切路径(clipping path)。

  你可以调用任意多次save方法,将canvas状态入栈。每一次调用restore方法,上一个保存的状态就从堆中弹出,所有设定都恢复。

下面的例子能很容易的说明save与restore的使用方法:

function draw() {  
   var ctx = document.getElementById('lesson01').getContext('2d');  
  
  ctx.fillRect(0,0,150,150);    //  Draw a rectangle with default settings  
  ctx.save();                   //  Save the default state  
  
  ctx.fillStyle = '#09F'        //  Make changes to the settings  
  ctx.fillRect(15,15,120,120);  //  Draw a rectangle with new settings  
  
  ctx.save();                   //  Save the current state  
  ctx.fillStyle = '#FFF'        //  Make changes to the settings  
  ctx.globalAlpha = 0.5;      
  ctx.fillRect(30,30,90,90);    //  Draw a rectangle with new settings  
  
  ctx.restore();                //  Restore previous state  
  ctx.fillRect(45,45,60,60);    //  Draw a rectangle with restored settings  
  
  ctx.restore();                //  Restore original state  
  ctx.fillRect(60,60,30,30);    //  Draw a rectangle with restored settings  

  在上面的例子中可以看到,如果每次都手动修改各个样式的值,那将会很麻烦,特别是样式的值很多的时候,更是容易出错。这个时候使用save/restore还是很方便的。

变换

  学过图形学的都知道,变换有这么几种:移动,旋转和缩放。为了弄清楚变换的效果,我们一定要理解,变换的目标是哪个。上面我也说了,这些变换都是针对网格坐标系的。下面分别看一下这些变换。

平移变换:将网格坐标系的原点移动指定的偏移量。 

context.translate(x, y)
translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量。
在做变形之前先保存状态是一个良好的习惯。大多数情况下,调用restore方法比手动恢复原先的状态要简单得多。特别是在循环中,更要注意保存和恢复canvas的状态。

旋转变换:将网格坐标系沿着自己的原点顺时针旋转指定的角度。

context.rotate(angle)
这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。旋转的中心点始终是 canvas 的原点。

缩放变换:将网格坐标系的坐标单位按照指定的比例进行缩小或放大。

context.scale(x, y)
scale 方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。

  因为像素大小是不变的,所以这个变换实际的效果就是同样大小的画布内,能画的东西多了或少了。

  变换例子如下:

function draw() {  
   var ctx = document.getElementById('lesson01').getContext('2d');  
  ctx.lineWidth = 1.5;  
  ctx.fillRect(0,0,300,300);   

  ctx.translate(150,150);  
  ctx.rotate(Math.PI/4);
  ctx.scale(0.5,0.5); 
  ctx.clearRect(-40,-40, 80,80); 
}

变换矩阵:所有的变换其实都可以用矩阵来表述。

可以用下面两种方法直接设置变换矩阵:

context.transform(m11, m12, m21, m22, dx, dy)
context.setTransform(m11, m12, m21, m22, dx, dy)
第一个方法直接将当前的变形矩阵乘上下面的矩阵(注意排列的顺序):
m11  m21  dx
m12  m22  dy
0       0       1
第二个方法会重置当前的变形矩阵为单位矩阵,然后以相同的参数调用transform方法。

function draw() {  
   var canvas = document.getElementById("lesson01");  
   var ctx = canvas.getContext("2d");  
  
   var sin = Math.sin(Math.PI/6);  
   var cos = Math.cos(Math.PI/6);  
  ctx.translate(200, 200);  
   var c = 0;  
   for ( var i=0; i <= 12; i++) {  
    c = Math.floor(255 / 12 * i);  
    ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";  
    ctx.fillRect(0, 0, 100, 10);  
    ctx.transform(cos, sin, -sin, cos, 0, 0);  
  }  
    
  ctx.setTransform(-1, 0, 0, 1, 200, 200);  
  ctx.fillStyle = "rgba(255, 128, 255, 0.5)";  
  ctx.fillRect(0, 50, 100, 100);  
}

  所有的变换起始都是通过变换矩阵实现的,所以上述的平移,旋转,缩放都可以用相应的矩阵代替,精通数学的同学可以自己推导出来:
平移:context.translate(dx,dy)可以使用context.transform (1,0,0,1,dx,dy)或者context.transform(0,1,1,0.dx,dy)代替。
旋转:context.rotate(θ)可以使用context.transform(Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180),-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),0,0)或者

context.transform(-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180), 0,0)代替。
缩放:context.scale(sx, sy)可以使用context.transform(sx,0,0,sy,0,0)或者context.transform(0,sy,sx,0, 0,0)代替。 

组合
  默认情况下,我们总是将一个图形画在另一个之上,也就是说绘制的结果受制于绘制图形的顺序。大多数情况下,这样是不够的,设置组合属性就是解决图形重叠时采取何种效果的问题。我们使用globalCompositeOperation属性来改变默认做法。
globalCompositeOperation = type
  type是下列的12中字符串值之一:

  • source-over (default):这是默认设置,新图形会覆盖在原有内容之上。
  • source-in:新图形会仅仅出现与原有内容重叠的部分。其它区域都变成透明的。
  • ource-out:结果是只有新图形中与原有内容不重叠的部分会被绘制出来。
  • source-atop:新图形中与原有内容重叠的部分会被绘制,并覆盖于原有内容之上。
  • lighter:两图形中重叠部分作加色处理。
  • xor:重叠的部分会变成透明。
  • destination-over:会在原有内容之下绘制新图形。
  • destination-in:原有内容中与新图形重叠的部分会被保留,其它区域都变成透明的。
  • destination-out:原有内容中与新图形不重叠的部分会被保留。
  • destination-atop:原有内容中与新内容重叠的部分会被保留,并会在原有内容之下绘制新图形。
  • darker:两图形中重叠的部分作减色处理。
  • copy:只有新图形会被保留,其它都被清除掉。

  假设我们先绘制了一个蓝色的矩形,再绘制一个红色的圆形,则应用这12种组合设置的结果如下所示:

注意:如果设置的属性值没有效果,说明目前您使用的浏览器还不支持该组合属性值。

裁剪
  与组合相关的一个问题是裁剪。其实在绘制路径的时候,最后一步将图形绘制到canvas的函数除了stroke和fill外,还有就是clip;以clip结束路径时会将当前绘制的图形当做裁剪路径,只有在裁剪路径内的图形才会显示。裁切路径属于canvas状态的一部分,可以被保存起来。如果我们在创建新裁切路径时想保留原来的裁切路径,我们需要做的就是保存一下canvas的状态。
  例如下面的例子就是先绘制Mask层的背景和裁剪路径,然后绘制的图形就只有在裁剪路径内的才可见:

function draw() {  
   var ctx = document.getElementById('lesson01').getContext('2d');  
   //  draw mask background
  ctx.fillRect(0,0,150,150);  
  ctx.translate(75,75);  

   //  create a circular clipping path  
  ctx.beginPath();  
  ctx.arc(0,0,60,0,Math.PI*2, true);  
  ctx.clip();  
  
   //  draw background  
   var lingrad = ctx.createLinearGradient(0,-75,0,75);  
  lingrad.addColorStop(0, '#232256');  
  lingrad.addColorStop(1, '#143778');  
    
  ctx.fillStyle = lingrad;  
  ctx.fillRect(-75,-75,150,150);  
  
   //  draw stars  
   for ( var j=1;j<50;j++){  
    ctx.save();  
    ctx.fillStyle = '#fff';  
    ctx.translate(75-Math.floor(Math.random()*150),  
                  75-Math.floor(Math.random()*150));  
    drawStar(ctx,Math.floor(Math.random()*4)+2);  
    ctx.restore();  
  }    
}  
function drawStar(ctx,r){  
  ctx.save();  
  ctx.beginPath()  
  ctx.moveTo(r,0);  
   for ( var i=0;i<9;i++){  
    ctx.rotate(Math.PI/5);  
     if(i%2 == 0) {  
      ctx.lineTo((r/0.525731)*0.200811,0);  
    }  else {  
      ctx.lineTo(r,0);  
    }  
  }  
  ctx.closePath();  
  ctx.fill();  
  ctx.restore();  
}

实用参考:
官方参考文档以及API详细说明:http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html
权威开发入门:https://developer.mozilla.org/cn/Canvas_tutorial


推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Givenasinglylinkedlist,returnarandomnode'svaluefromthelinkedlist.Eachnodemusthavethe s ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
author-avatar
手机用户2502903937
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有