lufy's legend

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 68835|回复: 24

HTML5高级编程之图形扭曲及其应用

[复制链接]

37

主题

8

好友

9305

积分

诸侯王

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

发表于 2013-2-25 22:56:14 |显示全部楼层
HTML5中的几种变形
HTML5中的变形,共有以下几种方法
scale() 缩放
rotate() 旋转
translate() 平移
transform() 矩阵变形
setTransform() 重设矩阵
这几个方法,对图片一共能完成下面几种处理


但是,如果要实现下面这种不规则的变形,就不行了

那咱们一步步,先来看HTML5的这几个方法。

1,缩放方法如下
  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <canvas id="myCanvas" width="800" height="280"></canvas>
  5. <script type="text/javascript">
  6. var c=document.getElementById('myCanvas');
  7. var ctx=c.getContext('2d');
  8. var img = new Image();
  9. img.src="face.jpg";
  10. img.onload = function(){
  11.         ctx.drawImage(img,0,0);
  12.         ctx.scale(0.5,0.5);
  13.         ctx.drawImage(img,500,0);
  14. };
  15. </script>
  16. </body>
  17. </html>
复制代码
效果
2,旋转代码
  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <canvas id="myCanvas" width="800" height="400"></canvas>
  5. <script type="text/javascript">
  6. var c=document.getElementById('myCanvas');
  7. var ctx=c.getContext('2d');
  8. var img = new Image();
  9. img.src="face.jpg";
  10. img.onload = function(){
  11.         ctx.rotate(20*Math.PI/180);
  12.         ctx.drawImage(img,200,0);
  13. };
  14. </script>
  15. </body>
  16. </html>
复制代码

效果


3,平移代码
  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <canvas id="myCanvas" width="800" height="400"></canvas>
  5. <script type="text/javascript">
  6. var c=document.getElementById('myCanvas');
  7. var ctx=c.getContext('2d');
  8. var img = new Image();
  9. img.src="face.jpg";
  10. img.onload = function(){
  11.         ctx.drawImage(img,0,0);
  12.         ctx.translate(100,100);
  13.         ctx.drawImage(img,0,0);
  14. };
  15. </script>
  16. </body>
  17. </html>
复制代码
效果

4,倾斜代码
  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <canvas id="myCanvas" width="800" height="400"></canvas>
  5. <script type="text/javascript">
  6. var c=document.getElementById('myCanvas');
  7. var ctx=c.getContext('2d');
  8. var img = new Image();
  9. img.src="face.jpg";
  10. img.onload = function(){
  11.         ctx.setTransform(1.3,0.1,-0.2,1,80,40);
  12.         ctx.drawImage(img,0,0);
  13. };
  14. </script>
  15. </body>
  16. </html>
复制代码
效果

不规则变形
前面提到,HTML5没办法直接实现不规则变形,但是,可以通过一系列组合来实现不规则变形,比如将下面的这个变形做一下分解

分解后变成

那继续看,它其实可以看做是两个变形的组合,如下图

其实就是将多个变形组合到了一起,这样讲几个变形的其中的一部分拿出来,再拼凑成新的图形,就变成了刚才的特殊图形了
顺着这个思路,我仿照AS3,将一张图分解成多个小的三角形,效果如下

就这样,很轻松的实现了drawtriangles函数,用来扭曲图形,它与AS3的drawtriangles函数功能基本一致,区别就是,第4个之后的参数的含义不同,在这里它第4个参数表示分割线的线粗,第5个参数则表示分割线的颜色,如果不设定,则不显示分割线,这个函数效果如下,你可以实现任何变形,甚至3D变形也可以

这面是测试连接,你可以拖动图中的红点,来让图片发生任意的扭曲变形

使用这个drawtriangles函数,你需要下载HTML5开源引擎lufylegend的1.5版或以上版本,lufylegend1.5版发布地址如下

下面来详细讲解一下drawtriangles函数的使用方法。并且使用drawtriangles函数实现下面这种处理效果

