Displays a base image and an "x-ray" view of a second image where the mouse is pointing
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

356 lignes
11 KiB

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