index.html 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>BiCiMon</title>
  6. <link rel="shortcut icon" type="image/png" href="images/logo.png">
  7. <script src="https://unpkg.com/alpinejs@3.9.5/dist/cdn.min.js" defer
  8. integrity="sha384-uvrjP84ugVTfHtrnvF3/rM9lWkcO9x7yPo/Pnr9JjHbWjj6dbv5aL1Wmsu6HadlI"
  9. crossorigin="anonymous"></script>
  10. <script src="https://unpkg.com/nosleep.js@0.12.0/dist/NoSleep.min.js" defer
  11. integrity="sha384-lp0XiAMtqqqjyVLBYjAcDQzxc2XyGj3sGESoiWbccHdpXjNUPUjWrq5GMqaGHGp9"
  12. crossorigin="anonymous"></script>
  13. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css"
  14. integrity="sha384-IJLmUY0f1ePPX6uSCJ9Bxik64/meJmjSYD7dHaJqTXXEBE4y+Oe9P2KBZa/z7p0Q" crossorigin="anonymous">
  15. <meta name="viewport" content="width=device-width, initial-scale=1">
  16. <link rel="manifest" href="./manifest.json">
  17. <meta name="mobile-web-app-capable" content="yes"/>
  18. <meta name="mobile-web-app-status-bar-style" content="black"/>
  19. <meta name="mobile-web-app-title" content="BiCiMon">
  20. <meta name="theme-color" content="#2A3443"/>
  21. <meta name="description" content="Bike Speedometer">
  22. <link rel="stylesheet" href="./index.css">
  23. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  24. </head>
  25. <body>
  26. <div class="container" x-data="main">
  27. <div class="notification is-danger m-1" x-show="error" x-transition>
  28. <span x-text="error"></span>
  29. <button class="delete" @click="error = ''"></button>
  30. </div>
  31. <nav class="tabs is-boxed pb-3 mb-0" style="top: 0; padding-top: 3px; position: sticky; background: white">
  32. <ul>
  33. <li x-bind:class="page === 'dashboard' ? 'is-active' : ''" style="margin-left: 1rem"
  34. @click="page = 'dashboard'">
  35. <a>Dashboard</a>
  36. </li>
  37. <li x-bind:class="page === 'course' ? 'is-active' : ''" @click="page = 'course'">
  38. <a>Course</a>
  39. </li>
  40. <li x-bind:class="page === 'settings' ? 'is-active' : ''" @click="page = 'settings'">
  41. <a>🔧</a>
  42. </li>
  43. <li style="margin-left: auto; margin-right: 1rem" x-show="heading">
  44. <span>🧭</span> <span x-text="fmtHeading()"></span>
  45. </li>
  46. </ul>
  47. </nav>
  48. <div x-show="page === 'dashboard'" x-transition>
  49. <template x-if="!time.update">
  50. <div>
  51. <div class="has-text-centered">Awaiting geolocation ...</div>
  52. <progress class="progress is-small is-dark" max="100"></progress>
  53. </div>
  54. </template>
  55. <div class="has-text-centered">
  56. <div>
  57. <div class="title" style="font-size: 15rem; color: black; margin-bottom: 0; line-height: .8"
  58. x-text="fmtCurrentSpeed()"></div>
  59. <div class="heading" style="font-weight: bold">
  60. km/h <span style="font-weight: normal; text-transform: initial"
  61. x-show="time.updateDelay !== null">&mdash;
  62. <span x-text="fmtUpdateDelay()"></span></span>
  63. </div>
  64. </div>
  65. </div>
  66. <div class="level is-mobile dark-bg">
  67. <div class="level-item has-text-centered">
  68. <div>
  69. <p class="heading">🏔 Altitude</p>
  70. <p class="title">
  71. <span x-text="round(altitude.current)"></span><small>±<span
  72. x-text="round(altitude.accuracy)"></span>m</small>
  73. </p>
  74. </div>
  75. </div>
  76. <div class="level-item has-text-centered">
  77. <div>
  78. <p class="heading">📍 Accuracy</p>
  79. <p class="title">
  80. <small>±</small><span x-text="round(positionAccuracy)"></span><small>m</small>
  81. </p>
  82. </div>
  83. </div>
  84. </div>
  85. <div class="level is-mobile">
  86. <div class="level-item has-text-centered">
  87. <div>
  88. <p class="heading">🚴 Max</p>
  89. <p class="title" x-text="fmtMaxSpeed()"></p>
  90. <p class="heading">km/h</p>
  91. </div>
  92. </div>
  93. <div class="level-item has-text-centered">
  94. <div>
  95. <p class="heading">Ø Avg</p>
  96. <p class="title" x-text="fmtAvgSpeed()"></p>
  97. <p class="heading">km/h</p>
  98. </div>
  99. </div>
  100. <div class="level-item has-text-centered">
  101. <div>
  102. <p class="heading">⌛ Avg-Wait</p>
  103. <p class="title" x-text="fmtAvgNoWaitSpeed()"></p>
  104. <p class="heading">km/h</p>
  105. </div>
  106. </div>
  107. </div>
  108. <div class="level is-mobile dark-bg">
  109. <div class="level-item has-text-centered">
  110. <div>
  111. <p class="heading">⌚ Start</p>
  112. <p class="title" x-text="fmtStartTime()"></p>
  113. </div>
  114. </div>
  115. <div class="level-item has-text-centered">
  116. <div>
  117. <p class="heading">⏱ Trip</p>
  118. <p class="title" x-text="fmtTripTime()"></p>
  119. </div>
  120. </div>
  121. <div class="level-item has-text-centered">
  122. <div>
  123. <p class="heading">⌛ Wait</p>
  124. <p class="title" x-text="fmtWaitTime()"></p>
  125. </div>
  126. </div>
  127. </div>
  128. </div>
  129. <template x-if="page === 'course'">
  130. <div class="mx-1 mb-5">
  131. <div class="is-flex is-justify-content-space-between px-2 scale">
  132. <span>0
  133. <math class="formula">
  134. <mfrac>
  135. <mn>km</mn>
  136. <mn>h</mn>
  137. </mfrac>
  138. </math>
  139. </span>
  140. <span>
  141. <span x-text="fmtMaxSpeed()"></span>
  142. <math class="formula">
  143. <mfrac>
  144. <mn>km</mn>
  145. <mn>h</mn>
  146. </mfrac>
  147. </math>
  148. </span>
  149. </div>
  150. <template x-for="(sp, idx) in speed.timeseries.values">
  151. <div>
  152. <div
  153. :class="{'has-background-danger': sp < speed.minRide, 'has-background-dark': sp >= speed.minRide}"
  154. :style="{width: (sp * 100 / speed.max) + '%', height: clamp(1000 / speed.timeseries.values.length, 1, 10) + 'px'}">
  155. </div>
  156. </div>
  157. </template>
  158. </div>
  159. </template>
  160. <div x-show="page === 'settings'" x-transition>
  161. <section class="hero is-link" style="position: relative">
  162. <div class="hero-body">
  163. <p class="title">
  164. BiCiMon
  165. </p>
  166. <p class="subtitle">
  167. Bike Speedometer
  168. </p>
  169. </div>
  170. <a href="https://github.com/knrdl/bicimon#readme" target="_blank" rel="noopener noreferrer"
  171. aria-label="View source on GitHub" style="position: absolute; right:0; top: 0">
  172. <svg width="80" height="80" viewBox="0 0 250 250"
  173. style="fill:#151513; color:#fff;"
  174. aria-hidden="true">
  175. <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
  176. <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
  177. fill="currentColor" style="transform-origin: 130px 106px;"></path>
  178. <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
  179. fill="currentColor"></path>
  180. </svg>
  181. </a>
  182. </section>
  183. <div class="is-flex is-justify-content-center mt-5">
  184. <button @click="toggleNoSleep" class="is-rounded button"
  185. :class="{'is-outlined': !noSleep, 'is-success': noSleep, 'is-info': !noSleep}">
  186. <span x-show="noSleep" class="is-size-3">✓</span>
  187. Try to keep screen on
  188. </button>
  189. </div>
  190. </div>
  191. </div>
  192. <script src="./index.js"></script>
  193. </body>
  194. </html>