lufy 发表于 2013-2-25 22:56:14

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

HTML5中的几种变形HTML5中的变形,共有以下几种方法scale() 缩放
rotate() 旋转
translate() 平移
transform() 矩阵变形
setTransform() 重设矩阵
这几个方法,对图片一共能完成下面几种处理http://lufylegend.com/lufylegend_blog_img/png/4.png

但是,如果要实现下面这种不规则的变形,就不行了http://lufylegend.com/lufylegend_blog_img/png/5.png
那咱们一步步,先来看HTML5的这几个方法。
1,缩放方法如下<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="800" height="280"></canvas>
<script type="text/javascript">
var c=document.getElementById('myCanvas');
var ctx=c.getContext('2d');
var img = new Image();
img.src="face.jpg";
img.onload = function(){
        ctx.drawImage(img,0,0);
        ctx.scale(0.5,0.5);
        ctx.drawImage(img,500,0);
};
</script>
</body>
</html>效果http://lufylegend.com/lufylegend_blog_img/png/6.png2,旋转代码
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="800" height="400"></canvas>
<script type="text/javascript">
var c=document.getElementById('myCanvas');
var ctx=c.getContext('2d');
var img = new Image();
img.src="face.jpg";
img.onload = function(){
        ctx.rotate(20*Math.PI/180);
        ctx.drawImage(img,200,0);
};
</script>
</body>
</html>
效果
http://lufylegend.com/lufylegend_blog_img/png/7.png

3,平移代码<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="800" height="400"></canvas>
<script type="text/javascript">
var c=document.getElementById('myCanvas');
var ctx=c.getContext('2d');
var img = new Image();
img.src="face.jpg";
img.onload = function(){
        ctx.drawImage(img,0,0);
        ctx.translate(100,100);
        ctx.drawImage(img,0,0);
};
</script>
</body>
</html>效果http://lufylegend.com/lufylegend_blog_img/png/8.png
4,倾斜代码<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="800" height="400"></canvas>
<script type="text/javascript">
var c=document.getElementById('myCanvas');
var ctx=c.getContext('2d');
var img = new Image();
img.src="face.jpg";
img.onload = function(){
        ctx.setTransform(1.3,0.1,-0.2,1,80,40);
        ctx.drawImage(img,0,0);
};
</script>
</body>
</html>效果http://lufylegend.com/lufylegend_blog_img/png/9.png
不规则变形前面提到,HTML5没办法直接实现不规则变形,但是,可以通过一系列组合来实现不规则变形,比如将下面的这个变形做一下分解http://lufylegend.com/lufylegend_blog_img/png/10.png
分解后变成http://lufylegend.com/lufylegend_blog_img/png/11.png
那继续看,它其实可以看做是两个变形的组合,如下图http://lufylegend.com/lufylegend_blog_img/png/12.png
其实就是将多个变形组合到了一起,这样讲几个变形的其中的一部分拿出来,再拼凑成新的图形,就变成了刚才的特殊图形了顺着这个思路,我仿照AS3,将一张图分解成多个小的三角形,效果如下http://lufylegend.com/lufylegend_blog_img/png/13.png
就这样,很轻松的实现了drawtriangles函数,用来扭曲图形,它与AS3的drawtriangles函数功能基本一致,区别就是,第4个之后的参数的含义不同,在这里它第4个参数表示分割线的线粗,第5个参数则表示分割线的颜色,如果不设定,则不显示分割线,这个函数效果如下,你可以实现任何变形,甚至3D变形也可以http://img.my.csdn.net/uploads/201210/18/1350491055_6559.PNG
这面是测试连接,你可以拖动图中的红点,来让图片发生任意的扭曲变形http://lufy.netne.net/lufylegend-js/lufylegend-1.4/beginBitmapFill/sample01.html

使用这个drawtriangles函数,你需要下载HTML5开源引擎lufylegend的1.5版或以上版本,lufylegend1.5版发布地址如下http://blog.csdn.net/lufy_legend/article/details/8054658

