Transformable.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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. var scaleTmp = [];
  60. transformableProto.updateTransform = function () {
  61. var parent = this.parent;
  62. var parentHasTransform = parent && parent.transform;
  63. var needLocalTransform = this.needLocalTransform();
  64. var m = this.transform;
  65. if (!(needLocalTransform || parentHasTransform)) {
  66. m && mIdentity(m);
  67. return;
  68. }
  69. m = m || matrix.create();
  70. if (needLocalTransform) {
  71. this.getLocalTransform(m);
  72. } else {
  73. mIdentity(m);
  74. } // 应用父节点变换
  75. if (parentHasTransform) {
  76. if (needLocalTransform) {
  77. matrix.mul(m, parent.transform, m);
  78. } else {
  79. matrix.copy(m, parent.transform);
  80. }
  81. } // 保存这个变换矩阵
  82. this.transform = m;
  83. var globalScaleRatio = this.globalScaleRatio;
  84. if (globalScaleRatio != null && globalScaleRatio !== 1) {
  85. this.getGlobalScale(scaleTmp);
  86. var relX = scaleTmp[0] < 0 ? -1 : 1;
  87. var relY = scaleTmp[1] < 0 ? -1 : 1;
  88. var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;
  89. var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;
  90. m[0] *= sx;
  91. m[1] *= sx;
  92. m[2] *= sy;
  93. m[3] *= sy;
  94. }
  95. this.invTransform = this.invTransform || matrix.create();
  96. matrix.invert(this.invTransform, m);
  97. };
  98. transformableProto.getLocalTransform = function (m) {
  99. return Transformable.getLocalTransform(this, m);
  100. };
  101. /**
  102. * 将自己的transform应用到context上
  103. * @param {CanvasRenderingContext2D} ctx
  104. */
  105. transformableProto.setTransform = function (ctx) {
  106. var m = this.transform;
  107. var dpr = ctx.dpr || 1;
  108. if (m) {
  109. ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
  110. } else {
  111. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  112. }
  113. };
  114. transformableProto.restoreTransform = function (ctx) {
  115. var dpr = ctx.dpr || 1;
  116. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  117. };
  118. var tmpTransform = [];
  119. var originTransform = matrix.create();
  120. transformableProto.setLocalTransform = function (m) {
  121. if (!m) {
  122. // TODO return or set identity?
  123. return;
  124. }
  125. var sx = m[0] * m[0] + m[1] * m[1];
  126. var sy = m[2] * m[2] + m[3] * m[3];
  127. var position = this.position;
  128. var scale = this.scale;
  129. if (isNotAroundZero(sx - 1)) {
  130. sx = Math.sqrt(sx);
  131. }
  132. if (isNotAroundZero(sy - 1)) {
  133. sy = Math.sqrt(sy);
  134. }
  135. if (m[0] < 0) {
  136. sx = -sx;
  137. }
  138. if (m[3] < 0) {
  139. sy = -sy;
  140. }
  141. position[0] = m[4];
  142. position[1] = m[5];
  143. scale[0] = sx;
  144. scale[1] = sy;
  145. this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
  146. };
  147. /**
  148. * 分解`transform`矩阵到`position`, `rotation`, `scale`
  149. */
  150. transformableProto.decomposeTransform = function () {
  151. if (!this.transform) {
  152. return;
  153. }
  154. var parent = this.parent;
  155. var m = this.transform;
  156. if (parent && parent.transform) {
  157. // Get local transform and decompose them to position, scale, rotation
  158. matrix.mul(tmpTransform, parent.invTransform, m);
  159. m = tmpTransform;
  160. }
  161. var origin = this.origin;
  162. if (origin && (origin[0] || origin[1])) {
  163. originTransform[4] = origin[0];
  164. originTransform[5] = origin[1];
  165. matrix.mul(tmpTransform, m, originTransform);
  166. tmpTransform[4] -= origin[0];
  167. tmpTransform[5] -= origin[1];
  168. m = tmpTransform;
  169. }
  170. this.setLocalTransform(m);
  171. };
  172. /**
  173. * Get global scale
  174. * @return {Array.<number>}
  175. */
  176. transformableProto.getGlobalScale = function (out) {
  177. var m = this.transform;
  178. out = out || [];
  179. if (!m) {
  180. out[0] = 1;
  181. out[1] = 1;
  182. return out;
  183. }
  184. out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
  185. out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
  186. if (m[0] < 0) {
  187. out[0] = -out[0];
  188. }
  189. if (m[3] < 0) {
  190. out[1] = -out[1];
  191. }
  192. return out;
  193. };
  194. /**
  195. * 变换坐标位置到 shape 的局部坐标空间
  196. * @method
  197. * @param {number} x
  198. * @param {number} y
  199. * @return {Array.<number>}
  200. */
  201. transformableProto.transformCoordToLocal = function (x, y) {
  202. var v2 = [x, y];
  203. var invTransform = this.invTransform;
  204. if (invTransform) {
  205. vector.applyTransform(v2, v2, invTransform);
  206. }
  207. return v2;
  208. };
  209. /**
  210. * 变换局部坐标位置到全局坐标空间
  211. * @method
  212. * @param {number} x
  213. * @param {number} y
  214. * @return {Array.<number>}
  215. */
  216. transformableProto.transformCoordToGlobal = function (x, y) {
  217. var v2 = [x, y];
  218. var transform = this.transform;
  219. if (transform) {
  220. vector.applyTransform(v2, v2, transform);
  221. }
  222. return v2;
  223. };
  224. /**
  225. * @static
  226. * @param {Object} target
  227. * @param {Array.<number>} target.origin
  228. * @param {number} target.rotation
  229. * @param {Array.<number>} target.position
  230. * @param {Array.<number>} [m]
  231. */
  232. Transformable.getLocalTransform = function (target, m) {
  233. m = m || [];
  234. mIdentity(m);
  235. var origin = target.origin;
  236. var scale = target.scale || [1, 1];
  237. var rotation = target.rotation || 0;
  238. var position = target.position || [0, 0];
  239. if (origin) {
  240. // Translate to origin
  241. m[4] -= origin[0];
  242. m[5] -= origin[1];
  243. }
  244. matrix.scale(m, m, scale);
  245. if (rotation) {
  246. matrix.rotate(m, m, rotation);
  247. }
  248. if (origin) {
  249. // Translate back from origin
  250. m[4] += origin[0];
  251. m[5] += origin[1];
  252. }
  253. m[4] += position[0];
  254. m[5] += position[1];
  255. return m;
  256. };
  257. var _default = Transformable;
  258. module.exports = _default;