heatContainer.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import LineHeatmap from "./lineHeatmap";
  2. import "nouislider";
  3. const template = `<div>
  4. <div class="line-heatmap-controls">
  5. <div id="line-heatmap-slider"></div>
  6. </div>
  7. <div id="line-heatmap-canvas">
  8. <div id="line-heatmap-view"></div>
  9. <div id="line-heatmap-tooltip"></div>
  10. </div>
  11. </div>
  12. `;
  13. const cursorDist = 10;
  14. const stateObject = {};
  15. let x1, y1, x2, y2;
  16. let firstClick = true;
  17. let div = null;
  18. let posLeft;
  19. let posTop;
  20. let yBottom;
  21. let xBottom;
  22. function init (elementId, logString, useTooltip = false, config = {}) {
  23. const el = document.getElementById(elementId);
  24. el.innerHTML = template;
  25. const logData = logTransform(logString);
  26. // create container
  27. const heatmap = new LineHeatmap('line-heatmap-view');
  28. heatmap.create(config);
  29. heatmap.setTrackData(logData);
  30. const html5Slider = document.getElementById('line-heatmap-slider');
  31. noUiSlider.create(html5Slider, {
  32. start: [1, logData.length],
  33. connect: true,
  34. step: 1,
  35. margin: 1,
  36. range: {
  37. 'min': 1,
  38. 'max': logData.length
  39. }
  40. });
  41. stateObject['container'] = el;
  42. stateObject['heatmap'] = heatmap;
  43. stateObject['data'] = logData;
  44. stateObject['slider'] = html5Slider;
  45. if (useTooltip) {
  46. registerClickEvent();
  47. const canvasWrapper = document.getElementById('line-heatmap-canvas');
  48. const tooltip = document.getElementById('line-heatmap-tooltip');
  49. function updateTooltip (x, y, value) {
  50. const transl = `translate(${x + cursorDist}px, ${y + cursorDist}px)`;
  51. tooltip.style.webkitTransform = transl;
  52. tooltip.innerHTML = `(${x},${y},${value})`;
  53. };
  54. canvasWrapper.onmousemove = function (ev) {
  55. var x = ev.layerX;
  56. var y = ev.layerY;
  57. // getValueAt gives us the value for a point p(x/y)
  58. var value = stateObject['heatmap'].instance.getValueAt({
  59. x: x,
  60. y: y
  61. });
  62. tooltip.style.display = 'block';
  63. updateTooltip(x, y, value);
  64. };
  65. // hide tooltip on mouseout
  66. canvasWrapper.onmouseout = function() {
  67. tooltip.style.display = 'none';
  68. };
  69. /* tooltip code end */
  70. }
  71. html5Slider.noUiSlider.on('update', function (values, handle) {
  72. const logValues = this.get();
  73. const newData = stateObject['data'].slice(+logValues[0]-1, +logValues[1] + 1);
  74. stateObject['heatmap'].setTrackData(newData);
  75. if(div !== null)
  76. updateDivText(div, posLeft, posTop, xBottom, yBottom);
  77. });
  78. }
  79. function updateLogString (logString) {
  80. const logData = logTransform(logString);
  81. stateObject['data'] = logData;
  82. stateObject['slider'].noUiSlider.updateOptions({
  83. range: {
  84. 'min': 1,
  85. 'max': logData.length
  86. }
  87. });
  88. stateObject['heatmap'].setTrackData(logData);
  89. }
  90. function countClicks (x1, y1, x2, y2) {
  91. const instance = stateObject['heatmap'].instance;
  92. const points = instance.getData().data;
  93. let counter = 0;
  94. for(let i = 0; i < points.length; ++i) {
  95. const point = points[i];
  96. if (point.x >= x1 && point.y >= y1) {
  97. if (point.x <= x2 && point.y <= y2) {
  98. counter += point.value;
  99. }
  100. }
  101. }
  102. return counter;
  103. }
  104. function getClicksInWindow (x1, y1, x2, y2) {
  105. const instance = stateObject['heatmap'].instance;
  106. const points = instance.getData().data;
  107. const list = [];
  108. for(let i = 0; i < points.length; ++i) {
  109. const point = points[i];
  110. if (point.x >= x1 && point.y >= y1) {
  111. if (point.x <= x2 && point.y <= y2) {
  112. list.push(point);
  113. }
  114. }
  115. }
  116. return list;
  117. }
  118. function totalClicks () {
  119. return stateObject['data'].length;
  120. }
  121. function getData () {
  122. return stateObject['data'];
  123. }
  124. function logTransform (logString) {
  125. const data = logString.split("\n")
  126. .filter(v => v.split(',').length > 3)
  127. .map(v => {
  128. const o = JSON.parse('[' + v + ']');
  129. return {
  130. x: o[0],
  131. y: o[1],
  132. value: 1
  133. };
  134. });
  135. return data;
  136. }
  137. function registerClickEvent () {
  138. const elemen = document.querySelector(".heatmap-canvas");
  139. window.onclick = function (evt) {
  140. if(evt.target !== elemen) {
  141. return;
  142. }
  143. if (firstClick) {
  144. if (evt.pageX || evt.pageY) {
  145. x1 = evt.pageX;
  146. y1 = evt.pageY;
  147. } else {
  148. x1 = evt.clientX + document.body.scrollLeft + elemen.scrollLeft;
  149. y1 = evt.clientY + document.body.scrollTop + elemen.scrollTop;
  150. }
  151. x1 -= elemen.offsetLeft;
  152. y1 -= elemen.offsetTop;
  153. firstClick = false;
  154. } else {
  155. if (evt.pageX || evt.pageY) {
  156. x2 = evt.pageX;
  157. y2 = evt.pageY;
  158. } else {
  159. x2 = evt.clientX + document.body.scrollLeft + elemen.scrollLeft;
  160. y2 = evt.clientY + document.body.scrollTop + elemen.scrollTop;
  161. }
  162. x2 -= elemen.offsetLeft;
  163. y2 -= elemen.offsetTop;
  164. firstClick = true;
  165. if (div !== null) {
  166. document.getElementById("line-heatmap-canvas").removeChild(div);
  167. div = null;
  168. }
  169. div = document.createElement("div");
  170. posLeft = Math.min(x1,x2);
  171. posTop = Math.min(y1,y2) - 585;
  172. yBottom = Math.max(y1,y2) - 585;
  173. xBottom = Math.max(x1,x2);
  174. const width = Math.abs(xBottom - posLeft);
  175. const height = Math.abs(yBottom - posTop);
  176. updateDivText(div, posLeft, posTop, xBottom, yBottom);
  177. div.style.width = width + "px";
  178. div.style.height = height + "px";
  179. div.style.position = "absolute";
  180. div.style.top = posTop + "px";
  181. div.style.left = posLeft + "px";
  182. div.style.border = "1px solid green";
  183. document.getElementById("line-heatmap-canvas").append(div);
  184. }
  185. }
  186. }
  187. function updateDivText (div, posLeft, posTop, xBottom, yBottom) {
  188. const count = countClicks(posLeft, posTop, xBottom, yBottom);
  189. const ratio = count/totalClicks();
  190. div.innerHTML = `<span style="font-size:14px">Quantidade de clicks: ${count}</span>
  191. <span style="font-size:14px">Proporção em relação ao total: ${ratio}</span>`;
  192. }
  193. function clicksEuclideanDist (points) {
  194. if(points.length <= 1) {
  195. return 0;
  196. } else if (points.length == 2) {
  197. const p1 = points[0];
  198. const p2 = points[1];
  199. return euclideanDist(p1, p2);
  200. } else {
  201. let count = 0;
  202. points.reduce((p1, p2) => {
  203. count += euclideanDist(p1, p2);
  204. return p2;
  205. });
  206. return count;
  207. }
  208. }
  209. function calcPixelsPerClick () {
  210. const data = getData();
  211. const totalPixels = clicksEuclideanDist(data);
  212. return totalPixels / totalClicks();
  213. }
  214. function calcPixelsPerClickInArea () {
  215. if(div == null) {
  216. return 0;
  217. }
  218. const data = getClicksInWindow(posLeft, posTop, xBottom, yBottom);
  219. const totalPixels = clicksEuclideanDist(data);
  220. return totalPixels / countClicks(posLeft, posTop, xBottom, yBottom);
  221. }
  222. function euclideanDist (p1, p2) {
  223. return Math.sqrt(Math.pow(p1.x-p2.x, 2) + Math.pow(p1.y-p2.y, 2));
  224. }
  225. export default {
  226. init,
  227. updateLogString,
  228. countClicks,
  229. totalClicks,
  230. getData,
  231. calcPixelsPerClick,
  232. calcPixelsPerClickInArea
  233. }