下面来详细讲解一下drawtriangles函数的使用方法。并且使用drawtriangles函数实现下面这种处理效果http://img.my.csdn.net/uploads/201210/25/1351125712_2118.PNG
因为这个方法是从AS3移植而来,所以它的使用方法和AS3基本一致,这里是AS3的drawtriangles函数API,大家可以参照一下http://help.adobe.com/zh_CN/FlashPlatform/reference/actionscript/3/flash/display/Graphics.html#drawTriangles()
上次也说明过,移植后的drawtriangles函数,第4个之后的参数的含义不同,在这里它第4个参数表示分割线的线粗,第5个参数则表示分割线的颜色,如果不设定,则不显示分割线
drawTriangles函数的定义drawTriangles(vertices, indices, uvtData,thickness,color)
vertices:由数字构成的矢量,其中的每一对数字将被视为一个坐标位置(一个 x, y 对)。vertices 参数是必需的。
indices:一个由整数或索引构成的矢量,其中每三个索引定义一个三角形。如果 indexes 参数为 null,则每三个顶点(vertices 矢量中的 6 对 x,y)定义一个三角形。否则,每个索引将引用一个顶点,即 vertices 矢量中的一对数字。例如,indexes 引用 (vertices, vertices)。
uvtData:由用于应用纹理映射的标准坐标构成的矢量。每个坐标引用用于填充的位图上的一个点。每个顶点必须具有一个 UV 或一个 UVT 坐标。对于 UV 坐标,(0,0) 是位图的左上角,(1,1) 是位图的右下角。
thickness:分割三角形的边框的线粗
color:分割三角形的边框的颜色直接看上面的文字,恐怕不太容易理解,下面来举几个例子,最后两个参数比较简单,先来说说这两个参数,下面是最后两个参数线宽设置为2,颜色为白色的效果
http://img.my.csdn.net/uploads/201210/18/1350520598_7758.PNG
可以看到,上图中显示了三角形的边框。好了,接下来说说其他三个参数的用法,1,第一个参数vertices,其实就是定义每个顶点的坐标,这几个顶点的顺序依次为下图
http://img.my.csdn.net/uploads/201210/26/1351208344_2652.PNG
vertices参数中储存的就是上面的9个顶点的坐标,代码如下vertices = new Array();
vertices.push(0, 0);
vertices.push(0, 120);
vertices.push(0, 240);
vertices.push(120, 0);
vertices.push(120, 120);
vertices.push(120, 240);
vertices.push(240, 0);
vertices.push(240, 120);
vertices.push(240, 240);2,第二个参数indices是定义三角形,数组vertices中每三个顶点可以组成一个三角形,indices就是来定义这些三角形,这些三角形的顶点顺序是有规定的,其实从前面的图中可以看到,每两个三角形是一个矩形,定义这些三角形的时候,要以这些矩形的四个顶点为基准,三角形的顶点顺序分别是(左上,右上,左下)和(右上,左下,右下),如下图所示
http://img.my.csdn.net/uploads/201210/25/1351125712_1427.PNG
对应图中的三角形,代码如下indices = new Array();
indices.push(0, 3, 1);
indices.push(3, 1, 4);
indices.push(1, 4, 2);
indices.push(4, 2, 5);
indices.push(3, 6, 4);
indices.push(6, 4, 7);
indices.push(4, 7, 5);
indices.push(7, 5, 8);2,第三个参数uvtData是定义上面的每个顶点相对于整张图片的比例,比如上面的图中的9个顶点的坐标,他们相对于原图片中的位置分别为下图所示

http://img.my.csdn.net/uploads/201210/25/1351125712_3093.PNG
换算成代码如下uvtData = new Array();
uvtData.push(0, 0);
uvtData.push(0, 0.5);
uvtData.push(0, 1);
uvtData.push(0.5, 0);
uvtData.push(0.5, 0.5);
uvtData.push(0.5, 1);
uvtData.push(1, 0);
uvtData.push(1, 0.5);
uvtData.push(1, 1);有了上面这些参数的定义,然后通过LSprite对象的graphics属性的beginBitmapFill和drawTriangles两个函数,就可以绘制多样化的图形了beginBitmapFill是用位图图像填充绘图区,参数是LBitmapData对象

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

indices = new Array();
indices.push(0, 3, 1);
indices.push(3, 1, 4);
indices.push(1, 4, 2);
indices.push(4, 2, 5);
indices.push(3, 6, 4);
indices.push(6, 4, 7);
indices.push(4, 7, 5);
indices.push(7, 5, 8);

