Furaffinity-Loading-Animations

Library for creating different loading animations on Furaffinity

目前为 2025-02-02 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/485153/1530873/Furaffinity-Loading-Animations.js

  1. // ==UserScript==
  2. // @name Furaffinity-Loading-Animations
  3. // @namespace Violentmonkey Scripts
  4. // @require https://update.greasyfork.org/scripts/525666/1530813/Furaffinity-Prototype-Extensions.js
  5. // @grant none
  6. // @version 1.2.0
  7. // @author Midori Dragon
  8. // @description Library for creating different loading animations on Furaffinity
  9. // @icon https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png
  10. // @license MIT
  11. // ==/UserScript==
  12. // jshint esversion: 8
  13. (() => {
  14. "use strict";
  15. class LoadingBar {
  16. constructor(baseElem) {
  17. this._delay = 2e3;
  18. this._loadingBar = document.createElement("div");
  19. if (!document.getElementById("flaloadingbarstyle")) {
  20. const style = document.createElement("style");
  21. style.id = "flaloadingbarstyle";
  22. style.innerHTML = "@keyframes gradient { 0% { background-position: 0 0; } 100% { background-position: -200% 0; } }";
  23. document.head.appendChild(style);
  24. }
  25. this._baseElem = baseElem;
  26. this._baseElem.appendChild(this._loadingBar);
  27. this.updateBaseElem();
  28. }
  29. get baseElem() {
  30. return this._baseElem;
  31. }
  32. set baseElem(value) {
  33. if (this._baseElem !== value) {
  34. this._baseElem = value;
  35. this.updateBaseElem();
  36. }
  37. }
  38. get delay() {
  39. return this._delay;
  40. }
  41. set delay(value) {
  42. if (this._delay !== value) {
  43. this._delay = value;
  44. this._loadingBar.style.animation = `gradient ${this._delay / 1e3}s infinite`;
  45. }
  46. }
  47. get text() {
  48. var _a;
  49. return null !== (_a = this._loadingBar.textContent) && void 0 !== _a ? _a : "";
  50. }
  51. set text(value) {
  52. if (this._loadingBar.textContent !== value) this._loadingBar.textContent = value;
  53. }
  54. get cornerRadius() {
  55. return parseFloat(this._loadingBar.style.borderRadius.trimEnd("px"));
  56. }
  57. set cornerRadius(value) {
  58. if (parseFloat(this._loadingBar.style.borderRadius.trimEnd("px")) !== value) this._loadingBar.style.borderRadius = value + "px";
  59. }
  60. get height() {
  61. return parseFloat(this._loadingBar.style.height.trimEnd("px"));
  62. }
  63. set height(value) {
  64. if (parseFloat(this._loadingBar.style.height.trimEnd("px")) !== value) {
  65. this._loadingBar.style.height = value + "px";
  66. this._loadingBar.style.lineHeight = value + "px";
  67. }
  68. }
  69. get fontSize() {
  70. return parseFloat(this._loadingBar.style.fontSize.trimEnd("px"));
  71. }
  72. set fontSize(value) {
  73. if (parseFloat(this._loadingBar.style.fontSize.trimEnd("px")) !== value) if (null != value) this._loadingBar.style.fontSize = value + "px"; else this._loadingBar.style.fontSize = "";
  74. }
  75. get gradient() {
  76. return this._loadingBar.style.background;
  77. }
  78. set gradient(value) {
  79. if (this._loadingBar.style.background !== value) this._loadingBar.style.background = value;
  80. }
  81. get visible() {
  82. return "none" !== this._loadingBar.style.display;
  83. }
  84. set visible(value) {
  85. if (this._loadingBar.style.display !== (value ? "block" : "none")) this._loadingBar.style.display = value ? "block" : "none";
  86. }
  87. dispose() {
  88. this.visible = false;
  89. this._baseElem.removeChild(this._loadingBar);
  90. }
  91. updateBaseElem() {
  92. this._loadingBar.className = "fla-loadingbar";
  93. this._loadingBar.textContent = this.text;
  94. this._loadingBar.style.width = "100%";
  95. this._loadingBar.style.height = "60px";
  96. this._loadingBar.style.lineHeight = this.height + "px";
  97. this._loadingBar.style.textShadow = "-1px -1px 0 #000000, 1px -1px 0 #000000, -1px 1px 0 #000000, 1px 1px 0 #000000";
  98. this._loadingBar.style.fontSize = "15px";
  99. this._loadingBar.style.background = "repeating-linear-gradient(to right, rgba(255,10,3,1) 0%, rgba(255,139,6,1) 8%, rgba(253,228,11,1) 16%, rgba(127,236,12,1) 26%, rgba(32,225,207,1) 36%, rgba(140,60,223,1) 46%, rgba(140,60,223,1) 54%, rgba(32,225,207,1) 64%, rgba(127,236,12,1) 74%, rgba(253,228,11,1) 84%, rgba(255,139,6,1) 92%, rgba(255,10,3,1) 100%)";
  100. this._loadingBar.style.backgroundSize = "200% auto";
  101. this._loadingBar.style.backgroundPosition = "0 100%";
  102. this._loadingBar.style.animation = `gradient ${this.delay / 1e3}s infinite`;
  103. this._loadingBar.style.animationFillMode = "forwards";
  104. this._loadingBar.style.animationTimingFunction = "linear";
  105. this._loadingBar.style.display = "none";
  106. }
  107. }
  108. class LoadingImage {
  109. constructor(baseElem) {
  110. this.delay = 100;
  111. this.doScaleImage = true;
  112. this.scaleChange = .05;
  113. this.scaleChangeMax = 1.2;
  114. this.scaleChangeMin = .8;
  115. this.doRotateImage = true;
  116. this.rotateDegrees = 5;
  117. this._isGrowing = true;
  118. this._scale = 1;
  119. this._rotation = 0;
  120. this._size = 60;
  121. this._image = document.createElement("img");
  122. this._image.src = "https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png";
  123. this._imageContainer = document.createElement("div");
  124. this._baseElem = baseElem;
  125. this._imageContainer.appendChild(this._image);
  126. this._baseElem.appendChild(this._imageContainer);
  127. this.updateBaseElem();
  128. }
  129. get baseElem() {
  130. return this._baseElem;
  131. }
  132. set baseElem(value) {
  133. if (this._baseElem !== value) {
  134. this._baseElem = value;
  135. this.updateBaseElem();
  136. }
  137. }
  138. get imageSrc() {
  139. return this._image.src;
  140. }
  141. set imageSrc(value) {
  142. if (this._image.src !== value) this._image.src = value;
  143. }
  144. get rotation() {
  145. return this._rotation;
  146. }
  147. set rotation(value) {
  148. if (this._rotation !== value) {
  149. this._rotation = value;
  150. this._image.style.transform = `scale(${this._scale}) rotate(${this._rotation}deg)`;
  151. }
  152. }
  153. get scale() {
  154. return this._scale;
  155. }
  156. set scale(value) {
  157. if (this._scale !== value) {
  158. this._scale = value;
  159. this._image.style.transform = `scale(${this._scale}) rotate(${this._rotation}deg)`;
  160. }
  161. }
  162. get size() {
  163. return parseFloat(this._imageContainer.style.width.trimEnd("px"));
  164. }
  165. set size(value) {
  166. if (parseFloat(this._imageContainer.style.width.trimEnd("px")) !== value) {
  167. this._imageContainer.style.width = this._size + "px";
  168. this._imageContainer.style.height = this._size + "px";
  169. }
  170. }
  171. get visible() {
  172. return "none" !== this._imageContainer.style.display;
  173. }
  174. set visible(value) {
  175. if (this._imageContainer.style.display !== (value ? "block" : "none")) {
  176. this._imageContainer.style.display = value ? "block" : "none";
  177. if (value) this._intervalId = setInterval((() => {
  178. this.updateAnimationFrame();
  179. }), this.delay); else clearInterval(this._intervalId);
  180. }
  181. }
  182. get isGrowing() {
  183. return this._isGrowing;
  184. }
  185. set isGrowing(value) {
  186. if (this._isGrowing !== value) this._isGrowing = value;
  187. }
  188. dispose() {
  189. this.visible = false;
  190. this._baseElem.removeChild(this._imageContainer);
  191. }
  192. updateBaseElem() {
  193. this._imageContainer.className = "fla-loading-image-container";
  194. this._imageContainer.style.position = "relative";
  195. this._imageContainer.style.width = this.size + "px";
  196. this._imageContainer.style.height = this.size + "px";
  197. this._imageContainer.style.left = "50%";
  198. this._imageContainer.style.transform = "translateX(-50%)";
  199. this._imageContainer.style.display = "none";
  200. this._image.className = "fla-loading-image";
  201. this._image.src = this.imageSrc;
  202. this._image.style.width = "100%";
  203. this._image.style.height = "100%";
  204. this._image.style.transition = "transform 0.5s ease-in-out";
  205. }
  206. updateAnimationFrame() {
  207. if (this.isGrowing) {
  208. this._scale += this.scaleChange;
  209. this._rotation += this.rotateDegrees;
  210. } else {
  211. this._scale -= this.scaleChange;
  212. this._rotation -= this.rotateDegrees;
  213. }
  214. if (this._scale >= this.scaleChangeMax || this._scale <= this.scaleChangeMin) this.isGrowing = !this.isGrowing;
  215. this._image.style.transform = `scale(${this._scale}) rotate(${this._rotation}deg)`;
  216. }
  217. }
  218. class LoadingSpinner {
  219. constructor(baseElem) {
  220. this._delay = 1e3;
  221. this._size = 60;
  222. this._spinnerThickness = 4;
  223. this._spinnerLength = 1;
  224. this._linearSpin = false;
  225. this._animationCurve = "cubic-bezier(.53,.24,.46,.83)";
  226. this._forecolorHex = "#8941de";
  227. this._backcolorHex = "#f3f3f3";
  228. this._spinner = document.createElement("div");
  229. this._spinnerContainer = document.createElement("div");
  230. if (!document.getElementById("flaspinnerstyle")) {
  231. const style = document.createElement("style");
  232. style.id = "flaspinnerstyle";
  233. style.innerHTML = "@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }";
  234. document.head.appendChild(style);
  235. }
  236. this._baseElem = baseElem;
  237. this._spinnerContainer.appendChild(this._spinner);
  238. this._baseElem.appendChild(this._spinnerContainer);
  239. this.updateBaseElem();
  240. }
  241. get baseElem() {
  242. return this._baseElem;
  243. }
  244. set baseElem(value) {
  245. if (this._baseElem !== value) {
  246. this._baseElem = value;
  247. this.updateBaseElem();
  248. }
  249. }
  250. get delay() {
  251. return this._delay;
  252. }
  253. set delay(value) {
  254. if (this._delay !== value) {
  255. this._delay = value;
  256. this._spinner.style.animation = `spin ${this._delay / 1e3}s ${this.animationCurve} infinite`;
  257. }
  258. }
  259. get linearSpin() {
  260. return this._linearSpin;
  261. }
  262. set linearSpin(value) {
  263. if (this._linearSpin !== value) {
  264. this._linearSpin = value;
  265. this._animationCurve = this._linearSpin ? "linear" : "cubic-bezier(.53,.24,.46,.83)";
  266. this._spinner.style.animation = `spin ${this.delay / 1e3}s ${this.animationCurve} infinite`;
  267. }
  268. }
  269. get animationCurve() {
  270. return this._animationCurve;
  271. }
  272. set animationCurve(value) {
  273. if (this._animationCurve !== value) {
  274. this._animationCurve = value;
  275. this._linearSpin = "linear" === this._animationCurve;
  276. this._spinner.style.animation = `spin ${this.delay / 1e3}s ${this.animationCurve} infinite`;
  277. }
  278. }
  279. get size() {
  280. return this._size;
  281. }
  282. set size(value) {
  283. if (this._size !== value) {
  284. this._size = value;
  285. this.updateSize();
  286. }
  287. }
  288. get spinnerThickness() {
  289. return this._spinnerThickness;
  290. }
  291. set spinnerThickness(value) {
  292. if (this._spinnerThickness !== value) {
  293. this._spinnerThickness = value;
  294. this.updateSpinnerBorders();
  295. this.updateSize();
  296. }
  297. }
  298. get spinnerLength() {
  299. return this._spinnerLength;
  300. }
  301. set spinnerLength(value) {
  302. if (this._spinnerLength !== value) {
  303. this._spinnerLength = value;
  304. this.updateSpinnerBorders();
  305. }
  306. }
  307. get forecolorHex() {
  308. return this._forecolorHex;
  309. }
  310. set forecolorHex(value) {
  311. if (this._forecolorHex !== value) {
  312. if (!value.startsWith("#")) value = "#" + value;
  313. this._forecolorHex = value;
  314. this.updateSpinnerBorders();
  315. }
  316. }
  317. get backcolorHex() {
  318. return this._backcolorHex;
  319. }
  320. set backcolorHex(value) {
  321. if (this._backcolorHex !== value) {
  322. if (!value.startsWith("#")) value = "#" + value;
  323. this._backcolorHex = value;
  324. this.updateSpinnerBorders();
  325. }
  326. }
  327. get visible() {
  328. return "none" !== this._spinnerContainer.style.display;
  329. }
  330. set visible(value) {
  331. if (this._spinnerContainer.style.display !== (value ? "block" : "none")) this._spinnerContainer.style.display = value ? "block" : "none";
  332. }
  333. dispose() {
  334. this.visible = false;
  335. this._baseElem.removeChild(this._spinnerContainer);
  336. }
  337. updateSize() {
  338. this._spinnerContainer.style.width = this.size + "px";
  339. this._spinnerContainer.style.height = this.size + "px";
  340. this._spinner.style.width = this.size - 2 * this.spinnerThickness + "px";
  341. this._spinner.style.height = this.size - 2 * this.spinnerThickness + "px";
  342. }
  343. updateSpinnerBorders() {
  344. this._spinner.style.border = this.spinnerThickness + "px solid " + this.backcolorHex;
  345. if (this.spinnerLength >= 4) this._spinner.style.borderLeft = this.spinnerThickness + "px solid " + this._forecolorHex; else if (this.spinnerLength >= 3) this._spinner.style.borderBottom = this.spinnerThickness + "px solid " + this._forecolorHex; else if (this.spinnerLength >= 2) this._spinner.style.borderRight = this.spinnerThickness + "px solid " + this._forecolorHex; else if (this.spinnerLength >= 1) this._spinner.style.borderTop = this.spinnerThickness + "px solid " + this._forecolorHex;
  346. }
  347. updateBaseElem() {
  348. this._spinnerContainer.className = "fla-spinner-container";
  349. this._spinnerContainer.style.position = "relative";
  350. this._spinnerContainer.style.width = this.size + "px";
  351. this._spinnerContainer.style.height = this.size + "px";
  352. this._spinnerContainer.style.left = "50%";
  353. this._spinnerContainer.style.transform = "translateX(-50%)";
  354. this._spinnerContainer.style.display = "none";
  355. this._spinner.className = "fla-spinner";
  356. this.updateSpinnerBorders();
  357. this._spinner.style.borderRadius = "50%";
  358. this._spinner.style.position = "relative";
  359. this._spinner.style.width = this.size - 2 * this.spinnerThickness + "px";
  360. this._spinner.style.height = this.size - 2 * this.spinnerThickness + "px";
  361. this._spinner.style.animation = `spin ${this.delay / 1e3}s ${this.animationCurve} infinite`;
  362. this.updateSize();
  363. }
  364. }
  365. class LoadingTextSpinner {
  366. constructor(baseElem) {
  367. this._characters = [ "◜", "◠", "◝", "◞", "◡", "◟" ];
  368. this._delay = 600;
  369. this._currIndex = -1;
  370. this._spinner = document.createElement("div");
  371. this._baseElem = baseElem;
  372. this._baseElem.appendChild(this._spinner);
  373. this.updateBaseElem();
  374. }
  375. get baseElem() {
  376. return this._baseElem;
  377. }
  378. set baseElem(value) {
  379. if (this._baseElem !== value) {
  380. this._baseElem = value;
  381. this.updateBaseElem();
  382. }
  383. }
  384. get fontSize() {
  385. return parseFloat(this._spinner.style.fontSize.trimEnd("px"));
  386. }
  387. set fontSize(value) {
  388. if (parseFloat(this._spinner.style.fontSize.trimEnd("px")) !== value) this._spinner.style.fontSize = value + "px";
  389. }
  390. get visible() {
  391. return "none" !== this._spinner.style.display;
  392. }
  393. set visible(value) {
  394. if (this._spinner.style.display !== (value ? "block" : "none")) {
  395. this._spinner.style.display = value ? "block" : "none";
  396. if (value) this.start(); else this.stop();
  397. }
  398. }
  399. get delay() {
  400. return this._delay;
  401. }
  402. set delay(value) {
  403. if (this._delay !== value) {
  404. this._delay = value;
  405. if (this.visible) {
  406. this.stop();
  407. this.start();
  408. }
  409. }
  410. }
  411. get characters() {
  412. return this._characters;
  413. }
  414. set characters(value) {
  415. if (this._characters !== value) {
  416. this._characters = value;
  417. if (this.visible) {
  418. this.stop();
  419. this.start();
  420. }
  421. }
  422. }
  423. dispose() {
  424. this.visible = false;
  425. this._baseElem.removeChild(this._spinner);
  426. }
  427. start() {
  428. if (null == this._intervalId && 0 !== this._characters.length) {
  429. if (this._currIndex < 0) this._currIndex = 0;
  430. this._prevContainerTextContent = function removeTextNodes(targetElement) {
  431. let removedText = "";
  432. !function traverseAndRemoveTextNodes(node) {
  433. var _a;
  434. for (let i = node.childNodes.length - 1; i >= 0; i--) {
  435. const child = node.childNodes[i];
  436. if (child.nodeType === Node.TEXT_NODE) {
  437. removedText += (null !== (_a = child.textContent) && void 0 !== _a ? _a : "") + "\n";
  438. node.removeChild(child);
  439. }
  440. }
  441. }(targetElement);
  442. return removedText;
  443. }(this._baseElem);
  444. this._spinner.textContent = "⠀⠀";
  445. this._intervalId = setInterval((() => {
  446. if (this._currIndex >= this.characters.length) this._currIndex = 0;
  447. this._spinner.textContent = this.characters[this._currIndex];
  448. this._currIndex++;
  449. }), this.delay / this.characters.length);
  450. }
  451. }
  452. stop() {
  453. var _a;
  454. if (null != this._intervalId) {
  455. clearInterval(this._intervalId);
  456. this._baseElem.textContent = null !== (_a = this._prevContainerTextContent) && void 0 !== _a ? _a : null;
  457. this._intervalId = void 0;
  458. }
  459. }
  460. updateBaseElem() {
  461. this._spinner.className = "fla-text-spinner";
  462. this._spinner.style.display = "none";
  463. this._spinner.style.fontSize = "15px";
  464. }
  465. }
  466. Object.defineProperties(window, {
  467. FALoadingSpinner: {
  468. get: () => LoadingSpinner
  469. },
  470. FALoadingTextSpinner: {
  471. get: () => LoadingTextSpinner
  472. },
  473. FALoadingImage: {
  474. get: () => LoadingImage
  475. },
  476. FALoadingBar: {
  477. get: () => LoadingBar
  478. }
  479. });
  480. })();