Displays a base image and an "x-ray" view of a second image where the mouse is pointing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

275 line
8.4 KiB

  1. "use strict";
  2. let overlayLoaded = false;
  3. let baseLoaded = false;
  4. let radius = 200;
  5. let softness = 25;
  6. document.addEventListener("DOMContentLoaded", e => {
  7. document.querySelector("#load-button").addEventListener("click", e => {
  8. console.log("Trying to load...");
  9. const baseInput = document.querySelector("#base-url").value;
  10. const overlayInput = document.querySelector("#overlay-url").value;
  11. let success = true;
  12. try {
  13. let baseURL = new URL(baseInput)
  14. console.log(baseURL);
  15. } catch {
  16. document.querySelector("#base-url").value = "";
  17. document.querySelector("#base-url").placeholder = "Invalid URL...";
  18. success = false;
  19. }
  20. try {
  21. let overlayURL = new URL(overlayInput)
  22. console.log(overlayURL);
  23. } catch {
  24. document.querySelector("#overlay-url").value = "";
  25. document.querySelector("#overlay-url").placeholder = "Invalid URL...";
  26. success = false;
  27. }
  28. if (!success) {
  29. return;
  30. }
  31. const overlayImg = document.querySelector("#overlay-img");
  32. const baseImg = document.querySelector("#base-img");
  33. overlayImg.src = overlayInput;
  34. baseImg.src = baseInput;
  35. load();
  36. try {
  37. localStorage.setItem("base", baseInput);
  38. localStorage.setItem("overlay", overlayInput);
  39. } catch {
  40. console.error("Couldn't set something in local storage :(")
  41. }
  42. });
  43. let url = new URL(window.location);
  44. const overlay = document.querySelector("#overlay");
  45. overlay.addEventListener("mousemove", e => {
  46. let x = e.clientX - e.target.getBoundingClientRect().x;
  47. let y = e.clientY - e.target.getBoundingClientRect().y;
  48. updateOverlay([[x,y]]);
  49. });
  50. overlay.addEventListener("touchmove", e => {
  51. let offsetX = e.target.getBoundingClientRect().x;
  52. let offsetY = e.target.getBoundingClientRect().y;
  53. let touches = [];
  54. for (let i=0; i < e.touches.length; i++) {
  55. let x = e.touches[i].clientX - offsetX;
  56. let y = e.touches[i].clientY - offsetY;
  57. touches.push([x,y]);
  58. }
  59. updateOverlay(touches);
  60. });
  61. document.querySelector("#radius-slider").addEventListener("input", e => {
  62. try {
  63. radius = parseInt(e.target.value);
  64. document.querySelector("#radius-input").value = radius;
  65. } catch {
  66. console.warn("That wasn't a valid radius: " + e.target.value);
  67. }
  68. });
  69. document.querySelector("#radius-input").addEventListener("input", e => {
  70. try {
  71. radius = parseInt(e.target.value);
  72. document.querySelector("#radius-slider").value = radius;
  73. } catch {
  74. console.warn("That wasn't a valid radius: " + e.target.value);
  75. }
  76. });
  77. document.querySelector("#softness-slider").addEventListener("input", e => {
  78. try {
  79. softness = parseInt(e.target.value);
  80. document.querySelector("#softness-input").value = softness;
  81. } catch {
  82. console.warn("That wasn't a valid softness: " + e.target.value);
  83. }
  84. });
  85. document.querySelector("#softness-input").addEventListener("input", e => {
  86. try {
  87. softness = parseInt(e.target.value);
  88. document.querySelector("#softness-slider").value = softness;
  89. } catch {
  90. console.warn("That wasn't a valid softness: " + e.target.value);
  91. }
  92. });
  93. // see if we have params already; if so, use them!
  94. const overlayImg = document.querySelector("#overlay-img");
  95. const baseImg = document.querySelector("#base-img");
  96. const baseInput = document.querySelector("#base-url");
  97. const overlayInput = document.querySelector("#overlay-url");
  98. if (url.searchParams.has("base") && url.searchParams.has("overlay")) {
  99. let baseURL = url.searchParams.get("base");
  100. let overlayURL = url.searchParams.get("overlay");
  101. baseImg.src = baseURL;
  102. overlayImg.src = overlayURL;
  103. baseInput.value = baseURL;
  104. overlayInput.value = overlayURL;
  105. load();
  106. } else {
  107. try {
  108. baseInput.value = localStorage.getItem("base");
  109. overlayInput.value = localStorage.getItem("overlay");
  110. } catch {
  111. console.error("Couldn't get something from local storage :(")
  112. }
  113. }
  114. if (url.searchParams.has("radius")) {
  115. try {
  116. radius = parseInt(url.searchParams.get("radius"));
  117. document.querySelector("#radius-slider").value = radius;
  118. document.querySelector("#radius-input").value = radius;
  119. } catch {
  120. console.warn("That was a bogus radius...");
  121. }
  122. }
  123. if (url.searchParams.has("softness")) {
  124. try {
  125. softness = parseInt(url.searchParams.get("softness"));
  126. document.querySelector("#softness-slider").value = softness;
  127. document.querySelector("#softness-input").value = softness;
  128. } catch {
  129. console.warn("That was a bogus softness...");
  130. }
  131. }
  132. document.querySelector("#share-button").addEventListener("click", e => {
  133. let shareURL = new URL(window.location);
  134. // for some reason, the parser gets confused by urlencoded urls...
  135. // so, to get rid of all parameters, we do this
  136. let keys = Array.from(shareURL.searchParams.keys());
  137. do {
  138. keys = Array.from(shareURL.searchParams.keys());
  139. keys.forEach(key => {
  140. shareURL.searchParams.delete(key);
  141. });
  142. } while (keys.length > 0)
  143. shareURL.searchParams.append("base", baseImg.src);
  144. shareURL.searchParams.append("overlay", overlayImg.src);
  145. shareURL.searchParams.append("radius", radius);
  146. shareURL.searchParams.append("softness", softness);
  147. console.log(shareURL);
  148. window.location = shareURL;
  149. });
  150. });
  151. function load() {
  152. document.querySelector("#menu").classList.remove("start");
  153. const overlayImg = document.querySelector("#overlay-img");
  154. const baseImg = document.querySelector("#base-img");
  155. overlayImg.addEventListener("load", function overlayLoad() {
  156. console.log("The overlay is loaded");
  157. overlayLoaded = true;
  158. if (overlayLoaded && baseLoaded) {
  159. setup();
  160. }
  161. overlayImg.removeEventListener("load", overlayLoad);
  162. })
  163. baseImg.addEventListener("load", function baseLoad() {
  164. console.log("The base is loaded");
  165. baseLoaded = true;
  166. if (overlayLoaded && baseLoaded) {
  167. setup();
  168. }
  169. baseImg.removeEventListener("load", baseLoad);
  170. })
  171. }
  172. function setup() {
  173. const overlay = document.querySelector("#overlay");
  174. const base = document.querySelector("#base");
  175. overlay.classList.remove("hidden");
  176. base.classList.remove("hidden");
  177. const overlayImg = document.querySelector("#overlay-img");
  178. const baseImg = document.querySelector("#base-img");
  179. /** @type {CanvasRenderingContext2D} */
  180. const overlayCtx = overlay.getContext("2d");
  181. /** @type {CanvasRenderingContext2D} */
  182. const baseCtx = base.getContext("2d");
  183. baseCtx.canvas.width = baseImg.width;
  184. baseCtx.canvas.height = baseImg.height;
  185. baseCtx.drawImage(baseImg, 0, 0);
  186. overlayCtx.canvas.width = overlayImg.width;
  187. overlayCtx.canvas.height = overlayImg.height;
  188. console.log("Done");
  189. }
  190. function updateOverlay(points) {
  191. /** @type {CanvasRenderingContext2D} */
  192. const overlayCtx = overlay.getContext("2d");
  193. const overlayImg = document.querySelector("#overlay-img");
  194. const w = overlayCtx.canvas.width;
  195. const h = overlayCtx.canvas.height;
  196. overlayCtx.save();
  197. overlayCtx.globalCompositeOperation = "source-over";
  198. overlayCtx.clearRect(0, 0, w, h);
  199. points.forEach(point => {
  200. const [x,y] = point;
  201. overlayCtx.beginPath();
  202. overlayCtx.ellipse(x, y, radius, radius, 0, 0, 2 * Math.PI);
  203. const gradient = overlayCtx.createRadialGradient(x, y, 0, x, y, radius);
  204. gradient.addColorStop((100-softness)/100, '#000000FF');
  205. gradient.addColorStop(1, '#00000000');
  206. overlayCtx.fillStyle = gradient;
  207. overlayCtx.fill();
  208. })
  209. overlayCtx.globalCompositeOperation = "source-in";
  210. overlayCtx.drawImage(overlayImg, 0, 0);
  211. overlayCtx.restore();
  212. }