ilove.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <!DOCTYPE html>
  2. <!-- SPDX-License-Identifier: GPL-3.0-or-later -->
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>I Love SVG</title>
  8. <style>
  9. * {
  10. margin: 0;
  11. padding: 0;
  12. box-sizing: border-box;
  13. }
  14. body {
  15. background-color: gray;
  16. font-family: 'Arial', sans-serif;
  17. display: flex;
  18. flex-direction: column;
  19. height: 100vh;
  20. padding: 20px;
  21. }
  22. .svg-container {
  23. display: flex;
  24. flex-wrap: wrap;
  25. gap: 10px;
  26. margin-bottom: 20px;
  27. }
  28. .svg-container img {
  29. height: 75px;
  30. cursor: pointer;
  31. }
  32. .input-wrapper {
  33. display: flex;
  34. align-items: center;
  35. justify-content: center; /* Centers the items horizontally */
  36. flex-wrap: wrap; /* Allow the items to wrap if there isn't enough horizontal space */
  37. gap: 10px; /* Gap between the items */
  38. }
  39. .input-wrapper #labelInput {
  40. font-family: 'IBM Plex Sans', sans-serif;
  41. font-weight: bold;
  42. font-size: 24px;
  43. color: white;
  44. background-color: gray;
  45. border: none;
  46. cursor: default;
  47. padding: 10px 15px;
  48. margin-right: 10px;
  49. text-align: right;
  50. width: 135px;
  51. }
  52. .string-input input {
  53. font-family: 'IBM Plex Sans', sans-serif;
  54. font-weight: bold;
  55. font-size: 24px;
  56. background-color: black;
  57. color: white;
  58. border: none;
  59. border-radius: 8px;
  60. padding: 10px 15px;
  61. width: 100%;
  62. box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
  63. outline: none;
  64. }
  65. .string-input input:focus {
  66. box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.3);
  67. }
  68. .big-svg-container {
  69. flex: 1;
  70. display: flex;
  71. align-items: center;
  72. justify-content: center;
  73. overflow: hidden;
  74. }
  75. .big-svg {
  76. max-width: 100%;
  77. max-height: 95%;
  78. object-fit: contain;
  79. }
  80. .input-wrapper button {
  81. background-color: darkgray;
  82. color: white;
  83. border: none;
  84. border-radius: 8px;
  85. padding: 5px 10px;
  86. margin-left: 10px; /* Space between buttons */
  87. cursor: pointer;
  88. transition: background-color 0.3s; /* Smooth transition for hover effect */
  89. }
  90. .input-wrapper button:hover {
  91. background-color: #555; /* Darker shade on hover */
  92. }
  93. /* Style for the dropdown */
  94. #resolutionSelect {
  95. background-color: darkgray; /* Same as button background */
  96. color: white; /* Text color */
  97. border: none; /* Remove border */
  98. border-radius: 8px; /* Rounded corners, same as buttons */
  99. padding: 5px 10px; /* Some padding for aesthetics */
  100. margin-left: 10px; /* Space between dropdown and adjacent elements */
  101. font-family: "IBM Plex Sans", sans-serif; /* Font from SVG */
  102. font-weight: bold; /* Bold font */
  103. font-size: 16px; /* Font size consistent with text box */
  104. cursor: pointer; /* Hand cursor for better UX */
  105. appearance: none; /* Remove default styling on some browsers */
  106. -webkit-appearance: none; /* Remove default styling on WebKit browsers */
  107. -moz-appearance: none; /* Remove default styling on Firefox */
  108. padding: 10px 20px; /* Increase padding for more height */
  109. line-height: 1.5; /* Increase line height for a taller dropdown */
  110. /* Arrow for the dropdown */
  111. background: darkgray url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M31.3 192l121.5 121.5c4.7 4.7 12.3 4.7 17 0L291.3 192c4.7-4.7 4.7-12.3 0-17l-22.6-22.6c-4.7-4.7-12.3-4.7-17 0L160 273.9 68.3 152c-4.7-4.7-12.3-4.7-17 0L28.7 174.5c-4.7 4.7-4.7 12.3 0 17.1z" fill="white"/></svg>') no-repeat right 10px center;
  112. padding-right: 40px; /* Add padding to prevent text from overlapping the arrow */
  113. }
  114. /* Hover effect for the dropdown, similar to buttons */
  115. #resolutionSelect:hover {
  116. background-color: #555; /* Darker shade on hover */
  117. }
  118. /* Arrow for the dropdown (this is a bit of a hack, but it works for adding a custom arrow) */
  119. #resolutionSelect::-ms-expand {
  120. display: none; /* Hide default arrow for IE/Edge */
  121. }
  122. #resolutionSelect::after {
  123. content: '\25BC'; /* Down arrow symbol */
  124. position: absolute;
  125. top: 50%;
  126. right: 10px;
  127. transform: translateY(-50%);
  128. pointer-events: none; /* Ensure dropdown still works when clicking arrow */
  129. }
  130. </style>
  131. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
  132. </head>
  133. <body>
  134. <div class="svg-container">
  135. <!-- SVG examples will be loaded here -->
  136. </div>
  137. <div class="input-wrapper">
  138. <input type="text" value="You Love" id="labelInput" readonly>
  139. <div class="string-input">
  140. <input type="text" id="stringInput" placeholder="Enter your STRING here">
  141. </div>
  142. <select id="resolutionSelect">
  143. <option value="360">360p</option>
  144. <option value="720">720p</option>
  145. <option value="1080">1080p</option>
  146. <option value="4k">4k</option>
  147. <option value="8k">8k</option>
  148. </select>
  149. <button id="copyUrl"><i class="fas fa-copy"></i><br/>URL</button>
  150. <button id="downloadSvg"><i class="fas fa-download"></i><br/>SVG</button>
  151. <button id="downloadPng"><i class="fas fa-image"></i><br/>PNG</button>
  152. <button id="downloadWebp"><i class="fas fa-file-image"></i><br/>WEBP</button>
  153. </div>
  154. <div class="big-svg-container">
  155. <img src="" alt="Big SVG Image" id="bigSvg" class="big-svg">
  156. </div>
  157. <script>
  158. document.addEventListener('DOMContentLoaded', function() {
  159. const examples = ["TROUBLE", "3AM", "BUGS", "ML", "AI", "CODE", "DEV", "DevOps", "Observability", "Telemetry", "Dashboards", "Automation", "K8s", "Docker", "PostgreSQL", "NightShifts", "OnCall", "Linux", "Debian", "Ubuntu", "Arch", "Manjaro", "CentOS", "AWS"];
  160. const svgContainer = document.querySelector('.svg-container');
  161. const bigSvg = document.getElementById('bigSvg');
  162. const stringInput = document.getElementById('stringInput');
  163. // Calculate the API endpoint
  164. const currentUrl = new URL(window.location.href);
  165. const apiEndpoint = `${currentUrl.origin}${currentUrl.pathname.replace('ilove.html', '')}api/v2/ilove.svg?love=`;
  166. // Load examples
  167. examples.forEach(example => {
  168. const img = document.createElement('img');
  169. img.src = `${apiEndpoint}${example}`;
  170. img.addEventListener('click', function() {
  171. stringInput.value = example;
  172. updateBigSvg();
  173. });
  174. svgContainer.appendChild(img);
  175. });
  176. // Set the default value of the text box to the first word from the examples
  177. stringInput.value = examples[0];
  178. updateBigSvg();
  179. // Update the big SVG when the input changes
  180. stringInput.addEventListener('input', updateBigSvg);
  181. function updateBigSvg() {
  182. const encodedValue = encodeURIComponent(stringInput.value);
  183. bigSvg.src = `${apiEndpoint}${encodedValue}`;
  184. }
  185. // Copy URL functionality
  186. document.getElementById('copyUrl').addEventListener('click', function() {
  187. const tempTextArea = document.createElement('textarea');
  188. tempTextArea.value = bigSvg.src;
  189. document.body.appendChild(tempTextArea);
  190. tempTextArea.select();
  191. document.execCommand('copy');
  192. document.body.removeChild(tempTextArea);
  193. // Update button icon and text temporarily
  194. const btn = this; // 'this' refers to the clicked element (in this case, the 'copyUrl' button)
  195. const originalIcon = btn.querySelector('i');
  196. const breakElement = btn.querySelector('br');
  197. // Replace the icon and text
  198. originalIcon.className = "fas fa-check"; // Change to checkmark icon
  199. btn.textContent = ''; // Clear the button text
  200. btn.appendChild(originalIcon); // Add the checkmark icon
  201. btn.appendChild(breakElement); // Add the break element
  202. btn.appendChild(document.createTextNode('Copied')); // Add the "Copied" text
  203. setTimeout(() => {
  204. originalIcon.className = "fas fa-copy"; // Revert back to copy icon
  205. btn.textContent = ''; // Clear the button text
  206. btn.appendChild(originalIcon); // Add the copy icon
  207. btn.appendChild(breakElement); // Add the break element
  208. btn.appendChild(document.createTextNode('URL')); // Add the "URL" text
  209. }, 2000); // Revert back after 2 seconds
  210. });
  211. // Download SVG
  212. document.getElementById('downloadSvg').addEventListener('click', function() {
  213. const filename = `I love ${stringInput.value}.svg`;
  214. triggerDownload(bigSvg.src, filename);
  215. });
  216. // Download PNG
  217. document.getElementById('downloadPng').addEventListener('click', function() {
  218. convertSvgToImage('png');
  219. });
  220. // Download WEBP
  221. document.getElementById('downloadWebp').addEventListener('click', function() {
  222. convertSvgToImage('webp');
  223. });
  224. function convertSvgToImage(format) {
  225. const image = new Image();
  226. image.src = bigSvg.src;
  227. image.onload = function() {
  228. const resolution = getResolutionDimensions();
  229. // Calculate the scaling factor based on the resolution width
  230. const scaleFactor = resolution.width / image.width;
  231. // Set canvas dimensions based on SVG's aspect ratio
  232. const canvas = document.createElement('canvas');
  233. canvas.width = image.width * scaleFactor;
  234. canvas.height = image.height * scaleFactor;
  235. const ctx = canvas.getContext('2d');
  236. ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
  237. const filename = `I love ${stringInput.value}.${format}`;
  238. triggerDownload(canvas.toDataURL(`image/${format}`), filename);
  239. };
  240. }
  241. function getResolutionDimensions() {
  242. const resolution = document.getElementById('resolutionSelect').value;
  243. switch(resolution) {
  244. case "360": return { width: 640, height: 360 };
  245. case "720": return { width: 1280, height: 720 };
  246. case "1080": return { width: 1920, height: 1080 };
  247. case "4k": return { width: 3840, height: 2160 };
  248. case "8k": return { width: 7680, height: 4320 };
  249. default: return { width: 640, height: 360 }; // Default to 360p
  250. }
  251. }
  252. function triggerDownload(url, filename) {
  253. const downloadLink = document.createElement('a');
  254. downloadLink.href = url;
  255. downloadLink.download = filename;
  256. document.body.appendChild(downloadLink);
  257. downloadLink.click();
  258. document.body.removeChild(downloadLink);
  259. }
  260. });
  261. </script>
  262. </body>
  263. </html>