uvtData = new Array();
uvtData.push(0, 0);
uvtData.push(0, 0.5);
uvtData.push(0, 1);
uvtData.push(0.5, 0);
uvtData.push(0.5, 0.5);
uvtData.push(0.5, 1);
uvtData.push(1, 0);
uvtData.push(1, 0.5);
uvtData.push(1, 1);
backLayer.graphics.beginBitmapFill(bitmapData);
backLayer.graphics.drawTriangles(vertices, indices, uvtData);效果如图http://img.my.csdn.net/uploads/201210/26/1351209430_6101.PNG
上面的变形是将图片分割成了8个三角形,要实现更多种变形,那只需要将图片分割成更多的小三角形就可以了比如我利用这个函数,制作了一个简陋的图片修饰工具,效果如下http://img.my.csdn.net/uploads/201210/26/1351209778_2208.PNG
http://img.my.csdn.net/uploads/201210/25/1351125699_9223.PNG
大家可以点击下面的连接,来测试一下它的效果http://lufylegend.com/html5/lufylegend/ps.html备注:使用drawtriangles函数,你需要下载HTML5开源引擎lufylegend的1.5版或以上版本,lufylegend1.5版发布地址如下
http://blog.csdn.net/lufy_legend/article/details/8054658

下面来看看drawtriangles函数的扩展。利用drawtriangles函数来实现一个旋转的3D地球,效果如下http://img.my.csdn.net/uploads/201211/19/1353331023_9384.PNG
因为lufylegend1.5.0版的drawtriangles函数有个bug,所以我悄悄的更新了lufylegend1.5.1版,大家可以到官网下载,地址如下http://lufylegend.com/lufylegend
其实绘制3d球体效果的话,首先就是绘制一个平面,然后将这个平面分成一个一个的小三角形,然后再用这些小三角形拼凑成一个圆球就可以了现在,我先创建一个空白的LBitmapData对象,然后将这个对象分割成N个小三角形,具体做法看下面代码earthBitmapData = new LBitmapData("#ffffff", 0, 0, 500, 300);
var i, j;
vertices = new Array();
for(i=0;i<=cols;i++){
        for(j=0;j<=rows;j++){
                vertices.push(i*15,j*15);
        }
}
indices = new Array();
for (i = 0; i < cols; i++) {
        for (j = 0; j < rows; j++) {
                indices.push(i * (rows + 1) + j, (i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1);
                indices.push((i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1, (i + 1) * (rows + 1) + j + 1);
        }
}
uvtData = new Array();
for (i = 0; i <= cols; i++) {
        for (j = 0; j <= rows; j++) {
                uvtData.push(i / cols, j / rows);
        }
}接着,利用drawtriangles函数将LBitmapData对象绘制到画面上backLayer = new LSprite();
addChild(backLayer);
backLayer.graphics.clear();
backLayer.graphics.beginBitmapFill(earthBitmapData);
backLayer.graphics.drawTriangles(vertices, indices, uvtData, 2);得到效果如下图。http://img.my.csdn.net/uploads/201211/26/1353907834_6005.PNG
要想将这个平面编程一个圆,就需要计算图中每个小三角形的坐标,先来看看y坐标应该如何计算,看下面一张图,是一个球的垂直切面http://img.my.csdn.net/uploads/201211/26/1353908486_7330.PNG
利用三角函数,计算图中的y坐标,和y坐标所在位置的球的水平切面圆的半径r1var a = Math.sin(angle);
if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
var y =  -r*a;
var sa = Math.cos(angle);
var r1 =  Math.abs(r*sa);于是,首先将计算好的y坐标带入到vertices数组中for(i=0;i<=cols;i++){
        for(j=0;j<=rows;j++){
                var angle = (90-180*j/rows)*Math.PI/180;
                var a = Math.sin(angle);
                if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
                if((90-180*j/rows)%180==0)a=0;
                var sy =  -r*a;
                vertices.push(i*15,sy);
        }
}因为还没有计算x的坐标,所以得到一个特殊图形,如下http://img.my.csdn.net/uploads/201211/26/1353907834_4951.PNG
接着,看看x的坐标如何计算,首先将半径为r1的平面切面拿出来,如下图http://img.my.csdn.net/uploads/201211/26/1353908486_1831.PNG
利用三角函数,计算图中的x坐标var b = Math.cos(angle*Math.PI/180);
var x =  r1*b;这时,如果只将计算好的x坐标带入到vertices数组中的话for(i=0;i<=cols;i++){
        for(j=0;j<=rows;j++){
                var sa = Math.cos(angle);
                if((90-180*j/rows)%180==0)sa=1;
                var sr =  Math.abs(r*sa);
                var angle2 = 360*(i+1)/cols;
                var b = Math.cos(angle2*Math.PI/180);
                if(angle2%360==0)b=1;
                else if(angle2%180==0)b=-1;
                var sx =  sr*b;
                vertices.push(sx,j*15);
        }
}因为没有计算y的坐标,所以得到一个很有意思的图形,如下http://img.my.csdn.net/uploads/201211/26/1353907834_7560.PNG
如果将计算好的x坐标和y坐标,同时带入到vertices数组中的话for(i=0;i<=cols;i++){
        for(j=0;j<=rows;j++){
                var angle = (90-180*j/rows)*Math.PI/180;
                var a = Math.sin(angle);
                if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
                if((90-180*j/rows)%180==0)a=0;
                var sy =  -r*a;
                var sa = Math.cos(angle);
                if((90-180*j/rows)%180==0)sa=1;
                var sr =  Math.abs(r*sa);
                var angle2 = 360*(i+1)/cols;
                var b = Math.cos(angle2*Math.PI/180);
                if(angle2%360==0)b=1;
                else if(angle2%180==0)b=-1;
                var sx =  sr*b;
                vertices.push(sx, sy);
        }
}得到一个完整的球体图形,如下http://img.my.csdn.net/uploads/201211/26/1353914919_6563.PNG
接下来就简单了,将空白图片换成地球的平面图,代码如下earthBitmapData = new LBitmapData(imglist["earth"]);再次运行代码,就可以得到下面的3D图形了http://img.my.csdn.net/uploads/201211/27/1353991290_7330.PNG
下面,该让这个地球转动起来了,根据上一篇介绍的内容,传入drawtriangles函数的uvtData数组中的元素是每个小三角形在原图片中的相对位置,它们决定了绘制图片的开始位置,如果将一组位置比如0123,变换其中的位置成为1230,再继续变换成2301,这样不断的进行位置变换,那么从视觉上,其实就已经实现了旋转了,那么在代码中,只需要将分割完的数组的按照每一列进行移动,每次都将第一列的两组三角形移到最后一列,这样第二列的两组三角形就变为了第一列,这样不停的变换就能让一个地球转动起来for (i = 0; i <= rows; i++) {
        uvtData.push(uvtData.shift());
        uvtData.push(uvtData.shift());
}如果要改变这个地球的大小的话,就更简单了,改变LSprite对象的scaleX和scaleY属性就可以改变它的大小了,大家可以点击下面的连接,来测试一下它的效果http://lufylegend.com/demo/3dearth/earch.html
备注:再次说明一下,本篇所介绍的内容需要HTML5开源引擎lufylegend的1.5.1版或以上版本的支持,lufylegend1.5.1版发布地址如下
http://lufylegend.com/lufylegend


wangcheng800718 发表于 2013-3-12 22:01:25

图片挂了。:L

leonly0224 发表于 2013-12-10 21:11:46

请问下,我想用你这个框架做个“长廊”效果
就是“网格”形状的“地板”,感觉是人在往前走,地板在往后退,
其实就是矩形图片,做了个梯形的缩放,然后平铺,滚动
应该怎么实现呢?望赐教

lufy 发表于 2013-12-10 23:28:39

leonly0224 发表于 2013-12-10 21:11 static/image/common/back.gif
请问下,我想用你这个框架做个“长廊”效果
就是“网格”形状的“地板”,感觉是人在往前走,地板在往后退 ...
就用这个帖子里讲的drawTriangles函数就可以实现啊,
控制好座标,让座标按照梯形轨道移动就可以了

leonly0224 发表于 2013-12-11 11:50:19

本帖最后由 leonly0224 于 2013-12-11 15:56 编辑

lufy 发表于 2013-12-10 23:28 static/image/common/back.gif
就用这个帖子里讲的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 ?

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

lufy 发表于 2013-12-11 22:19:27

leonly0224 发表于 2013-12-11 11:50 static/image/common/back.gif
你好,感谢回复!
我现在是这么弄的
首先图片按照你上面三国人物的划分方法来划分9个定点

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

总会花开 发表于 2014-1-19 19:22:04



   关注下~~~

macdaddy 发表于 2014-2-3 09:25:41

请问怎样实现贴图效果?贴在杯子上,或者贴在衣服上的立体效果。还有用你这个引擎做的地球实例,可以去掉上面很多条一圈圈的白线吗?谢谢

lufy 发表于 2014-2-3 23:06:41

macdaddy 发表于 2014-2-3 09:25 static/image/common/back.gif
请问怎样实现贴图效果?贴在杯子上,或者贴在衣服上的立体效果。还有用你这个引擎做的地球实例,可以去掉上 ...

就用该帖子说的方法就可以吧。
白线的问题是根据浏览器而定的,比如我的Mac版chrome就完全没有白线的

tomastong 发表于 2014-12-11 15:14:38

讲解的非常好,水平不够,需要慢慢理解!不过我想问一下楼主,这个lufylegend只能从你发布的那本书进行学习吗,有没其他的资源?
页: [1] 2 3
查看完整版本: HTML5高级编程之图形扭曲及其应用