touch_handler.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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. element: null
  10. };
  11. }
  12. calculateDistance() {
  13. if (this.touch.start.x >= -1 && this.touch.move.x >= -1) {
  14. let horizontalDistance = Math.abs(this.touch.move.x - this.touch.start.x);
  15. let verticalDistance = Math.abs(this.touch.move.y - this.touch.start.y);
  16. if (horizontalDistance > 30 && verticalDistance < 70) {
  17. return this.touch.move.x - this.touch.start.x;
  18. }
  19. }
  20. return 0;
  21. }
  22. findElement(element) {
  23. if (element.classList.contains("touch-item")) {
  24. return element;
  25. }
  26. return DomHelper.findParent(element, "touch-item");
  27. }
  28. onTouchStart(event) {
  29. if (event.touches === undefined || event.touches.length !== 1) {
  30. return;
  31. }
  32. this.reset();
  33. this.touch.start.x = event.touches[0].clientX;
  34. this.touch.start.y = event.touches[0].clientY;
  35. this.touch.element = this.findElement(event.touches[0].target);
  36. }
  37. onTouchMove(event) {
  38. if (event.touches === undefined || event.touches.length !== 1 || this.element === null) {
  39. return;
  40. }
  41. this.touch.move.x = event.touches[0].clientX;
  42. this.touch.move.y = event.touches[0].clientY;
  43. let distance = this.calculateDistance();
  44. let absDistance = Math.abs(distance);
  45. if (absDistance > 0) {
  46. let opacity = 1 - (absDistance > 75 ? 0.9 : absDistance / 75 * 0.9);
  47. let tx = distance > 75 ? 75 : (distance < -75 ? -75 : distance);
  48. this.touch.element.style.opacity = opacity;
  49. this.touch.element.style.transform = "translateX(" + tx + "px)";
  50. event.preventDefault();
  51. }
  52. }
  53. onTouchEnd(event) {
  54. if (event.touches === undefined) {
  55. return;
  56. }
  57. if (this.touch.element !== null) {
  58. let distance = Math.abs(this.calculateDistance());
  59. if (distance > 75) {
  60. toggleEntryStatus(this.touch.element);
  61. }
  62. // If not on the unread page, undo transform of the dragged element.
  63. if (document.URL.split("/").indexOf("unread") == -1 || distance <= 75) {
  64. this.touch.element.style.opacity = 1;
  65. this.touch.element.style.transform = "none";
  66. }
  67. }
  68. this.reset();
  69. }
  70. listen() {
  71. let elements = document.querySelectorAll(".touch-item");
  72. let hasPassiveOption = DomHelper.hasPassiveEventListenerOption();
  73. elements.forEach((element) => {
  74. element.addEventListener("touchstart", (e) => this.onTouchStart(e), hasPassiveOption ? { passive: true } : false);
  75. element.addEventListener("touchmove", (e) => this.onTouchMove(e), hasPassiveOption ? { passive: false } : false);
  76. element.addEventListener("touchend", (e) => this.onTouchEnd(e), hasPassiveOption ? { passive: true } : false);
  77. element.addEventListener("touchcancel", () => this.reset(), hasPassiveOption ? { passive: true } : false);
  78. });
  79. let entryContentElement = document.querySelector(".entry-content");
  80. if (entryContentElement) {
  81. let doubleTapTimers = {
  82. previous: null,
  83. next: null
  84. };
  85. const detectDoubleTap = (doubleTapTimer, event) => {
  86. const timer = doubleTapTimers[doubleTapTimer];
  87. if (timer === null) {
  88. doubleTapTimers[doubleTapTimer] = setTimeout(() => {
  89. doubleTapTimers[doubleTapTimer] = null;
  90. }, 200);
  91. } else {
  92. event.preventDefault();
  93. goToPage(doubleTapTimer);
  94. }
  95. };
  96. entryContentElement.addEventListener("touchend", (e) => {
  97. if (e.changedTouches[0].clientX >= (entryContentElement.offsetWidth / 2)) {
  98. detectDoubleTap("next", e);
  99. } else {
  100. detectDoubleTap("previous", e);
  101. }
  102. }, hasPassiveOption ? { passive: false } : false);
  103. entryContentElement.addEventListener("touchmove", (e) => {
  104. Object.keys(doubleTapTimers).forEach(timer => doubleTapTimers[timer] = null);
  105. });
  106. }
  107. }
  108. }