因为这个方法是从AS3移植而来,所以它的使用方法和AS3基本一致,这里是AS3的drawtriangles函数API,大家可以参照一下
上次也说明过,移植后的drawtriangles函数,第4个之后的参数的含义不同,在这里它第4个参数表示分割线的线粗,第5个参数则表示分割线的颜色,如果不设定,则不显示分割线

drawTriangles函数的定义
  1. drawTriangles(vertices, indices, uvtData,thickness,color)
  2. vertices:由数字构成的矢量,其中的每一对数字将被视为一个坐标位置(一个 x, y 对)。vertices 参数是必需的。
  3. indices:一个由整数或索引构成的矢量,其中每三个索引定义一个三角形。如果 indexes 参数为 null,则每三个顶点(vertices 矢量中的 6 对 x,y)定义一个三角形。否则,每个索引将引用一个顶点,即 vertices 矢量中的一对数字。例如,indexes[1] 引用 (vertices[2], vertices[3])。
  4. uvtData:由用于应用纹理映射的标准坐标构成的矢量。每个坐标引用用于填充的位图上的一个点。每个顶点必须具有一个 UV 或一个 UVT 坐标。对于 UV 坐标,(0,0) 是位图的左上角,(1,1) 是位图的右下角。
  5. thickness:分割三角形的边框的线粗
  6. color:分割三角形的边框的颜色
复制代码
直接看上面的文字,恐怕不太容易理解,下面来举几个例子,最后两个参数比较简单,先来说说这两个参数,下面是最后两个参数线宽设置为2,颜色为白色的效果

可以看到,上图中显示了三角形的边框。
好了,接下来说说其他三个参数的用法,
1,第一个参数vertices,其实就是定义每个顶点的坐标,这几个顶点的顺序依次为下图

vertices参数中储存的就是上面的9个顶点的坐标,代码如下
  1. vertices = new Array();
  2. vertices.push(0, 0);
  3. vertices.push(0, 120);
  4. vertices.push(0, 240);
  5. vertices.push(120, 0);
  6. vertices.push(120, 120);
  7. vertices.push(120, 240);
  8. vertices.push(240, 0);
  9. vertices.push(240, 120);
  10. vertices.push(240, 240);
复制代码
2,第二个参数indices是定义三角形,数组vertices中每三个顶点可以组成一个三角形,indices就是来定义这些三角形,这些三角形的顶点顺序是有规定的,其实从前面的图中可以看到,每两个三角形是一个矩形,定义这些三角形的时候,要以这些矩形的四个顶点为基准,三角形的顶点顺序分别是(左上,右上,左下)和(右上,左下,右下),如下图所示

对应图中的三角形,代码如下
  1. indices = new Array();
  2. indices.push(0, 3, 1);
  3. indices.push(3, 1, 4);
  4. indices.push(1, 4, 2);
  5. indices.push(4, 2, 5);
  6. indices.push(3, 6, 4);
  7. indices.push(6, 4, 7);
  8. indices.push(4, 7, 5);
  9. indices.push(7, 5, 8);
复制代码
2,第三个参数uvtData是定义上面的每个顶点相对于整张图片的比例,比如上面的图中的9个顶点的坐标,他们相对于原图片中的位置分别为下图所示


换算成代码如下
  1. uvtData = new Array();
  2. uvtData.push(0, 0);
  3. uvtData.push(0, 0.5);
  4. uvtData.push(0, 1);
  5. uvtData.push(0.5, 0);
  6. uvtData.push(0.5, 0.5);
  7. uvtData.push(0.5, 1);
  8. uvtData.push(1, 0);
  9. uvtData.push(1, 0.5);
  10. uvtData.push(1, 1);
复制代码
有了上面这些参数的定义,然后通过LSprite对象的graphics属性的beginBitmapFill和drawTriangles两个函数,就可以绘制多样化的图形了
beginBitmapFill是用位图图像填充绘图区,参数是LBitmapData对象

