Displays a base image and an "x-ray" view of a second image where the mouse is pointing
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

356 行
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. }