Transformable.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. var matrix = require("../core/matrix");
  2. var vector = require("../core/vector");
  3. /**
  4. * 提供变换扩展
  5. * @module zrender/mixin/Transformable
  6. * @author pissang (https://www.github.com/pissang)
  7. */
  8. var mIdentity = matrix.identity;
  9. var EPSILON = 5e-5;
  10. function isNotAroundZero(val) {
  11. return val > EPSILON || val < -EPSILON;
  12. }
  13. /**
  14. * @alias module:zrender/mixin/Transformable
  15. * @constructor
  16. */
  17. var Transformable = function (opts) {
  18. opts = opts || {}; // If there are no given position, rotation, scale
  19. if (!opts.position) {
  20. /**
  21. * 平移
  22. * @type {Array.<number>}
  23. * @default [0, 0]
  24. */
  25. this.position = [0, 0];
  26. }
  27. if (opts.rotation == null) {
  28. /**
  29. * 旋转
  30. * @type {Array.<number>}
  31. * @default 0
  32. */
  33. this.rotation = 0;
  34. }
  35. if (!opts.scale) {
  36. /**
  37. * 缩放
  38. * @type {Array.<number>}
  39. * @default [1, 1]
  40. */
  41. this.scale = [1, 1];
  42. }
  43. /**
  44. * 旋转和缩放的原点
  45. * @type {Array.<number>}
  46. * @default null
  47. */
  48. this.origin = this.origin || null;
  49. };
  50. var transformableProto = Transformable.prototype;
  51. transformableProto.transform = null;
  52. /**
  53. * 判断是否需要有坐标变换
  54. * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵
  55. */
  56. transformableProto.needLocalTransform = function () {
  57. return isNotAroundZero(this.rotation) || isNotAroundZero(this.position[0]) || isNotAroundZero(this.position[1]) || isNotAroundZero(this.scale[0] - 1) || isNotAroundZero(this.scale[1] - 1);
  58. };
  59. transformableProto.updateTransform = function () {
  60. var parent = this.parent;
  61. var parentHasTransform = parent && parent.transform;
  62. var needLocalTransform = this.needLocalTransform();
  63. var m = this.transform;
  64. if (!(needLocalTransform || parentHasTransform)) {
  65. m && mIdentity(m);
  66. return;
  67. }
  68. m = m || matrix.create();
  69. if (needLocalTransform) {
  70. this.getLocalTransform(m);
  71. } else {
  72. mIdentity(m);
  73. } // 应用父节点变换
  74. if (parentHasTransform) {
  75. if (needLocalTransform) {
  76. matrix.mul(m, parent.transform, m);
  77. } else {
  78. matrix.copy(m, parent.transform);
  79. }
  80. } // 保存这个变换矩阵
  81. this.transform = m;
  82. this.invTransform = this.invTransform || matrix.create();
  83. matrix.invert(this.invTransform, m);
  84. };
  85. transformableProto.getLocalTransform = function (m) {
  86. return Transformable.getLocalTransform(this, m);
  87. };
  88. /**
  89. * 将自己的transform应用到context上
  90. * @param {CanvasRenderingContext2D} ctx
  91. */
  92. transformableProto.setTransform = function (ctx) {
  93. var m = this.transform;
  94. var dpr = ctx.dpr || 1;
  95. if (m) {
  96. ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
  97. } else {
  98. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  99. }
  100. };
  101. transformableProto.restoreTransform = function (ctx) {
  102. var dpr = ctx.dpr || 1;
  103. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  104. };
  105. var tmpTransform = [];
  106. /**
  107. * 分解`transform`矩阵到`position`, `rotation`, `scale`
  108. */
  109. transformableProto.decomposeTransform = function () {
  110. if (!this.transform) {
  111. return;
  112. }
  113. var parent = this.parent;
  114. var m = this.transform;
  115. if (parent && parent.transform) {
  116. // Get local transform and decompose them to position, scale, rotation
  117. matrix.mul(tmpTransform, parent.invTransform, m);
  118. m = tmpTransform;
  119. }
  120. var sx = m[0] * m[0] + m[1] * m[1];
  121. var sy = m[2] * m[2] + m[3] * m[3];
  122. var position = this.position;
  123. var scale = this.scale;
  124. if (isNotAroundZero(sx - 1)) {
  125. sx = Math.sqrt(sx);
  126. }
  127. if (isNotAroundZero(sy - 1)) {
  128. sy = Math.sqrt(sy);
  129. }
  130. if (m[0] < 0) {
  131. sx = -sx;
  132. }
  133. if (m[3] < 0) {
  134. sy = -sy;
  135. }
  136. position[0] = m[4];
  137. position[1] = m[5];
  138. scale[0] = sx;
  139. scale[1] = sy;
  140. this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
  141. };
  142. /**
  143. * Get global scale
  144. * @return {Array.<number>}
  145. */
  146. transformableProto.getGlobalScale = function () {
  147. var m = this.transform;
  148. if (!m) {
  149. return [1, 1];
  150. }
  151. var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
  152. var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
  153. if (m[0] < 0) {
  154. sx = -sx;
  155. }
  156. if (m[3] < 0) {
  157. sy = -sy;
  158. }
  159. return [sx, sy];
  160. };
  161. /**
  162. * 变换坐标位置到 shape 的局部坐标空间
  163. * @method
  164. * @param {number} x
  165. * @param {number} y
  166. * @return {Array.<number>}
  167. */
  168. transformableProto.transformCoordToLocal = function (x, y) {
  169. var v2 = [x, y];
  170. var invTransform = this.invTransform;
  171. if (invTransform) {
  172. vector.applyTransform(v2, v2, invTransform);
  173. }
  174. return v2;
  175. };
  176. /**
  177. * 变换局部坐标位置到全局坐标空间
  178. * @method
  179. * @param {number} x
  180. * @param {number} y
  181. * @return {Array.<number>}
  182. */
  183. transformableProto.transformCoordToGlobal = function (x, y) {
  184. var v2 = [x, y];
  185. var transform = this.transform;
  186. if (transform) {
  187. vector.applyTransform(v2, v2, transform);
  188. }
  189. return v2;
  190. };
  191. /**
  192. * @static
  193. * @param {Object} target
  194. * @param {Array.<number>} target.origin
  195. * @param {number} target.rotation
  196. * @param {Array.<number>} target.position
  197. * @param {Array.<number>} [m]
  198. */
  199. Transformable.getLocalTransform = function (target, m) {
  200. m = m || [];
  201. mIdentity(m);
  202. var origin = target.origin;
  203. var scale = target.scale || [1, 1];
  204. var rotation = target.rotation || 0;
  205. var position = target.position || [0, 0];
  206. if (origin) {
  207. // Translate to origin
  208. m[4] -= origin[0];
  209. m[5] -= origin[1];
  210. }
  211. matrix.scale(m, m, scale);
  212. if (rotation) {
  213. matrix.rotate(m, m, rotation);
  214. }
  215. if (origin) {
  216. // Translate back from origin
  217. m[4] += origin[0];
  218. m[5] += origin[1];
  219. }
  220. m[4] += position[0];
  221. m[5] += position[1];
  222. return m;
  223. };
  224. var _default = Transformable;
  225. module.exports = _default;