图形变换。

一、画一片星空

先画一片canvas.width宽canvas.height高的黑色星空,再画200个随机位置,随机大小,随机旋转角度的星星。

window.onload=function(){
var canvas=document.getElementById("canvas"); canvas.width=800;
canvas.height=800; var context=canvas.getContext("2d"); context.fillStyle="black";
context.fillRect(0,0,canvas.width,canvas.height); for(var i=0;i<200;i++){
var r=Math.random()*10+10;
var x=Math.random()*canvas.width;
var y=Math.random()*canvas.height;
var a=Math.random()*360;
drawStar(context,x,y,r,r/2.0,a);
} } //rot顺时针旋转的角度
function drawStar(ctx,x,y,r,R,rot){
ctx.beginPath();
//角度转弧度:除以180*PI
for(var i=0;i<5;i++){
ctx.lineTo(Math.cos((18+i*72-rot)/180*Math.PI)*R+x,
-Math.sin((18+i*72-rot)/180*Math.PI)*R+y);
ctx.lineTo(Math.cos((54+i*72-rot)/180*Math.PI)*r+x,
-Math.sin((54+i*72-rot)/180*Math.PI)*r+y);
} ctx.closePath(); ctx.fillStyle="#fb3";
ctx.strokeStyle="#fd5";
ctx.lineWidth=3;
ctx.lineJoin="round"; ctx.fill();
ctx.stroke();
}

产生一个扁平化设计中200个星星的效果。

二、图像变换和状态保存

1、用标准路径+图形变换思想重构

上面drawStar函数承载的功能太多来,整个绘制路径的指定,同时把五角星的位移,大小,旋转多少度全部揉合在一个函数里了。

假如需要变为画一个四角形?六角形?代码改起来就比较麻烦了。

标准做法:修改函数结构。

接口不变,省去了旋转角度,画一个标准星星。假设外圆半径是内圆半径的两倍,所以只需要传入一个小r。drawStar里调用一个startPath()函数来绘制一个标准五角星的路径。

标准的五角星路径:只传入一个context,在(0,0)的位置绘制来一个大圆半径为1,同时没有任何偏移,任何旋转的的五角星。

在drawStar里勾绘出标准五角星后再通过图形变换使得标准五角星的位移变成在(x,y)的位置,大小变成R这么大,同时旋转rot角度。再进行具体的绘制。

这样一个设计的结构可以避免之前的问题。比如需求变成要画六角形,四角形,只需要把starPath()里面路径勾绘的代码进行相应的更改即可。

更高级的复用:starPath()函数以参数的形式传入drawStar()中。这样drawStar可以叫drawSheap用户可以绘制任意的图形,只需要传入绘制图形的标准路径,变更的位移量,大小量,旋转量即可。

//rot顺时针旋转的角度
function drawStar(ctx,x,y,r,R,rot){
starPath(ctx);
//绘制在(x,y)大小为R,旋转rot度的五角星
//...
}
function starPath(ctx){
ctx.beginPath();
//角度转弧度:除以180*PI
for(var i=0;i<5;i++){
ctx.lineTo(Math.cos((18+i*72)/180*Math.PI),
-Math.sin((18+i*72)/180*Math.PI));
ctx.lineTo(Math.cos((54+i*72)/180*Math.PI),
-Math.sin((54+i*72)/180*Math.PI));
}
ctx.closePath();
}

总结:图形学里绘制先绘制标准路径,再通过图形变换成需求大小。

2,图形变换

三种基本操作:

  • 位移translate(x,y)
  • 旋转rotate(deg)
  • 缩放 scale(sx,sy)

translate会叠加

绿色正方形位置经过2次translate后到达了(200,200)。并不是代码里看起来的(150,150)。

window.onload=function(){
var canvas=document.getElementById("canvas"); canvas.width=400;
canvas.height=400; var context=canvas.getContext("2d"); context.fillStyle="red";
context.translate(50,50);
context.fillRect(0,0,200,200); context.fillStyle="green";
context.translate(150,150);
context.fillRect(0,0,200,200);
}