如果,在vertices参数中定义的坐标位置就是原图片中所对应的位置,那么图片是没有什么变化的,但是,如果改变这些坐标的位置,比如下面的代码
  1. vertices = new Array();
  2. vertices.push(0, 0);
  3. vertices.push(0-50, 120);//这里将原坐标的x坐标左移50
  4. vertices.push(0, 240);
  5. vertices.push(120, 0);
  6. vertices.push(120, 120);
  7. vertices.push(120, 240);
  8. vertices.push(240, 0);
  9. vertices.push(240+50, 120);//这里将原坐标的x坐标右移50
  10. vertices.push(240, 240);

  11. indices = new Array();
  12. indices.push(0, 3, 1);
  13. indices.push(3, 1, 4);
  14. indices.push(1, 4, 2);
  15. indices.push(4, 2, 5);
  16. indices.push(3, 6, 4);
  17. indices.push(6, 4, 7);
  18. indices.push(4, 7, 5);
  19. indices.push(7, 5, 8);

  20. uvtData = new Array();
  21. uvtData.push(0, 0);
  22. uvtData.push(0, 0.5);
  23. uvtData.push(0, 1);
  24. uvtData.push(0.5, 0);
  25. uvtData.push(0.5, 0.5);
  26. uvtData.push(0.5, 1);
  27. uvtData.push(1, 0);
  28. uvtData.push(1, 0.5);
  29. uvtData.push(1, 1);
  30. backLayer.graphics.beginBitmapFill(bitmapData);
  31. backLayer.graphics.drawTriangles(vertices, indices, uvtData);
复制代码
效果如图

上面的变形是将图片分割成了8个三角形,要实现更多种变形,那只需要将图片分割成更多的小三角形就可以了
比如我利用这个函数,制作了一个简陋的图片修饰工具,效果如下


大家可以点击下面的连接,来测试一下它的效果
备注:
使用drawtriangles函数,你需要下载HTML5开源引擎lufylegend的1.5版或以上版本,lufylegend1.5版发布地址如下

下面来看看drawtriangles函数的扩展。利用drawtriangles函数来实现一个旋转的3D地球,效果如下

