mui.pullToRefresh.material.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. (function($) {
  2. var CLASS_PULL_TOP_TIPS = $.className('pull-top-tips');
  3. $.PullToRefresh = $.PullToRefresh.extend({
  4. init: function(element, options) {
  5. this._super(element, options);
  6. this.options = $.extend(true, {
  7. down: {
  8. tips: {
  9. colors: ['008000', 'd8ad44', 'd00324', 'dc00b8', '017efc'],
  10. size: 200, //width=height=size;x=y=size/2;radius=size/4
  11. lineWidth: 15,
  12. duration: 1000,
  13. tail_duration: 1000 * 2.5
  14. }
  15. }
  16. }, this.options);
  17. this.options.down.tips.color = this.options.down.tips.colors[0];
  18. this.options.down.tips.colors = this.options.down.tips.colors.map(function(color) {
  19. return {
  20. r: parseInt(color.substring(0, 2), 16),
  21. g: parseInt(color.substring(2, 4), 16),
  22. b: parseInt(color.substring(4, 6), 16)
  23. };
  24. });
  25. },
  26. initPullDownTips: function() {
  27. var self = this;
  28. if ($.isFunction(self.options.down.callback)) {
  29. self.pullDownTips = (function() {
  30. var element = document.querySelector('.' + CLASS_PULL_TOP_TIPS);
  31. if (element) {
  32. element.parentNode.removeChild(element);
  33. }
  34. if (!element) {
  35. element = document.createElement('div');
  36. element.classList.add(CLASS_PULL_TOP_TIPS);
  37. element.innerHTML = '<div class="mui-pull-top-wrapper"><div class="mui-pull-top-canvas"><canvas id="pullDownTips" width="' + self.options.down.tips.size + '" height="' + self.options.down.tips.size + '"></canvas></div></div>';
  38. element.addEventListener('webkitTransitionEnd', self);
  39. document.body.appendChild(element);
  40. }
  41. self.pullDownCanvas = document.getElementById("pullDownTips");
  42. self.pullDownCanvasCtx = self.pullDownCanvas.getContext('2d');
  43. self.canvasUtils.init(self.pullDownCanvas, self.options.down.tips);
  44. return element;
  45. }());
  46. }
  47. },
  48. removePullDownTips: function() {
  49. this._super();
  50. this.canvasUtils.stopSpin();
  51. },
  52. pulling: function(deltaY) {
  53. var ratio = Math.min(deltaY / (this.options.down.height * 1.5), 1);
  54. var ratioPI = Math.min(1, ratio * 2);
  55. this.pullDownTips.style.webkitTransform = 'translate3d(0,' + (deltaY < 0 ? 0 : deltaY) + 'px,0)';
  56. this.pullDownCanvas.style.opacity = ratioPI;
  57. this.pullDownCanvas.style.webkitTransform = 'rotate(' + 300 * ratio + 'deg)';
  58. var canvas = this.pullDownCanvas;
  59. var ctx = this.pullDownCanvasCtx;
  60. var size = this.options.down.tips.size;
  61. ctx.lineWidth = this.options.down.tips.lineWidth;
  62. ctx.fillStyle = '#' + this.options.down.tips.color;
  63. ctx.strokeStyle = '#' + this.options.down.tips.color;
  64. ctx.stroke();
  65. ctx.clearRect(0, 0, size, size);
  66. //fixed android 4.1.x
  67. canvas.style.display = 'none'; // Detach from DOM
  68. canvas.offsetHeight; // Force the detach
  69. canvas.style.display = 'inherit'; // Reattach to DOM
  70. this.canvasUtils.drawArcedArrow(ctx, size / 2 + 0.5, size / 2, size / 4, 0 * Math.PI, 5 / 3 * Math.PI * ratioPI, false, 1, 2, 0.7853981633974483, 25, this.options.down.tips.lineWidth, this.options.down.tips.lineWidth);
  71. },
  72. beforeChangeOffset: function(deltaY) {},
  73. afterChangeOffset: function(deltaY) {},
  74. dragEndAfterChangeOffset: function(isNeedRefresh) {
  75. if (isNeedRefresh) {
  76. this.canvasUtils.startSpin();
  77. this.pullDownLoading();
  78. } else {
  79. this.canvasUtils.stopSpin();
  80. this.endPullDownToRefresh();
  81. }
  82. },
  83. canvasUtils: (function() {
  84. var canvasObj = null,
  85. ctx = null,
  86. size = 200,
  87. lineWidth = 15,
  88. tick = 0,
  89. startTime = 0,
  90. frameTime = 0,
  91. timeLast = 0,
  92. oldStep = 0,
  93. acc = 0,
  94. head = 0,
  95. tail = 180,
  96. rad = Math.PI / 180,
  97. duration = 1000,
  98. tail_duration = 1000 * 2.5,
  99. colors = ['35ad0e', 'd8ad44', 'd00324', 'dc00b8', '017efc'],
  100. rAF = null;
  101. function easeLinear(currentIteration, startValue, changeInValue, totalIterations) {
  102. return changeInValue * currentIteration / totalIterations + startValue;
  103. }
  104. function easeInOutQuad(currentIteration, startValue, changeInValue, totalIterations) {
  105. if ((currentIteration /= totalIterations / 2) < 1) {
  106. return changeInValue / 2 * currentIteration * currentIteration + startValue;
  107. }
  108. return -changeInValue / 2 * ((--currentIteration) * (currentIteration - 2) - 1) + startValue;
  109. }
  110. function minmax(value, v0, v1) {
  111. var min = Math.min(v0, v1);
  112. var max = Math.max(v0, v1);
  113. if (value < min)
  114. return min;
  115. if (value > max)
  116. return min;
  117. return value;
  118. }
  119. var drawHead = function(ctx, x0, y0, x1, y1, x2, y2, style) {
  120. 'use strict';
  121. if (typeof(x0) == 'string') x0 = parseInt(x0);
  122. if (typeof(y0) == 'string') y0 = parseInt(y0);
  123. if (typeof(x1) == 'string') x1 = parseInt(x1);
  124. if (typeof(y1) == 'string') y1 = parseInt(y1);
  125. if (typeof(x2) == 'string') x2 = parseInt(x2);
  126. if (typeof(y2) == 'string') y2 = parseInt(y2);
  127. var radius = 3;
  128. var twoPI = 2 * Math.PI;
  129. ctx.save();
  130. ctx.beginPath();
  131. ctx.moveTo(x0, y0);
  132. ctx.lineTo(x1, y1);
  133. ctx.lineTo(x2, y2);
  134. switch (style) {
  135. case 0:
  136. var backdist = Math.sqrt(((x2 - x0) * (x2 - x0)) + ((y2 - y0) * (y2 - y0)));
  137. ctx.arcTo(x1, y1, x0, y0, .55 * backdist);
  138. ctx.fill();
  139. break;
  140. case 1:
  141. ctx.beginPath();
  142. ctx.moveTo(x0, y0);
  143. ctx.lineTo(x1, y1);
  144. ctx.lineTo(x2, y2);
  145. ctx.lineTo(x0, y0);
  146. ctx.fill();
  147. break;
  148. case 2:
  149. ctx.stroke();
  150. break;
  151. case 3:
  152. var cpx = (x0 + x1 + x2) / 3;
  153. var cpy = (y0 + y1 + y2) / 3;
  154. ctx.quadraticCurveTo(cpx, cpy, x0, y0);
  155. ctx.fill();
  156. break;
  157. case 4:
  158. var cp1x, cp1y, cp2x, cp2y, backdist;
  159. var shiftamt = 5;
  160. if (x2 == x0) {
  161. backdist = y2 - y0;
  162. cp1x = (x1 + x0) / 2;
  163. cp2x = (x1 + x0) / 2;
  164. cp1y = y1 + backdist / shiftamt;
  165. cp2y = y1 - backdist / shiftamt;
  166. } else {
  167. backdist = Math.sqrt(((x2 - x0) * (x2 - x0)) + ((y2 - y0) * (y2 - y0)));
  168. var xback = (x0 + x2) / 2;
  169. var yback = (y0 + y2) / 2;
  170. var xmid = (xback + x1) / 2;
  171. var ymid = (yback + y1) / 2;
  172. var m = (y2 - y0) / (x2 - x0);
  173. var dx = (backdist / (2 * Math.sqrt(m * m + 1))) / shiftamt;
  174. var dy = m * dx;
  175. cp1x = xmid - dx;
  176. cp1y = ymid - dy;
  177. cp2x = xmid + dx;
  178. cp2y = ymid + dy;
  179. }
  180. ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x0, y0);
  181. ctx.fill();
  182. break;
  183. }
  184. ctx.restore();
  185. };
  186. var drawArcedArrow = function(ctx, x, y, r, startangle, endangle, anticlockwise, style, which, angle, d, lineWidth, lineRatio) {
  187. 'use strict';
  188. style = typeof(style) != 'undefined' ? style : 3;
  189. which = typeof(which) != 'undefined' ? which : 1;
  190. angle = typeof(angle) != 'undefined' ? angle : Math.PI / 8;
  191. lineWidth = lineWidth || 1;
  192. lineRatio = lineRatio || 10;
  193. d = typeof(d) != 'undefined' ? d : 10;
  194. ctx.save();
  195. ctx.lineWidth = lineWidth;
  196. ctx.beginPath();
  197. ctx.arc(x, y, r, startangle, endangle, anticlockwise);
  198. ctx.stroke();
  199. var sx, sy, lineangle, destx, desty;
  200. if (which & 1) {
  201. sx = Math.cos(startangle) * r + x;
  202. sy = Math.sin(startangle) * r + y;
  203. lineangle = Math.atan2(x - sx, sy - y);
  204. if (anticlockwise) {
  205. destx = sx + 10 * Math.cos(lineangle);
  206. desty = sy + 10 * Math.sin(lineangle);
  207. } else {
  208. destx = sx - 10 * Math.cos(lineangle);
  209. desty = sy - 10 * Math.sin(lineangle);
  210. }
  211. drawArrow(ctx, sx, sy, destx, desty, style, 2, angle, d);
  212. }
  213. if (which & 2) {
  214. sx = Math.cos(endangle) * r + x;
  215. sy = Math.sin(endangle) * r + y;
  216. lineangle = Math.atan2(x - sx, sy - y);
  217. if (anticlockwise) {
  218. destx = sx - 10 * Math.cos(lineangle);
  219. desty = sy - 10 * Math.sin(lineangle);
  220. } else {
  221. destx = sx + 10 * Math.cos(lineangle);
  222. desty = sy + 10 * Math.sin(lineangle);
  223. }
  224. drawArrow(ctx, sx - lineRatio * Math.sin(endangle), sy + lineRatio * Math.cos(endangle), destx - lineRatio * Math.sin(endangle), desty + lineRatio * Math.cos(endangle), style, 2, angle, d)
  225. }
  226. ctx.restore();
  227. }
  228. var drawArrow = function(ctx, x1, y1, x2, y2, style, which, angle, d) {
  229. 'use strict';
  230. if (typeof(x1) == 'string') x1 = parseInt(x1);
  231. if (typeof(y1) == 'string') y1 = parseInt(y1);
  232. if (typeof(x2) == 'string') x2 = parseInt(x2);
  233. if (typeof(y2) == 'string') y2 = parseInt(y2);
  234. style = typeof(style) != 'undefined' ? style : 3;
  235. which = typeof(which) != 'undefined' ? which : 1;
  236. angle = typeof(angle) != 'undefined' ? angle : Math.PI / 8;
  237. d = typeof(d) != 'undefined' ? d : 10;
  238. var toDrawHead = typeof(style) != 'function' ? drawHead : style;
  239. var dist = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  240. var ratio = (dist - d / 3) / dist;
  241. var tox, toy, fromx, fromy;
  242. if (which & 1) {
  243. tox = Math.round(x1 + (x2 - x1) * ratio);
  244. toy = Math.round(y1 + (y2 - y1) * ratio);
  245. } else {
  246. tox = x2;
  247. toy = y2;
  248. }
  249. if (which & 2) {
  250. fromx = x1 + (x2 - x1) * (1 - ratio);
  251. fromy = y1 + (y2 - y1) * (1 - ratio);
  252. } else {
  253. fromx = x1;
  254. fromy = y1;
  255. }
  256. ctx.beginPath();
  257. ctx.moveTo(fromx, fromy);
  258. ctx.lineTo(tox, toy);
  259. ctx.stroke();
  260. var lineangle = Math.atan2(y2 - y1, x2 - x1);
  261. var h = Math.abs(d / Math.cos(angle));
  262. if (which & 1) {
  263. var angle1 = lineangle + Math.PI + angle;
  264. var topx = x2 + Math.cos(angle1) * h;
  265. var topy = y2 + Math.sin(angle1) * h;
  266. var angle2 = lineangle + Math.PI - angle;
  267. var botx = x2 + Math.cos(angle2) * h;
  268. var boty = y2 + Math.sin(angle2) * h;
  269. toDrawHead(ctx, topx, topy, x2, y2, botx, boty, style);
  270. }
  271. if (which & 2) {
  272. var angle1 = lineangle + angle;
  273. var topx = x1 + Math.cos(angle1) * h;
  274. var topy = y1 + Math.sin(angle1) * h;
  275. var angle2 = lineangle - angle;
  276. var botx = x1 + Math.cos(angle2) * h;
  277. var boty = y1 + Math.sin(angle2) * h;
  278. toDrawHead(ctx, topx, topy, x1, y1, botx, boty, style);
  279. }
  280. };
  281. var spinColors = function(currentIteration, totalIterations) {
  282. var step = currentIteration % totalIterations;
  283. if (step < oldStep)
  284. colors.push(colors.shift());
  285. var c0 = colors[0],
  286. c1 = colors[1],
  287. r = minmax(easeLinear(step, c0.r, c1.r - c0.r, totalIterations), c0.r, c1.r),
  288. g = minmax(easeLinear(step, c0.g, c1.g - c0.g, totalIterations), c0.g, c1.g),
  289. b = minmax(easeLinear(step, c0.b, c1.b - c0.b, totalIterations), c0.b, c1.b);
  290. oldStep = step;
  291. return "rgb(" + parseInt(r) + "," + parseInt(g) + "," + parseInt(b) + ")";
  292. }
  293. var spin = function(t) {
  294. var timeCurrent = t || (new Date).getTime();
  295. if (!startTime) {
  296. startTime = timeCurrent;
  297. }
  298. tick = timeCurrent - startTime;
  299. acc = easeInOutQuad((tick + tail_duration / 2) % tail_duration, 0, duration, tail_duration);
  300. head = easeLinear((tick + acc) % duration, 0, 360, duration);
  301. tail = 20 + Math.abs(easeLinear((tick + tail_duration / 2) % tail_duration, -300, 600, tail_duration));
  302. ctx.lineWidth = lineWidth;
  303. ctx.lineCap = "round";
  304. ctx.strokeStyle = spinColors(tick, duration);
  305. ctx.clearRect(0, 0, size, size);
  306. //fixed android 4.1.x
  307. canvasObj.style.display = 'none'; // Detach from DOM
  308. canvasObj.offsetHeight; // Force the detach
  309. canvasObj.style.display = 'inherit'; // Reattach to DOM
  310. ctx.beginPath();
  311. ctx.arc(size / 2, size / 2, size / 4, parseInt(head - tail) % 360 * rad, parseInt(head) % 360 * rad, false);
  312. ctx.stroke();
  313. rAF = requestAnimationFrame(spin);
  314. };
  315. var startSpin = function() {
  316. startTime = 0;
  317. oldStep = 0;
  318. rAF = requestAnimationFrame(spin);
  319. };
  320. var stopSpin = function() {
  321. rAF && cancelAnimationFrame(rAF);
  322. }
  323. var init = function(canvas, options) {
  324. canvasObj = canvas;
  325. ctx = canvasObj.getContext('2d');
  326. var options = $.extend(true, {}, options);
  327. colors = options.colors;
  328. duration = options.duration;
  329. tail_duration = options.tail_duration;
  330. size = options.size;
  331. lineWidth = options.lineWidth;
  332. };
  333. return {
  334. init: init,
  335. drawArcedArrow: drawArcedArrow,
  336. startSpin: startSpin,
  337. stopSpin: stopSpin
  338. };
  339. })()
  340. });
  341. })(mui);