为了避免上述问题,最佳实践是使用图形变换之后,再反向操作把图形变换的结果逆转过来。如下:

window.onload=function(){
var canvas=document.getElementById("canvas"); canvas.width=400;
canvas.height=400; var context=canvas.getContext("2d"); context.fillStyle="red";
context.translate(50,50);
context.fillRect(0,0,200,200);
context.translate(-50,-50);//反向操作 context.fillStyle="green";
context.translate(150,150);
context.fillRect(0,0,200,200);
context.translate(-150,-150);//反向操作
}

3,canvas状态的保存save()和恢复restore()

逆转图形变换太麻烦了,canvas提供了一个save()API,保存当前的图形状态,状态包括所有我们设置的状态,自然也包括图形变换的状态。

在完成图形变换并且具体绘制以后,在最后再调用一次context.restore()。

restore()和save()是成对出现的,restore()返回在save()时候canvas的所有状态, 这是一个非常好的保持canvas绘图状态的方法,在save()和restore()之间可以随意的更改canvas的状态而不影响后续的绘制效果。

window.onload=function(){
var canvas=document.getElementById("canvas"); canvas.width=400;
canvas.height=400; var context=canvas.getContext("2d"); context.save();
context.fillStyle="red";
context.translate(50,50);
context.fillRect(0,0,200,200);
//context.translate(-50,-50);//反向操作
context.restore(); context.save()
context.fillStyle="green";
context.translate(150,150);
context.fillRect(0,0,200,200);
// context.translate(-150,-150);//反向操作
context.restore();
}

Note:绘制整体元素,特别是在其中使用图形变换的时候,都应该先save()一下,最终结束绘制时再restore()一下以保证canvas图形绘制的正确。

三、应用translate,rotate和scale

1、使用translate和rotate绘制固定大小星星的星空

没有用scale.

ctx.translate(x,y);
ctx.rotate(rot/180*Math.PI);
window.onload=function(){
var canvas=document.getElementById("canvas"); canvas.width=800;
canvas.height=800; var context=canvas.getContext("2d"); context.fillStyle="black";
context.fillRect(0,0,canvas.width,canvas.height); for(var i=0;i<200;i++){
var r=Math.random()*10+10;
var x=Math.random()*canvas.width;
var y=Math.random()*canvas.height;
var a=Math.random()*360;
drawStar(context,x,y,r,a);
} } //rot顺时针旋转的角度
function drawStar(ctx,x,y,R,rot){ ctx.save(); ctx.translate(x,y);
ctx.rotate(rot/180*Math.PI);
starPath(ctx);
//绘制在(x,y)大小为R,旋转rot度的五角星 ctx.fillStyle="#fb3";
ctx.strokeStyle="#fd5";
ctx.lineWidth=3;
ctx.lineJoin="round"; ctx.fill();
ctx.stroke(); ctx.restore();
} function starPath(ctx){
ctx.beginPath();
//角度转弧度:除以180*PI
for(var i=0;i<5;i++){
ctx.lineTo(Math.cos((18+i*72)/180*Math.PI)*20,
-Math.sin((18+i*72)/180*Math.PI)*20);
ctx.lineTo(Math.cos((54+i*72)/180*Math.PI)*0.5*20,
-Math.sin((54+i*72)/180*Math.PI)*0.5*20);
}
ctx.closePath();
}

效果和上面图片一样。

2、scale副作用

不仅放在大小,还会放大坐标,边框等。

 var canvas=document.getElementById("canvas");

    canvas.width=400;
canvas.height=400; var context=canvas.getContext("2d"); context.save();
context.scale(1,1);
context.strokeRect(10,10,100,100);
context.restore(); context.save()
context.scale(2,2,);
context.strokeRect(10,10,100,100);
context.restore(); context.save()
context.scale(3,3,);
context.strokeRect(10,10,100,100);
context.restore();
}

3, 应用scale绘制星空

坐标是通过translate变换的,始终是(0,0)所以scale后还是(0,0)。

放弃外边框的绘制。

