如上图所示,这样的一个简单矩形,边界矩形是(x:-28, y:-35, width:152, height:128),这是在这个元件/显示对象自己的坐标空间的范围。

那么把这个放到父元件(舞台)中,再做一定变换。如下图所示,白色区域就是舞台,蓝色矩形中的白色十字架标记,就是世界坐标的(0,0)点。蓝色矩形的原点和世界坐标的原点对应,也就是说蓝色矩阵的坐标为(0,0)。

     

在舞台这个世界坐标系中,边界区域又是什么呢?我们的目标就是计算下图中的红色区域。

其实算法,很简单,在放到舞台之前,在蓝色矩形自己的局部坐标系中,边界是(x:-28, y:-35, width:152, height:128)。

那么,蓝色矩形4个顶点原来的坐标是可以轻易找到的:(-28,-35)、(152-28,-35)、(152,128)、(-28,128-35)。

矩形旋转了-60度,其实这个变换,可以具体转化为一个Matrix矩阵变换。通过自己计算或者利用Flash等辅助,可以知道Matrix如下:

  1. Matrix (a=0.4999847412109375, b=-0.865966796875, c=0.86602783203125, d=0.500030517578125, tx=-44.3, ty=6.8)

最后,计算出每个顶点经过Matrix变换后的新坐标,再通过这4个新坐标,算出上下左右的边框位置。

什么?怎么计算这个Matrix?好吧,先插播一下Matrix的计算。

首先,当然你得有一个Matrix类,可以参考任意语言的Matrix2D,其实都一样的。abcd和tx、ty,6个属性。

然后Matrix中实现rotate、translate等方法。

最后,简单2句,就可以算出:

  1. var matrix = new Matrix2D();
  2. matrix.rotate(-60*Math.PI/180);

回到正题,先把Matrix和rect的计算,封装一下:

  1. p._transformBounds = function (bounds, matrix) {
  2. if(bounds.x || bounds.y){
  3. matrix.append(1,0,0,1,bounds.x,bounds.y);
  4. }
  5.  
  6. var lt = {x:0, y:0};
  7. var rt = {x:bounds.width, y:0};
  8. var lb = {x:0, y:bounds.height};
  9. var rb = {x:bounds.width, y:bounds.height};
  10.  
  11. lt = transformPoint(matrix, lt);
  12. rt = transformPoint(matrix, rt);
  13. lb = transformPoint(matrix, lb);
  14. rb = transformPoint(matrix, rb);
  15.  
  16. var minX = Math.min(lt.x, rt.x, lb.x, rb.x);
  17. var minY = Math.min(lt.y, rt.y, lb.y, rb.y);
  18. var maxX = Math.max(lt.x, rt.x, lb.x, rb.x);
  19. var maxY = Math.max(lt.y, rt.y, lb.y, rb.y);
  20.  
  21. function transformPoint(m, p) {
  22. return {x: m.a * p.x + m.c * p.y + m.tx, y: m.b * p.x + m.d * p.y + m.ty};
  23. }
  24.  
  25. return new Rectangle(minX, minY, maxX - minX, maxY - minY);
  26. };

这里做了一个变化,如果bounds(也就是边框Rect)有x/y偏移,就把这个偏移转加到Matrix上,把rect简化为(0,0,width,height),这样有利于简化计算。

关键点是transformPoint函数,理解这个线性代数计算就能理解整个2D平面的算法。

其实,由于rect的x、y被简化为0,那么上述算法还可以优化,把函数拆开。最终变成:

  1. p._transformBounds = function(bounds, matrix) {
  2. var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height;
  3. var mtx = new Matrix2D();
  4. mtx.appendMatrix(matrix);
  5. if (x || y) { mtx.append(1,0,0,1,x,y); }
  6.  
  7. var x_a = width*mtx.a, x_b = width*mtx.b;
  8. var y_c = height*mtx.c, y_d = height*mtx.d;
  9. var tx = mtx.tx, ty = mtx.ty;
  10.  
  11. var minX = tx, maxX = tx, minY = ty, maxY = ty;
  12.  
  13. if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
  14. if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
  15. if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
  16.  
  17. if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
  18. if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
  19. if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
  20.  
  21. return new Rectangle(minX, minY, maxX-minX, maxY-minY);
  22. };

两个函数看起来相差很大,但如果大家有心思慢慢计算一下,会发现其实是一样的。

那么,整个计算的过程如下:

  1. p.test = function () {
  2. var bounds = new Rectangle(-28, -35, 152, 128);
  3. var matrix = new Matrix2D();
  4. matrix.rotate(-60*Math.PI/180);
  5. var t = this._transformBounds(bounds, matrix);
  6. console.log(t);
  7. };

得到

  1. Rectangle {x: -44.31088913245536, y: -124.88715006927039, width: 186.85125168440817, height: 195.6358613752347, initialize: function…}

大功告成。。。

说了一大堆废话,可能大家不理解这种计算意义何在,其实这种边框计算有很多用途,例如脏区重绘、碰撞检测。虽然一般来说,游戏框架都会给你封装好,但在某些情况下,还是不得不自己重新做一遍,这是Kenko在尝试做Flash转Canvas+脏区重绘的工作。

