ax.carousel.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. /**
  2. * jq.web.axCarousel - a axCarousel library for html5 mobile apps
  3. * @copyright AppMobi 2011 - AppMobi
  4. * ħ·½ÍŶÓ×é
  5. * 14-01-02
  6. */
  7. (function ($) {
  8. var cache = [];
  9. var objId = function (obj) {
  10. if (!obj.jqmaxCarouselId) obj.jqmaxCarouselId = $.uuid();
  11. return obj.jqmaxCarouselId;
  12. }
  13. $.fn.axCarousel = function (opts) {
  14. var tmp, id;
  15. for (var i = 0; i < this.length; i++) {
  16. //cache system
  17. id = objId(this[i]);
  18. if (!cache[id]) {
  19. tmp = new axCarousel(this[i], opts);
  20. cache[id] = tmp;
  21. } else {
  22. tmp = cache[id];
  23. }
  24. }
  25. return this.length == 1 ? tmp : this;
  26. };
  27. var axCarousel = (function () {
  28. var translateOpen = $.feat.cssTransformStart;
  29. var translateClose = $.feat.cssTransformEnd;
  30. var axCarousel = function (containerEl, opts) {
  31. if (typeof containerEl === "string" || containerEl instanceof String) {
  32. this.container = document.getElementById(containerEl);
  33. } else {
  34. this.container = containerEl;
  35. }
  36. if (!this.container) {
  37. alert("Error finding container for axCarousel " + containerEl);
  38. return;
  39. }
  40. if (this instanceof axCarousel) {
  41. for (var j in opts) {
  42. if (opts.hasOwnProperty(j)) {
  43. this[j] = opts[j];
  44. }
  45. }
  46. } else {
  47. return new axCarousel(containerEl, opts);
  48. }
  49. var that = this;
  50. jq(this.container).bind('destroy', function () {
  51. var id = that.container.jqmaxCarouselId;
  52. //window event need to be cleaned up manually, remaining binds are automatically killed in the dom cleanup process
  53. window.removeEventListener("orientationchange", that.orientationHandler, false);
  54. if (cache[id]) delete cache[id];
  55. });
  56. this.pagingDiv = this.pagingDiv ? document.getElementById(this.pagingDiv) : null;
  57. // initial setup
  58. this.container.style.overflow = "hidden";
  59. if (this.vertical) {
  60. this.horizontal = false;
  61. }
  62. var el = document.createElement("div");
  63. this.container.appendChild(el);
  64. var $el = $(el);
  65. var $container = $(this.container);
  66. var data = Array.prototype.slice.call(this.container.childNodes);
  67. while (data.length > 0) {
  68. var myEl = data.splice(0, 1);
  69. myEl = $container.find(myEl)
  70. if (myEl.get() == el)
  71. continue;
  72. $el.append(myEl.get());
  73. }
  74. if (this.horizontal) {
  75. el.style.display = "block";
  76. el.style['float'] = "left";
  77. }
  78. else {
  79. el.style.display = "block";
  80. }
  81. this.el = el;
  82. this.refreshItems();
  83. var jqEl = jq(el);
  84. jqEl.bind('touchmove', function (e) { that.touchMove(e); });
  85. jqEl.bind('touchend', function (e) { that.touchEnd(e); });
  86. jqEl.bind('touchstart', function (e) {
  87. that.touchStart(e);
  88. //e.stopPropagation();
  89. });
  90. this.orientationHandler = function () { that.onMoveIndex(that.axCarouselIndex, 0); };
  91. window.addEventListener("orientationchange", this.orientationHandler, false);
  92. };
  93. axCarousel.prototype = {
  94. startX: 0,
  95. startY: 0,
  96. dx: 0,
  97. dy: 0,
  98. myDivWidth: 0,
  99. myDivHeight: 0,
  100. cssMoveStart: 0,
  101. childrenCount: 0,
  102. axCarouselIndex: 0,
  103. vertical: false,
  104. horizontal: true,
  105. lockScroll: false,
  106. autoScrollTime: 10000,
  107. el: null,
  108. movingElement: false,
  109. container: null,
  110. pagingDiv: null,
  111. pagingCssName: "axCarousel_paging",
  112. pagingCssNameSelected: "axCarousel_paging_selected",
  113. pagingFunction: null,
  114. lockMove: false,
  115. pagingDivText: null,
  116. // handle the moving function
  117. touchStart: function (e) {
  118. this.myDivWidth = numOnly(this.container.clientWidth);
  119. this.myDivHeight = numOnly(this.container.clientHeight);
  120. this.lockMove = false;
  121. if (e.touches[0].target && e.touches[0].target.type !== undefined) {
  122. var tagname = e.touches[0].target.tagName.toLowerCase();
  123. if (tagname === "select" || tagname === "input" || tagname === "button") // stuff we need to allow
  124. {
  125. return;
  126. }
  127. }
  128. if (e.touches.length === 1) {
  129. this.movingElement = true;
  130. this.startY = e.touches[0].pageY;
  131. this.startX = e.touches[0].pageX;
  132. var cssMatrix = $.getCssMatrix(this.el);
  133. if (this.vertical) {
  134. try {
  135. this.cssMoveStart = numOnly(cssMatrix.f);
  136. } catch (ex1) {
  137. this.cssMoveStart = 0;
  138. }
  139. } else {
  140. try {
  141. this.cssMoveStart = numOnly(cssMatrix.e);
  142. } catch (ex1) {
  143. this.cssMoveStart = 0;
  144. }
  145. }
  146. }
  147. },
  148. touchMove: function (e) {
  149. if (!this.movingElement)
  150. return;
  151. if (e.touches.length > 1) {
  152. return this.touchEnd(e);
  153. }
  154. var rawDelta = {
  155. x: e.touches[0].pageX - this.startX,
  156. y: e.touches[0].pageY - this.startY
  157. };
  158. if (this.vertical) {
  159. var movePos = { x: 0, y: 0 };
  160. this.dy = e.touches[0].pageY - this.startY;
  161. this.dy += this.cssMoveStart;
  162. movePos.y = this.dy;
  163. e.preventDefault();
  164. if (this.pagingDivText == null) {
  165. e.stopPropagation();
  166. }
  167. } else {
  168. if (!this.lockMove && isHorizontalSwipe(rawDelta.x, rawDelta.y)) {
  169. var movePos = { x: 0, y: 0 };
  170. this.dx = e.touches[0].pageX - this.startX;
  171. this.dx += this.cssMoveStart;
  172. e.preventDefault();
  173. if (this.pagingDivText == null) {
  174. e.stopPropagation();
  175. }
  176. movePos.x = this.dx;
  177. }
  178. else
  179. return this.lockMove = true;
  180. }
  181. var totalMoved = this.vertical ? ((this.dy % this.myDivHeight) / this.myDivHeight * 100) * -1 : ((this.dx % this.myDivWidth) / this.myDivWidth * 100) * -1; // get a percentage of movement.
  182. if (movePos)
  183. this.moveCSS3(this.el, movePos);
  184. },
  185. touchEnd: function (e) {
  186. if (!this.movingElement) {
  187. return;
  188. }
  189. //e.preventDefault();
  190. //e.stopPropagation();
  191. var runFinal = false;
  192. try {
  193. var cssMatrix = $.getCssMatrix(this.el);
  194. var endPos = this.vertical ? numOnly(cssMatrix.f) : numOnly(cssMatrix.e);
  195. if (endPos > 0) {
  196. this.moveCSS3(this.el, {
  197. x: 0,
  198. y: 0
  199. }, "300");
  200. } else {
  201. var totalMoved = this.vertical ? ((this.dy % this.myDivHeight) / this.myDivHeight * 100) * -1 : ((this.dx % this.myDivWidth) / this.myDivWidth * 100) * -1; // get a percentage of movement.
  202. // Only need
  203. // to drag 3% to trigger an event
  204. var currInd = this.axCarouselIndex;
  205. if (endPos < this.cssMoveStart && totalMoved > 3) {
  206. currInd++; // move right/down
  207. } else if ((endPos > this.cssMoveStart && totalMoved < 97)) {
  208. currInd--; // move left/up
  209. }
  210. if (currInd > (this.childrenCount - 1)) {
  211. currInd = this.childrenCount - 1;
  212. }
  213. if (currInd < 0) {
  214. currInd = 0;
  215. }
  216. var movePos = {
  217. x: 0,
  218. y: 0
  219. };
  220. if (this.vertical) {
  221. movePos.y = (currInd * this.myDivHeight * -1);
  222. }
  223. else {
  224. movePos.x = (currInd * this.myDivWidth * -1);
  225. }
  226. this.moveCSS3(this.el, movePos, "150");
  227. if (this.pagingDiv && this.axCarouselIndex !== currInd) {
  228. document.getElementById(this.container.id + "_" + this.axCarouselIndex).className = this.pagingCssName;
  229. document.getElementById(this.container.id + "_" + currInd).className = this.pagingCssNameSelected;
  230. }
  231. if (this.axCarouselIndex != currInd)
  232. runFinal = true;
  233. this.axCarouselIndex = currInd;
  234. //this.autoScroll(currInd);
  235. }
  236. } catch (e) {
  237. console.log(e);
  238. }
  239. this.dx = 0;
  240. this.movingElement = false;
  241. this.startX = 0;
  242. this.dy = 0;
  243. this.startY = 0;
  244. if (runFinal && this.pagingFunction && typeof this.pagingFunction == "function")
  245. this.pagingFunction(this.axCarouselIndex);
  246. },
  247. onMoveIndex: function (newInd, transitionTime) {
  248. this.myDivWidth = numOnly(this.container.clientWidth);
  249. this.myDivHeight = numOnly(this.container.clientHeight);
  250. var runFinal = false;
  251. if (document.getElementById(this.container.id + "_" + this.axCarouselIndex))
  252. document.getElementById(this.container.id + "_" + this.axCarouselIndex).className = this.pagingCssName;
  253. var newTime = Math.abs(newInd - this.axCarouselIndex);
  254. var ind = newInd;
  255. if (ind < 0)
  256. ind = 0;
  257. if (ind > this.childrenCount - 1) {
  258. ind = this.childrenCount - 1;
  259. }
  260. var movePos = {
  261. x: 0,
  262. y: 0
  263. };
  264. if (this.vertical) {
  265. movePos.y = (ind * this.myDivHeight * -1);
  266. }
  267. else {
  268. movePos.x = (ind * this.myDivWidth * -1);
  269. }
  270. var time = transitionTime ? transitionTime : 50 + parseInt((newTime * 20));
  271. this.moveCSS3(this.el, movePos, time);
  272. if (this.axCarouselIndex != ind)
  273. runFinal = true;
  274. this.axCarouselIndex = ind;
  275. if (this.pagingDiv) {
  276. var tmpEl = document.getElementById(this.container.id + "_" + this.axCarouselIndex);
  277. if (tmpEl) tmpEl.className = this.pagingCssNameSelected;
  278. }
  279. if (runFinal && this.pagingFunction && typeof this.pagingFunction == "function")
  280. this.pagingFunction(currInd);
  281. },
  282. moveCSS3: function (el, distanceToMove, time, timingFunction) {
  283. if (!time)
  284. time = 0;
  285. else
  286. time = parseInt(time);
  287. if (!timingFunction)
  288. timingFunction = "linear";
  289. el.style[$.feat.cssPrefix + "Transform"] = "translate" + translateOpen + distanceToMove.x + "px," + distanceToMove.y + "px" + translateClose;
  290. el.style[$.feat.cssPrefix + "TransitionDuration"] = time + "ms";
  291. el.style[$.feat.cssPrefix + "BackfaceVisibility"] = "hidden";
  292. el.style[$.feat.cssPrefix + "TransformStyle"] = "preserve-3d";
  293. el.style[$.feat.cssPrefix + "TransitionTimingFunction"] = timingFunction;
  294. },
  295. addItem: function (el) {
  296. if (el && el.nodeType) {
  297. this.container.childNodes[0].appendChild(el);
  298. this.refreshItems();
  299. }
  300. },
  301. refreshItems: function () {
  302. var childrenCounter = 0;
  303. var that = this;
  304. var el = this.el;
  305. n = el.childNodes[0];
  306. var widthParam;
  307. var heightParam = "100%";
  308. var elems = [];
  309. for (; n; n = n.nextSibling) {
  310. if (n.nodeType === 1) {
  311. elems.push(n);
  312. childrenCounter++;
  313. }
  314. }
  315. var param = (100 / childrenCounter) + "%";
  316. this.childrenCount = childrenCounter;
  317. widthParam = parseFloat(100 / this.childrenCount) + "%";
  318. for (var i = 0; i < elems.length; i++) {
  319. if (this.horizontal) {
  320. elems[i].style.width = widthParam;
  321. elems[i].style.height = "100%";
  322. elems[i].style['float'] = "left";
  323. }
  324. else {
  325. elems[i].style.height = widthParam;
  326. elems[i].style.width = "100%";
  327. elems[i].style.display = "block";
  328. }
  329. }
  330. this.moveCSS3(el, {
  331. x: 0,
  332. y: 0
  333. });
  334. if (this.horizontal) {
  335. el.style.width = Math.ceil((this.childrenCount) * 100) + "%";
  336. el.style.height = "100%";
  337. el.style['min-height'] = "100%"
  338. }
  339. else {
  340. el.style.width = "100%";
  341. el.style.height = Math.ceil((this.childrenCount) * 100) + "%";
  342. el.style['min-height'] = Math.ceil((this.childrenCount) * 100) + "%";
  343. }
  344. // Create the paging dots
  345. if (this.pagingDivText == null) {
  346. if (this.pagingDiv) {
  347. this.pagingDiv.innerHTML = ""
  348. for (i = 0; i < this.childrenCount; i++) {
  349. var pagingEl = document.createElement("div");
  350. pagingEl.id = this.container.id + "_" + i;
  351. pagingEl.pageId = i;
  352. if (i !== this.axCarouselIndex) {
  353. pagingEl.className = this.pagingCssName;
  354. }
  355. else {
  356. pagingEl.className = this.pagingCssNameSelected;
  357. }
  358. pagingEl.onclick = function () {
  359. that.onMoveIndex(this.pageId);
  360. };
  361. var spacerEl = document.createElement("div");
  362. spacerEl.style.width = "20px";
  363. if (this.horizontal) {
  364. spacerEl.style.cssFloat = "left";
  365. spacerEl.innerHTML = "&nbsp;";
  366. }
  367. else {
  368. spacerEl.innerHTML = "&nbsp;";
  369. spacerEl.style.display = "block";
  370. }
  371. this.pagingDiv.appendChild(pagingEl);
  372. if (i + 1 < (this.childrenCount))
  373. this.pagingDiv.appendChild(spacerEl);
  374. pagingEl = null;
  375. spacerEl = null;
  376. }
  377. this.pagingDiv.style.width = (this.childrenCount) * 50 + "px";
  378. this.pagingDiv.style.height = "25px";
  379. }
  380. } else {
  381. if (this.pagingDiv) {
  382. this.pagingDiv.innerHTML = ""
  383. for (i = 0; i < this.childrenCount; i++) {
  384. var pagingEl = document.createElement("div");
  385. pagingEl.id = this.container.id + "_" + i;
  386. pagingEl.pageId = i;
  387. if (i !== this.carouselTabIndex) {
  388. pagingEl.className = this.pagingCssName;
  389. }
  390. else {
  391. pagingEl.className = this.pagingCssNameSelected;
  392. }
  393. pagingEl.onclick = function () {
  394. that.onMoveIndex(this.pageId);
  395. };
  396. pagingEl.innerHTML = this.pagingDivText[i];
  397. this.pagingDiv.appendChild(pagingEl);
  398. pagingEl = null;
  399. }
  400. }
  401. };
  402. this.onMoveIndex(this.axCarouselIndex);
  403. if (this.lockScroll) {
  404. this.autoScroll(this.axCarouselIndex);
  405. };
  406. },
  407. autoScroll: function (e) {
  408. var that = this;
  409. window.setInterval(function () {
  410. if (that.axCarouselIndex < (that.childrenCount - 1)) {
  411. that.onMoveIndex(that.axCarouselIndex + 1);
  412. } else {
  413. that.onMoveIndex(e);
  414. };
  415. }, that.autoScrollTime);
  416. }
  417. };
  418. return axCarousel;
  419. })();
  420. function isHorizontalSwipe(xAxis, yAxis) {
  421. var X = xAxis;
  422. var Y = yAxis;
  423. var Z = Math.round(Math.sqrt(Math.pow(X, 2) + Math.pow(Y, 2))); //the distance - rounded - in pixels
  424. var r = Math.atan2(Y, X); //angle in radians
  425. var swipeAngle = Math.round(r * 180 / Math.PI); //angle in degrees
  426. if (swipeAngle < 0) { swipeAngle = 360 - Math.abs(swipeAngle); } // for negative degree values
  427. if (((swipeAngle <= 215) && (swipeAngle >= 155)) || ((swipeAngle <= 45) && (swipeAngle >= 0)) || ((swipeAngle <= 360) && (swipeAngle >= 315))) // horizontal angles with threshold
  428. { return true; }
  429. else { return false }
  430. }
  431. })(jq);