window.onload=function(){
var canvas=document.getElementById("canvas"); canvas.width=800;
canvas.height=800; var context=canvas.getContext("2d"); context.fillStyle="black";
context.fillRect(0,0,canvas.width,canvas.height); for(var i=0;i<200;i++){
var r=Math.random()*10+10;
var x=Math.random()*canvas.width;
var y=Math.random()*canvas.height;
var a=Math.random()*360;
drawStar(context,x,y,r,a);
} } //rot顺时针旋转的角度
function drawStar(ctx,x,y,R,rot){ ctx.save(); ctx.translate(x,y);
ctx.rotate(rot/180*Math.PI);
ctx.scale(R,R); starPath(ctx);
//绘制在(x,y)大小为R,旋转rot度的五角星 ctx.fillStyle="#fb3";
//放弃外边框的绘制
// ctx.strokeStyle="#fd5";
// ctx.lineWidth=3;
// ctx.lineJoin="round"; ctx.fill();
// ctx.stroke(); ctx.restore();
} function starPath(ctx){
ctx.beginPath();
//角度转弧度:除以180*PI
for(var i=0;i<5;i++){
ctx.lineTo(Math.cos((18+i*72)/180*Math.PI),
-Math.sin((18+i*72)/180*Math.PI));
ctx.lineTo(Math.cos((54+i*72)/180*Math.PI)*0.5,
-Math.sin((54+i*72)/180*Math.PI)*0.5);
}
ctx.closePath();
}

星星没有外边框。

四、深入理解图形变换

图形变换的实质是对图形的顶点坐标的再计算。计算过程通过变换矩阵来完成。

二维的变换矩阵是3*3,三维的变换矩阵是4*4。

使用transform(a,b,c,d,e,f)设置变换矩阵每次设置是在之前的基础上设置的。

可以用setTransform(a,b,c,d,e,f))忽略掉之前所有的变换矩阵。先设置为单位矩阵再变换。

window.onload=function(){
var canvas=document.getElementById("canvas"); canvas.width=400;
canvas.height=400; var context=canvas.getContext("2d"); context.fillStyle="red";
context.strokeStyle="#058";
context.lineWidth=5;
/////////////////////////////
// a c e
// b d f
// 0 0 1
/////////////////////////////
// a,d 水平,垂直缩放
// b,c 水平,垂直倾斜
// e,f 水平,垂直位移
/////////////////////////////
context.save();
// context.transform(1,0,0,1,0,0);
//transform级联操作
context.transform(1,0,0,1,50,100);
context.transform(2,0,0,1.5,0,0);
context.transform(1,-0.2,-0.2,1,0,0);
//setTransform()只使用当前变换
context.setTransform(1,0,0,1,100,100);
context.fillRect(50,50,100,100);
context.strokeRect(50,50,100,100);
context.restore();
}

这部分内容和css3的动画的内容本质都是一样的,都是图形学的内容。

css3动画可以参考我之前的博客:

css3中变形与动画(一)

css3中变形与动画(二)

css3中变形与动画(三)

本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/8626422.html 有问题欢迎与我讨论,共同进步。