因为lufylegend1.5.0版的drawtriangles函数有个bug,所以我悄悄的更新了lufylegend1.5.1版,大家可以到官网下载,地址如下
其实绘制3d球体效果的话,首先就是绘制一个平面,然后将这个平面分成一个一个的小三角形,然后再用这些小三角形拼凑成一个圆球就可以了
现在,我先创建一个空白的LBitmapData对象,然后将这个对象分割成N个小三角形,具体做法看下面代码
  1. earthBitmapData = new LBitmapData("#ffffff", 0, 0, 500, 300);
  2. var i, j;
  3. vertices = new Array();
  4. for(i=0;i<=cols;i++){
  5.         for(j=0;j<=rows;j++){
  6.                 vertices.push(i*15,j*15);
  7.         }
  8. }
  9. indices = new Array();
  10. for (i = 0; i < cols; i++) {
  11.         for (j = 0; j < rows; j++) {
  12.                 indices.push(i * (rows + 1) + j, (i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1);
  13.                 indices.push((i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1, (i + 1) * (rows + 1) + j + 1);
  14.         }
  15. }
  16. uvtData = new Array();
  17. for (i = 0; i <= cols; i++) {
  18.         for (j = 0; j <= rows; j++) {
  19.                 uvtData.push(i / cols, j / rows);
  20.         }
  21. }
复制代码
接着,利用drawtriangles函数将LBitmapData对象绘制到画面上
  1. backLayer = new LSprite();
  2. addChild(backLayer);
  3. backLayer.graphics.clear();
  4. backLayer.graphics.beginBitmapFill(earthBitmapData);
  5. backLayer.graphics.drawTriangles(vertices, indices, uvtData, 2);
复制代码
得到效果如下图。

要想将这个平面编程一个圆,就需要计算图中每个小三角形的坐标,先来看看y坐标应该如何计算,看下面一张图,是一个球的垂直切面

利用三角函数,计算图中的y坐标,和y坐标所在位置的球的水平切面圆的半径r1
  1. var a = Math.sin(angle);
  2. if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
  3. var y =  -r*a;
  4. var sa = Math.cos(angle);
  5. var r1 =  Math.abs(r*sa);
复制代码
于是,首先将计算好的y坐标带入到vertices数组中
  1. for(i=0;i<=cols;i++){
  2.         for(j=0;j<=rows;j++){
  3.                 var angle = (90-180*j/rows)*Math.PI/180;
  4.                 var a = Math.sin(angle);
  5.                 if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
  6.                 if((90-180*j/rows)%180==0)a=0;
  7.                 var sy =  -r*a;
  8.                 vertices.push(i*15,sy);
  9.         }
  10. }
复制代码
因为还没有计算x的坐标,所以得到一个特殊图形,如下

接着,看看x的坐标如何计算,首先将半径为r1的平面切面拿出来,如下图

利用三角函数,计算图中的x坐标
  1. var b = Math.cos(angle*Math.PI/180);
  2. var x =  r1*b;
复制代码
这时,如果只将计算好的x坐标带入到vertices数组中的话
  1. for(i=0;i<=cols;i++){
  2.         for(j=0;j<=rows;j++){
  3.                 var sa = Math.cos(angle);
  4.                 if((90-180*j/rows)%180==0)sa=1;
  5.                 var sr =  Math.abs(r*sa);
  6.                 var angle2 = 360*(i+1)/cols;
  7.                 var b = Math.cos(angle2*Math.PI/180);
  8.                 if(angle2%360==0)b=1;
  9.                 else if(angle2%180==0)b=-1;
  10.                 var sx =  sr*b;
  11.                 vertices.push(sx,j*15);
  12.         }
  13. }
复制代码
因为没有计算y的坐标,所以得到一个很有意思的图形,如下

如果将计算好的x坐标和y坐标,同时带入到vertices数组中的话
  1. for(i=0;i<=cols;i++){
  2.         for(j=0;j<=rows;j++){
  3.                 var angle = (90-180*j/rows)*Math.PI/180;
  4.                 var a = Math.sin(angle);
  5.                 if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
  6.                 if((90-180*j/rows)%180==0)a=0;
  7.                 var sy =  -r*a;
  8.                 var sa = Math.cos(angle);
  9.                 if((90-180*j/rows)%180==0)sa=1;
  10.                 var sr =  Math.abs(r*sa);
  11.                 var angle2 = 360*(i+1)/cols;
  12.                 var b = Math.cos(angle2*Math.PI/180);
  13.                 if(angle2%360==0)b=1;
  14.                 else if(angle2%180==0)b=-1;
  15.                 var sx =  sr*b;
  16.                 vertices.push(sx, sy);
  17.         }
  18. }
复制代码
得到一个完整的球体图形,如下

接下来就简单了,将空白图片换成地球的平面图,代码如下
  1. earthBitmapData = new LBitmapData(imglist["earth"]);
复制代码
再次运行代码,就可以得到下面的3D图形了

下面,该让这个地球转动起来了,根据上一篇介绍的内容,传入drawtriangles函数的uvtData数组中的元素是每个小三角形在原图片中的相对位置,它们决定了绘制图片的开始位置,如果将一组位置比如0123,变换其中的位置成为1230,再继续变换成2301,这样不断的进行位置变换,那么从视觉上,其实就已经实现了旋转了,那么在代码中,只需要将分割完的数组的按照每一列进行移动,每次都将第一列的两组三角形移到最后一列,这样第二列的两组三角形就变为了第一列,这样不停的变换就能让一个地球转动起来
  1. for (i = 0; i <= rows; i++) {
  2.         uvtData.push(uvtData.shift());
  3.         uvtData.push(uvtData.shift());
  4. }
复制代码
如果要改变这个地球的大小的话,就更简单了,改变LSprite对象的scaleX和scaleY属性就可以改变它的大小了,大家可以点击下面的连接,来测试一下它的效果

备注:
再次说明一下,本篇所介绍的内容需要HTML5开源引擎lufylegend的1.5.1版或以上版本的支持,lufylegend1.5.1版发布地址如下


不回答与技术和引擎不相关的问题
回复

使用道具 举报

0

主题

0

好友

10

积分

士兵

Rank: 1

发表于 2013-3-12 22:01:25 |显示全部楼层
图片挂了。
回复

使用道具 举报

0

主题

0

好友

10

积分

士兵

Rank: 1

发表于 2013-12-10 21:11:46 |显示全部楼层
请问下,我想用你这个框架做个“长廊”效果
就是“网格”形状的“地板”,感觉是人在往前走,地板在往后退,
其实就是矩形图片,做了个梯形的缩放,然后平铺,滚动
应该怎么实现呢?望赐教
回复

使用道具 举报

37

主题

8

好友

9305

积分

诸侯王

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

发表于 2013-12-10 23:28:39 |显示全部楼层
leonly0224 发表于 2013-12-10 21:11
请问下,我想用你这个框架做个“长廊”效果
就是“网格”形状的“地板”,感觉是人在往前走,地板在往后退 ...

就用这个帖子里讲的drawTriangles函数就可以实现啊,
控制好座标,让座标按照梯形轨道移动就可以了
不回答与技术和引擎不相关的问题
回复

使用道具 举报

0

主题

0

好友

10

积分

士兵

Rank: 1

发表于 2013-12-11 11:50:19 |显示全部楼层
本帖最后由 leonly0224 于 2013-12-11 15:56 编辑
lufy 发表于 2013-12-10 23:28
就用这个帖子里讲的drawTriangles函数就可以实现啊,
控制好座标,让座标按照梯形轨道移动就可以了 ...

你好,感谢回复!
我现在是这么弄的
首先图片按照你上面三国人物的划分方法来划分9个定点
0    3    6
1    4    7
2    5    8
那么uvtData里面顺序是0 1 2 3 4 5 6 7 8

如果我想沿着y轴水平往下“滚动”,
是不是我把划分点最下面一行挪上来即可(变成下面这样)
2    5    8
0    3    6
1    4    7
那么uvtData里面顺序是2 0 1 5 3 4 8 6 7 ?

是不是这么个意思?
=============目前我这么弄,已经成功了
但是运行一小会,速度就会越来越卡。。。。到最后明显卡的一塌糊涂
请问有没有更高效的方法推荐下,谢谢

回复

使用道具 举报

37

主题

8

好友

9305

积分

诸侯王

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

发表于 2013-12-11 22:19:27 |显示全部楼层
leonly0224 发表于 2013-12-11 11:50
你好,感谢回复!
我现在是这么弄的
首先图片按照你上面三国人物的划分方法来划分9个定点

这种方法,要么从一开始就卡,要么就一直不卡
你说的越来越卡的话,应该是用法上哪里出了问题了
能不能把代码贴上来看看?
或者把代码打包用邮件发给我
不回答与技术和引擎不相关的问题
回复

使用道具 举报

0

主题

0

好友

4

积分

士兵

Rank: 1

发表于 2014-1-19 19:22:04 |显示全部楼层


   关注下~~~
回复

使用道具 举报

0

主题

0

好友

8

积分

士兵

Rank: 1

发表于 2014-2-3 09:25:41 |显示全部楼层
请问怎样实现贴图效果?贴在杯子上,或者贴在衣服上的立体效果。还有用你这个引擎做的地球实例,可以去掉上面很多条一圈圈的白线吗?谢谢
回复

使用道具 举报

37

主题

8

好友

9305

积分

诸侯王

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

发表于 2014-2-3 23:06:41 |显示全部楼层
macdaddy 发表于 2014-2-3 09:25
请问怎样实现贴图效果?贴在杯子上,或者贴在衣服上的立体效果。还有用你这个引擎做的地球实例,可以去掉上 ...

就用该帖子说的方法就可以吧。
白线的问题是根据浏览器而定的,比如我的Mac版chrome就完全没有白线的
不回答与技术和引擎不相关的问题
回复

使用道具 举报

0

主题

0

好友

4

积分

士兵

Rank: 1

发表于 2014-12-11 15:14:38 |显示全部楼层
讲解的非常好,水平不够,需要慢慢理解!不过我想问一下楼主,这个lufylegend只能从你发布的那本书进行学习吗,有没其他的资源?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

防止垃圾广告,请填写任意字符

Archiver|lufy's legend

GMT+8, 2024-3-29 21:37 , Processed in 0.067443 second(s), 32 queries .

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc.

回顶部