touch_handler.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. class TouchHandler {
  2. constructor() {
  3. this.reset();
  4. }
  5. reset() {
  6. this.touch = {
  7. start: { x: -1, y: -1 },
  8. move: { x: -1, y: -1 },
  9. moved: false,
  10. time: 0,
  11. element: null
  12. };
  13. }
  14. calculateDistance() {
  15. if (this.touch.start.x >= -1 && this.touch.move.x >= -1) {
  16. const horizontalDistance = Math.abs(this.touch.move.x - this.touch.start.x);
  17. const verticalDistance = Math.abs(this.touch.move.y - this.touch.start.y);
  18. if (horizontalDistance > 30 && verticalDistance < 70 || this.touch.moved) {
  19. return this.touch.move.x - this.touch.start.x;
  20. }
  21. }
  22. return 0;
  23. }
  24. findElement(element) {
  25. if (element.classList.contains("entry-swipe")) {
  26. return element;
  27. }
  28. return element.closest(".entry-swipe");
  29. }
  30. onItemTouchStart(event) {
  31. if (event.touches === undefined || event.touches.length !== 1) {
  32. return;
  33. }
  34. this.reset();
  35. this.touch.start.x = event.touches[0].clientX;
  36. this.touch.start.y = event.touches[0].clientY;
  37. this.touch.element = this.findElement(event.touches[0].target);
  38. this.touch.element.style.transitionDuration = "0s";
  39. }
  40. onItemTouchMove(event) {
  41. if (event.touches === undefined || event.touches.length !== 1 || this.element === null) {
  42. return;
  43. }
  44. this.touch.move.x = event.touches[0].clientX;
  45. this.touch.move.y = event.touches[0].clientY;
  46. const distance = this.calculateDistance();
  47. const absDistance = Math.abs(distance);
  48. if (absDistance > 0) {
  49. this.touch.moved = true;
  50. let tx = absDistance > 75 ? Math.pow(absDistance - 75, 0.5) + 75 : absDistance;
  51. if (distance < 0) {
  52. tx = -tx;
  53. }
  54. this.touch.element.style.transform = "translateX(" + tx + "px)";
  55. event.preventDefault();
  56. }
  57. }
  58. onItemTouchEnd(event) {
  59. if (event.touches === undefined) {
  60. return;
  61. }
  62. if (this.touch.element !== null) {
  63. const absDistance = Math.abs(this.calculateDistance());
  64. if (absDistance > 75) {
  65. toggleEntryStatus(this.touch.element);
  66. }
  67. if (this.touch.moved) {
  68. this.touch.element.style.transitionDuration = "0.15s";
  69. this.touch.element.style.transform = "none";
  70. }
  71. }
  72. this.reset();
  73. }
  74. onContentTouchStart(event) {
  75. if (event.touches === undefined || event.touches.length !== 1) {
  76. return;
  77. }
  78. this.reset();
  79. this.touch.start.x = event.touches[0].clientX;
  80. this.touch.start.y = event.touches[0].clientY;
  81. this.touch.time = Date.now();
  82. }
  83. onContentTouchMove(event) {
  84. if (event.touches === undefined || event.touches.length !== 1 || this.element === null) {
  85. return;
  86. }
  87. this.touch.move.x = event.touches[0].clientX;
  88. this.touch.move.y = event.touches[0].clientY;
  89. }
  90. onContentTouchEnd(event) {
  91. if (event.touches === undefined) {
  92. return;
  93. }
  94. const distance = this.calculateDistance();
  95. const absDistance = Math.abs(distance);
  96. const now = Date.now();
  97. if (now - this.touch.time <= 1000 && absDistance > 75) {
  98. if (distance > 0) {
  99. goToPage("previous");
  100. } else {
  101. goToPage("next");
  102. }
  103. }
  104. this.reset();
  105. }
  106. onTapEnd(event) {
  107. if (event.touches === undefined) {
  108. return;
  109. }
  110. const now = Date.now();
  111. if (this.touch.start.x !== -1 && now - this.touch.time <= 200) {
  112. const innerWidthHalf = window.innerWidth / 2;
  113. if (this.touch.start.x >= innerWidthHalf && event.changedTouches[0].clientX >= innerWidthHalf) {
  114. goToPage("next");
  115. } else if (this.touch.start.x < innerWidthHalf && event.changedTouches[0].clientX < innerWidthHalf) {
  116. goToPage("previous");
  117. }
  118. this.reset();
  119. } else {
  120. this.reset();
  121. this.touch.start.x = event.changedTouches[0].clientX;
  122. this.touch.time = now;
  123. }
  124. }
  125. listen() {
  126. const hasPassiveOption = DomHelper.hasPassiveEventListenerOption();
  127. document.querySelectorAll(".entry-swipe").forEach((element) => {
  128. element.addEventListener("touchstart", (e) => this.onItemTouchStart(e), hasPassiveOption ? { passive: true } : false);
  129. element.addEventListener("touchmove", (e) => this.onItemTouchMove(e), hasPassiveOption ? { passive: false } : false);
  130. element.addEventListener("touchend", (e) => this.onItemTouchEnd(e), hasPassiveOption ? { passive: true } : false);
  131. element.addEventListener("touchcancel", () => this.reset(), hasPassiveOption ? { passive: true } : false);
  132. });
  133. const element = document.querySelector(".entry-content");
  134. if (element) {
  135. if (element.classList.contains("gesture-nav-tap")) {
  136. element.addEventListener("touchend", (e) => this.onTapEnd(e), hasPassiveOption ? { passive: true } : false);
  137. element.addEventListener("touchmove", () => this.reset(), hasPassiveOption ? { passive: true } : false);
  138. element.addEventListener("touchcancel", () => this.reset(), hasPassiveOption ? { passive: true } : false);
  139. } else if (element.classList.contains("gesture-nav-swipe")) {
  140. element.addEventListener("touchstart", (e) => this.onContentTouchStart(e), hasPassiveOption ? { passive: true } : false);
  141. element.addEventListener("touchmove", (e) => this.onContentTouchMove(e), hasPassiveOption ? { passive: true } : false);
  142. element.addEventListener("touchend", (e) => this.onContentTouchEnd(e), hasPassiveOption ? { passive: true } : false);
  143. element.addEventListener("touchcancel", () => this.reset(), hasPassiveOption ? { passive: true } : false);
  144. }
  145. }
  146. }
  147. }