canvas星空和图形变换的更多相关文章

  1. SVG坐标系统及图形变换

    前面的话 前面介绍过SVG视野后,本文将开始介绍SVG坐标系统及图形变换 坐标定位 对于所有元素,SVG使用的坐标系统或者说网格系统,和Canvas用的差不多(所有计算机绘图都差不多).这种坐标系统是 ...

  2. HTML5-Canvas 图形变换+状态保存

    1. 图形变换 canvas是基于状态绘制图形的.故此一般情况下,canvas的绘制的图形路径和状态时分离的. function drawShape(ctx){ // 绘制路径 shapePath(c ...

  3. 2D平面中关于矩阵(Matrix)跟图形变换的讲解

    在二维平面上,常用的有以下三种基本的图形变化: 1)Translation 2)Scale 3)Rotation 在canvas的开发中,我们也经常会用到这样的一些图形变换,尤其是我们在写自定义Vie ...

  4. canvas基本绘制图形

    canvas H5新增的元素,提供了强大的图形的绘制,变换,图片,视频的处理等等.需要使用JavaScript脚本操作 浏览器支持 大多数的现代浏览器都可以支持:IE8以下的浏览器不支持 画布 可支持 ...

  5. Canvas学习:封装Canvas绘制基本图形API

    Canvas学习:封装Canvas绘制基本图形API Canvas Canvas学习   从前面的文章中我们了解到,通过Canvas中的CanvasRenderingContext2D对象中的属性和方 ...

  6. 学习笔记:HTML5 Canvas绘制简单图形

    HTML5 Canvas绘制简单图形 1.添加Canvas标签,添加id供js操作. <canvas id="mycanvas" height="700" ...

  7. 计算机图形学 - 图形变换(opengl版)

    作业题目: 图形变换:实现一个图形绕任意直线旋转的程序. 要求:把一个三维图形绕任意一条直线旋转,需要有初始图形,和旋转后的图形,最好也可以实时控制旋转. 最少要做出绕z轴旋转. 原理:http:// ...

  8. [html5] 学习笔记-Canvas 绘制渐变图形与绘制变形图形

    在 HTML5 中,使用 Canvas API 绘制图形的知识,可以对绘制图形进行处理,包含使用 Canvas API 绘制渐变图形,使用 Canvas API 的坐标轴变换处理功能绘制变形图形.其中 ...

  9. HTML5使用Canvas来绘制图形

    一.Canvas标签: 1.HTML5<canvas>元素用于图形的绘制,通过脚本(通常是javascript)来完成. 2.<canvas>标签只是图形容器,必须使用脚本来绘 ...

随机推荐

  1. SystemVerilog语言简介(三)

    15. 强制类型转换 Verilog不能将一个值强制转换成不同的数据类型.SystemVerilog通过使用'操作符提供了数据类型的强制转换功能.这种强制转换可以转换成任意类型,包括用户定义的类型.例 ...

  2. STM32F4 串口实验中收不到超级终端发送的数据,调试工具却可以

    我用串口精灵发送数据没有问题,但是接收数据没反应. 串口接受的时候必须要用中断的,你发送只靠单一的标志位是可以判断的,但是接受的时候,你是一直停留在while里面,我们判断接受是否完成,通过检测是否收 ...

  3. 嵌入式linux------ffmpeg移植 编码H264(am335x编码H264)

    [cpp] view plaincopy <pre name="code" class="cpp"><pre name="code& ...

  4. strcpy函数

    不调用C/C++库函数,编写strcpy()函数. char * my_strcpy(char *strDest,const char *strSrc) { char *p=strDest; whil ...

  5. mpeg文件格式分析

    MPEG-1流比特层次结构分析总结 1.简要介绍Mpeg 2.Mpeg-1数据流分析 2.1视频序列层(VideoStream) 2.2画面组层(GOP) 2.3画面层(Pictures) 2.4片层 ...

  6. 【原】Java学习笔记026 - 集合

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 需求:从三国演义中 ...

  7. 小白进阶之Scrapy(基于Scrapy-Redis的分布式以及cookies池)

    首先我们更新一下scrapy版本.最新版为1.3 再说一遍Windows的小伙伴儿 pip是装不上Scrapy的.推荐使用anaconda .不然还是老老实实用Linux吧. conda instal ...

  8. 自定义WIZ文档模板

    WIZ文档模板 1.在wiz笔记里面新建一个笔记,并将其做成一个模板 例子: 2.该作为模板的笔记制作完成后,右键-高级-另存为  导出为html格式 3.将导出的文件和文件夹(有时候只有一个htm文 ...

  9. Dockerfile 中的 multi-stage

    在应用了容器技术的软件开发过程中,控制容器镜像的大小可是一件费时费力的事情.如果我们构建的镜像既是编译软件的环境,又是软件最终的运行环境,这是很难控制镜像大小的.所以常见的配置模式为:分别为软件的编译 ...

  10. Qtree3题解(树链剖分(伪)+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...