该项目名为Fanvas,现已加入2014年度腾讯6个公司级开源项目,相信不久将来大家会看到这个项目的开源。

【HTML5 Canvas】计算元件/显示对象经过Matrix变换后在上级/舞台上的bounds(边界矩形rect)的更多相关文章

  1. HTML5 Canvas 绘制库存变化折线 计算出库存周转率

    <!DOCTYPE html> <html lang="utf-8"> <meta http-equiv="Content-Type&quo ...

  2. HTML5 Canvas 绘制库存变化折线 计算出最高最低库存

    <!DOCTYPE html> <html lang="utf-8"> <meta http-equiv="Content-Type&quo ...

  3. 如何在HTML5 Canvas 里面显示 Font Awesome 图标

        Font Awesome 是一套完美的图标字体,主要目的是和 Bootstrap 搭配使用. 提供的CSS 已经可以完美显示这些图标在网页里面.最新的版本4.3 里面,已经提供519 Icon ...

  4. HTML5 程序设计 - 使用HTML5 Canvas API

    请你跟着本篇示例代码实现每个示例,30分钟后,你会高喊:“HTML5 Canvas?!在哥面前,那都不是事儿!” 呵呵.不要被滚动条吓到,很多都是代码和图片.我没有分开写,不过上面给大家提供了目录,方 ...

  5. 使用html5 canvas绘制圆形或弧线

    注意:本文属于<html5 Canvas绘制图形入门详解>系列文章中的一部分.如果你是html5初学者,仅仅阅读本文,可能无法较深入的理解canvas,甚至无法顺畅地通读本文.请点击上述链 ...

  6. html5 Canvas API

    详细内容请点击 1.HTML Canvas API有两方面优势可以弥补:首先,不需要将所绘制图像中的每个图元当做对象存储,因此执行性能非常好:其次,在其他编程语言现有的优秀二维绘图API的基础上实现C ...

  7. 怎样用HTML5 Canvas制作一个简单的游戏

    原文连接: How To Make A Simple HTML5 Canvas Game 自从我制作了一些HTML5游戏(例如Crypt Run)后,我收到了很多建议,要求我写一篇关于怎样利用HTML ...

  8. HTML5 canvas游戏工作原理

    HTML5已经不是一个新名词.它看上去很cool,有很多feature,大多数人普遍看好它的发展.对于我来说,最感兴趣的是它的canvas标签,可以结合Javascript来绘制游戏画面. 我们可以在 ...

  9. html5 canvas时钟

    基础知识点:                canvas标签只是图形容器,您必须使用脚本来绘制图形. getContext() 方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性.——获取上 ...

随机推荐

  1. sublime text的扩展插件

    sublime text用作开发编辑器,还缺省二个比较重要功能:跨文件跳转.返回最后一次编辑的位置: 这里有二个插件正好解决此问题:CTags.ChangeList   其它常用的插件,google一 ...

  2. parity 钱包

    数据快照路径 C:\Users\admin\AppData\Local\Parity\Ethereum\chains\ethereum\db\906a34e69aec8c0d\snapshot\res ...

  3. Eclipse断点调试(DBG)Android应用

    1.添加断点 双击左侧边框便可添加断点,右击也能添加断点. 2.进入调试模式 点击虫子,然后选择工程运行,快捷键为单击F11 ,如果是正常运行就是Ctrl+F11 3.单步调试+跳到下一个断点 运行到 ...

  4. java 模拟发送post请求测试

    方法一: HttpClient public void postTest(HttpServletRequest request,Integer type,String phone,String pas ...

  5. Greenplum入门——基础知识、安装、常用函数

    Greenplum入门——基础知识.安装.常用函数 2017年10月08日 22:03:09 在咖啡里溺水的鱼 阅读数:8709    版权声明:本文为博主原创,允许非商业性质转载但请注明原作者和出处 ...

  6. Java多线程之Callable接口与Runnable的实现以及选择

    通过实现Runnable接口的实现 package Thread; import java.util.concurrent.ExecutorService;import java.util.concu ...

  7. Chapter 5 -- ImmutableCollections

    Example publicstatic final ImmutableSet<String> COLOR_NAMES =ImmutableSet.of(  "red" ...

  8. 使用C++/libCurl/Jsoncpp读取arcgis wmts 服务(restful模式)

    前言: 最近工作需要将arcgis的wmts服务接入我们的3DGis系统平台,要求用户只输入一个rest模式的wmts服务地址,系统即可自动获取并解析其元数据信息,生成wmts图层,并渲染显示.经过多 ...

  9. asp.net 读取word 文档的方法

    资料一:适合读取并显示(简单而明了) 第一种方法:    Response.ClearContent(); Response.ClearHeaders();   Response.ContentTyp ...

  10. SpringBoot-Mybatis_Plus学习记录之公共字段自动填充

    一.应用场景 平时在建对象表的时候都会有最后修改时间,最后修改人这两个字段,对于这些大部分表都有的字段,每次在新增和修改的时候都要考虑到这几个字段有没有传进去,很麻烦.mybatisPlus有一个很好 ...