Source: hook/transform.js

  1. /**
  2. * Copyright (c) 2015 NAVER Corp.
  3. * egjs projects are licensed under the MIT license
  4. */
  5. /**
  6. * A method that extends the <a href=http://api.jquery.com/animate/>.animate()</a> method provided by jQuery. With this method, you can use the transform property and 3D acceleration
  7. * @ko jQuery의<a href=http://api.jquery.com/animate/>animate() 메서드</a>를 확장한 메서드. CSS의 transform 속성과 3D 가속을 사용할 수 있다.
  8. * @name jQuery#animate
  9. * @method
  10. * @param {Object} properties An object composed of the CSS property and value which will be applied to an animation<ko>애니메이션을 적용할 CSS 속성과 값으로 구성된 객체</ko>
  11. * @param {Number|String} [duration=4000] Duration of the animation (unit: ms)<ko>애니메이션 진행 시간(단위: ms)</ko>
  12. * @param {String} [easing="swing"] The easing function to apply to an animation<ko>애니메이션에 적용할 easing 함수</ko>
  13. * @param {Function} [complete] A function that is called once the animation is complete.<ko>애니메이션을 완료한 다음 호출할 함수</ko>
  14. *
  15. * @example
  16. * $("#box")
  17. * .animate({"transform" : "translate3d(150px, 100px, 0px) rotate(20deg) scaleX(1)"} , 3000)
  18. * .animate({"transform" : "+=translate3d(150px, 10%, -20px) rotate(20deg) scale3d(2, 4.2, 1)"} , 3000);
  19. * @see {@link http://api.jquery.com/animate/}
  20. *
  21. * @support {"ie": "10+", "ch" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"}
  22. */
  23. eg.module("transform", ["jQuery", window], function($) {
  24. "use strict";
  25. /**
  26. * Get a 'px' converted value if it has a %.
  27. * Otherwise it returns value appened with 'px'.
  28. */
  29. function getConverted(val, base) {
  30. var ret = val;
  31. var num = val.match(/((-|\+)*[0-9]+)%/);
  32. if (num && num.length >= 1) {
  33. ret = base * (parseFloat(num[1]) / 100) + "px";
  34. } else if (val.indexOf("px") === -1) {
  35. ret = val + "px";
  36. }
  37. return ret;
  38. }
  39. function correctUnit(transform, width, height) {
  40. var m;
  41. var ret = "";
  42. var arr = transform.split(")");
  43. for (var i = 0, len = arr.length - 1; i < len; i++) {
  44. var name = arr[i];
  45. // '%' is only meaningful on translate.
  46. if ((m = name.match(/(translate([XYZ]|3d)?|rotate)\(([^)]*)/)) && m.length > 1) {
  47. if (m[1] === "rotate") {
  48. if (m[3].indexOf("deg") === -1) {
  49. name = m[1] + "(" + m[3] + "deg";
  50. }
  51. } else {
  52. switch (m[2]) {
  53. case "X":
  54. name = m[1] + "(" + getConverted(m[3], width);
  55. break;
  56. case "Y":
  57. name = m[1] + "(" + getConverted(m[3], height);
  58. break;
  59. case "Z":
  60. //Meaningless. Do nothing
  61. break;
  62. default://2d, 3d
  63. var nums = m[3].split(",");
  64. var bases = [width, height, 100];
  65. for (var k = 0, l = nums.length; k < l; k++) {
  66. nums[k] = getConverted(nums[k], bases[k]);
  67. }
  68. name = m[1] + "(" + nums.join(",");
  69. break;
  70. }
  71. }
  72. }
  73. name = " " + name + ")";
  74. ret += name;
  75. }
  76. //Remove wrong '%'s and '+=' because it cannot be translated to a matrix.
  77. ret = ret.replace("%", "").replace("+=", "");
  78. return ret;
  79. }
  80. /**
  81. * Parse a transform atom value.
  82. *
  83. * "30px" --> {num: 30, unit: "px"}
  84. *
  85. * Because calculation of string number is heavy,
  86. * In advance, convert a string number to a float number with an unit for the use of transformByPos,
  87. * which is called very frequently.
  88. */
  89. function toParsedFloat(val) {
  90. var m = val.match(/((-|\+)*[\d|\.]+)(px|deg|rad)*/);
  91. if (m && m.length >= 1) {
  92. return {"num": parseFloat(m[1]), "unit": m[3]};
  93. }
  94. }
  95. function getTransformGenerateFunction(transform) {
  96. var splitted = transform.split(")");
  97. var list = [];
  98. //Make parsed transform list.
  99. for (var i = 0, len = splitted.length - 1; i < len; i++) {
  100. var parsed = parseStyle(splitted[i]);
  101. parsed[1] = $.map(parsed[1], toParsedFloat);
  102. list.push(parsed);
  103. }
  104. return function transformByPos(pos) {
  105. var transform = "";
  106. var defaultVal = 0;
  107. $.each(list, function(i) {
  108. if (list[i][0].indexOf("scale") >= 0) {
  109. defaultVal = 1;
  110. } else {
  111. defaultVal = 0;
  112. }
  113. var valStr = $.map(list[i][1], function(value) {
  114. var val = value.num;
  115. defaultVal === 1 && (val = val - 1);
  116. return (defaultVal + val * pos) + (value.unit || "");
  117. }).join(",");
  118. transform += list[i][0] + "(" + valStr + ") ";
  119. });
  120. return transform;
  121. };
  122. }
  123. function rateFn(element, startTf, endTf) {
  124. var isRelative = endTf.indexOf("+=") >= 0;
  125. var start;
  126. var end;
  127. var basePos;
  128. // Convert translate unit to 'px'.
  129. endTf = correctUnit(endTf,
  130. parseFloat($.css(element, "width")) || 0,
  131. parseFloat($.css(element, "height")) || 0);
  132. if (isRelative) {
  133. start = (!startTf || startTf === "none") ?
  134. "matrix(1, 0, 0, 1, 0, 0)" : startTf;
  135. end = getTransformGenerateFunction(endTf);
  136. } else {
  137. start = toMatrixArray(startTf);
  138. basePos = toMatrixArray("none");//transform base-position
  139. //If the type of matrix is not equal, then match to matrix3d
  140. if (start[1].length < basePos[1].length) {
  141. start = toMatrix3d(start);
  142. } else if (start[1].length > basePos[1].length) {
  143. basePos = toMatrix3d(basePos);
  144. }
  145. end = getTransformGenerateFunction(endTf);
  146. }
  147. return function(pos) {
  148. var result = [];
  149. var ret = "";//matrix for interpolated value from current to base(1, 0, 0, 1, 0, 0)
  150. if (isRelative) {
  151. // This means a muliply between a matrix and a transform.
  152. return start + end(pos);
  153. }
  154. if (pos === 1) {
  155. ret = data2String(basePos);
  156. } else {
  157. for (var i = 0, s, e, l = start[1].length; i < l; i++) {
  158. s = parseFloat(start[1][i]);
  159. e = parseFloat(basePos[1][i]);
  160. result.push(s + (e - s) * pos);
  161. }
  162. ret = data2String([start[0], result]);
  163. }
  164. return ret + end(pos);
  165. };
  166. }
  167. /**
  168. * ["translate", [100, 0]] --> translate(100px, 0)
  169. * {translate : [100, 0]} --> translate(100px, 0)
  170. * {matrix : [1, 0, 1, 0, 100, 0]} --> matrix(1, 0, 1, 0, 100, 0)
  171. */
  172. function data2String(property) {
  173. var name;
  174. var tmp = [];
  175. if ($.isArray(property)) {
  176. name = property[0];
  177. return name + "(" + property[1].join(unit(name) + ",") + unit(name) + ")";
  178. } else {
  179. for (name in property) {
  180. tmp.push(name);
  181. }
  182. return $.map(tmp, function(v) {
  183. return v + "(" + property[v] + unit(v) + ")";
  184. }).join(" ");
  185. }
  186. }
  187. function unit(name) {
  188. return name.indexOf("translate") >= 0 ?
  189. "px" : name.indexOf("rotate") >= 0 ? "deg" : "";
  190. }
  191. // ["translate" , ["10", "20"]]
  192. function parseStyle(property) {
  193. var m = property.match(/(\b\w+?)\((\s*[^\)]+)/);
  194. var name;
  195. var value;
  196. var result = ["",""];
  197. if (m && m.length > 2) {
  198. name = m[1];
  199. value = m[2].split(",");
  200. value = $.map(value, function(v) {
  201. return $.trim(v);
  202. });
  203. result = [ $.trim(name), value ];
  204. }
  205. return result;
  206. }
  207. /**
  208. * Convert matrix string to array type.
  209. *
  210. * eg. matrix(1, 0, 0, 1, 0, 0) ==> ["matrix", [1, 0, 0, 1, 0, 0]]
  211. * matrix3d(1,0,0,0,0,1,-2.44929e-16,0,0,2.44929e-16,1,0,0,0,0,1)
  212. */
  213. function toMatrixArray(matrixStr) {
  214. var matched;
  215. if (!matrixStr || matrixStr === "none") {
  216. return ["matrix", [ "1", "0", "0", "1", "0", "0"] ];
  217. }
  218. matrixStr = matrixStr.replace(/\s/g, "");
  219. matched = matrixStr.match(/(matrix)(3d)*\((.*)\)/);
  220. return [matched[1] + (matched[2] || ""), matched[3].split(",")];
  221. }
  222. function toMatrix3d(matrix) {
  223. var name = matrix[0];
  224. var val = matrix[1];
  225. if (name === "matrix3d") {
  226. return matrix;
  227. }
  228. // matrix(a, b, c, d, tx, ty) is a shorthand for matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1)
  229. return [
  230. name + "3d", [val[0], val[1], "0", "0",
  231. val[2], val[3], "0", "0",
  232. "0", "0", "1", "0",
  233. val[4], val[5], "0", "1"]
  234. ];
  235. }
  236. $.fx.step.transform = function(fx) {
  237. fx.rateFn = fx.rateFn || rateFn(fx.elem, fx.start, fx.end);
  238. $.style(fx.elem, "transform", fx.rateFn(fx.pos));
  239. };
  240. // All of this interfaces are functions for unit testing.
  241. return {
  242. toMatrix: toMatrixArray,
  243. toMatrix3d: toMatrix3d
  244. };
  245. });
comments powered by Disqus