easyv悬浮窗

提供一个可编辑的悬浮球

  1. // ==UserScript==
  2. // @name easyv悬浮窗
  3. // @namespace https://lssnow.gitee.io/stone/
  4. // @version 0.1.0
  5. // @description 提供一个可编辑的悬浮球
  6. // @author stone
  7. // @match https://workspace.easyv.cloud/create/*
  8. // @match https://workspace.easyv.cloud/dashboard/*
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. "use stric"
  15. document.documentElement.style.overflow = "hidden"
  16. var isMin = false
  17. var ballSize = 60
  18. var currentPosition = "left"
  19. // 处理公共部分工具函数
  20. function resolveDom(styles, dom) {
  21. const element = document.createElement(dom)
  22. // element.style.display = "none"
  23. for (const key in styles) {
  24. element.style[key] = styles[key]
  25. }
  26. // element.style.display = "block"
  27.  
  28.  
  29. return element
  30. }
  31. // 创建面板
  32. function createPanel() {
  33. const styles = {
  34. position: "absolute",
  35. zIndex: 998,
  36. width: "250px",
  37. height: "300px",
  38. background: "rgba(255,255,255,0.5)",
  39. top: "10px",
  40. left: "10px",
  41. color: "black",
  42. border: "1px solid grey",
  43. borderRadius: "5px",
  44. display: "flex",
  45. flexDirection: "column",
  46. boxSizing: "border-box",
  47. transition: "width linear 0.3s,height linear 0.3s",
  48. boxShadow:
  49. `2.6px 2.8px 2.2px rgba(0, 0, 0, 0.02),
  50. 6.3px 6.7px 5.3px rgba(0, 0, 0, 0.028),
  51. 11.8px 12.5px 10px rgba(0, 0, 0, 0.035),
  52. 21px 22.3px 17.9px rgba(0, 0, 0, 0.042),
  53. 39.3px 41.8px 33.4px rgba(0, 0, 0, 0.05),
  54. 94px 100px 80px rgba(0, 0, 0, 0.07)`
  55. }
  56. const panel = resolveDom(styles, 'div')
  57. document.body.appendChild(panel)
  58. return panel
  59. }
  60.  
  61. // 创建导航栏
  62. function createMenu(panel) {
  63. const styles = {
  64. width: "100%",
  65. height: "10%",
  66. background: "rgba(255,255,255,0.5)",
  67. top: "10px",
  68. left: "10px",
  69. borderBottom: "1px solid grey",
  70. borderTopLeftRadius: "5px",
  71. borderTopRightRadius: "5px",
  72. }
  73. const menu = resolveDom(styles, 'div')
  74.  
  75.  
  76. const options = {
  77. isMouseDown: false,
  78. initX: 0,
  79. initY: 0
  80. }
  81. menu.addEventListener('mousedown', (e) => {
  82. panel.style.transition = "width linear 0.3s,height linear 0.3s"
  83. menu.style.cursor = "move"
  84. options.isMouseDown = true
  85. options.initX = e.clientX - panel.offsetLeft
  86. options.initY = e.clientY - panel.offsetTop
  87.  
  88. document.onmousemove = function (e) {
  89. if (options.isMouseDown) {
  90. let skewX = e.clientX - options.initX
  91. let skewY = e.clientY - options.initY
  92. if (skewX < 0) {
  93. skewX = 10
  94. }
  95. if (skewY < 0) {
  96. skewY = 0
  97. }
  98. if (skewX > (window.innerWidth - panel.offsetWidth)) {
  99. skewX = (window.innerWidth - panel.offsetWidth - 10)
  100. }
  101. if (skewY > window.innerHeight - panel.offsetHeight) {
  102. skewY = (window.innerHeight - panel.offsetHeight)
  103. }
  104. panel.style.left = skewX + "px"
  105. panel.style.top = skewY + "px"
  106. }
  107. }
  108. document.onmouseup = function (e) {
  109. options.isMouseDown = false
  110. menu.style.cursor = "default"
  111. this.onmousemove = null
  112. this.onmouseup = null
  113.  
  114. }
  115. })
  116.  
  117. panel.appendChild(menu)
  118. return menu
  119. }
  120.  
  121. // 创建文本域
  122. function createTextArea(panel) {
  123. let folditem;
  124. // console.log(folditem);
  125.  
  126. const styles = {
  127. width: "100%",
  128. height: "90%",
  129. background: "rgba(255,255,255,0.5)",
  130. border: "none",
  131. outline: "none",
  132. resize: "none",
  133. borderBottomLeftRadius: "5px",
  134. borderBottomRightRadius: "5px",
  135. boxSizing: "border-box"
  136. }
  137. const textarea = resolveDom(styles, 'textarea')
  138. const options = {
  139. isMouseDown: false,
  140. initX: 0,
  141. initY: 0
  142. }
  143. textarea.addEventListener('mousedown', (e) => {
  144. folditem = document.querySelector('#foldItem')
  145. if (!isMin) return
  146. panel.style.transition = "width linear 0.3s,height linear 0.3s"
  147. textarea.style.cursor = "move"
  148. options.isMouseDown = true
  149. options.initX = e.clientX - panel.offsetLeft
  150. options.initY = e.clientY - panel.offsetTop
  151.  
  152. document.onmousemove = function (e) {
  153. if (options.isMouseDown) {
  154. let skewX = e.clientX - options.initX
  155. let skewY = e.clientY - options.initY
  156. if (skewX < 0) {
  157. skewX = 0
  158. }
  159. if (skewY < 0) {
  160. skewY = 0
  161. }
  162. if (skewX > (window.innerWidth - panel.offsetWidth)) {
  163. skewX = (window.innerWidth - panel.offsetWidth)
  164. }
  165. if (skewY > window.innerHeight - panel.offsetHeight) {
  166. skewY = (window.innerHeight - panel.offsetHeight)
  167. }
  168. panel.style.left = skewX + "px"
  169. panel.style.top = skewY + "px"
  170. }
  171. }
  172. document.onmouseup = function (e) {
  173. panel.style.transition = "width linear 0.3s,height linear 0.3s,left linear 0.3s"
  174. let skewX = e.clientX - options.initX
  175. let skewY = e.clientY - options.initY
  176. const target = window.innerWidth / 2 - ballSize / 2
  177.  
  178. if (skewX < target) {
  179. currentPosition = "left"
  180. folditem.innerText = ">"
  181. folditem.style.border = "none"
  182. folditem.style.textAlign = "right"
  183. folditem.style.borderRight = "1px solid black",
  184. folditem.style.borderTopRightRadius = "40%",
  185. folditem.style.borderBottomRightRadius = "40%",
  186. folditem.style.removeProperty('left')
  187. folditem.style.right = 0
  188. skewX = -ballSize / 2
  189. } else {
  190. currentPosition = "right"
  191. folditem.innerText = "<"
  192. folditem.style.border = "none"
  193. folditem.style.textAlign = "left"
  194. folditem.style.borderLeft = "1px solid black",
  195. folditem.style.borderTopLeftRadius = "40%",
  196. folditem.style.borderBottomLeftRadius = "40%",
  197. folditem.style.removeProperty('right')
  198. folditem.style.left = 0
  199. skewX = window.innerWidth - panel.offsetWidth + ballSize / 2
  200. }
  201.  
  202. if (currentPosition == "left") {
  203. panel.style.removeProperty('right')
  204. panel.style.left = skewX + "px"
  205. } else {
  206. panel.style.removeProperty('left')
  207. panel.style.left = skewX + "px"
  208. }
  209.  
  210.  
  211. options.isMouseDown = false
  212. textarea.style.cursor = "default"
  213. this.onmousemove = null
  214. this.onmouseup = null
  215. }
  216. })
  217.  
  218. panel.appendChild(textarea)
  219. return textarea
  220. }
  221.  
  222.  
  223. // 吸附在左边
  224. function stickLeft(folditem, panel, textarea) {
  225. if (folditem.innerText == '>') {
  226. panel.style.borderRadius = "5px"
  227. panel.style.overflow = "visible"
  228. panel.style.width = "250px"
  229. panel.style.height = "300px"
  230. panel.style.left = "10px"
  231. isMin = false
  232. textarea.removeAttribute('readonly')
  233. folditem.style.right = "-10px"
  234. folditem.innerText = "<"
  235. } else {
  236. panel.style.transition = "width linear 0.3s,height linear 0.3s,left linear 0.3s"
  237. panel.style.borderRadius = "50%"
  238. panel.style.overflow = "hidden"
  239. panel.style.width = ballSize + "px"
  240. panel.style.height = ballSize + "px"
  241. panel.style.left = (-ballSize / 2) + "px"
  242. isMin = true
  243. textarea.setAttribute('readonly', "")
  244. folditem.style.right = "0px"
  245. folditem.innerText = ">"
  246. }
  247. }
  248.  
  249. // 吸附在右边
  250. function stickRight(folditem, panel, textarea) {
  251. if (folditem.innerText == '<') {
  252. panel.style.borderRadius = "5px"
  253. panel.style.overflow = "visible"
  254. panel.style.width = "250px"
  255. panel.style.height = "300px"
  256. panel.style.left = window.innerWidth - 290 + ballSize / 2 + "px"
  257. isMin = false
  258. textarea.removeAttribute('readonly')
  259. folditem.style.removeProperty("right")
  260. folditem.style.left = "-10px"
  261. folditem.innerText = ">"
  262. } else {
  263. panel.style.transition = "width linear 0.3s,height linear 0.3s,left linear 0.3s"
  264. panel.style.borderRadius = "50%"
  265. panel.style.overflow = "hidden"
  266. panel.style.width = ballSize + "px"
  267. panel.style.height = ballSize + "px"
  268. panel.style.left = window.innerWidth - 60 + ballSize / 2 + "px"
  269. isMin = true
  270. textarea.setAttribute('readonly', "")
  271. folditem.style.removeProperty("right")
  272. folditem.style.left = "0px"
  273. folditem.innerText = "<"
  274. }
  275. }
  276.  
  277. // 创建折叠选项
  278. function createFoldItem(panel, textarea) {
  279. const styles = {
  280. position: "absolute",
  281. zIndex: 999,
  282. width: "20px",
  283. height: "20px",
  284. right: "-10px",
  285. top: 0,
  286. bottom: 0,
  287. margin: "auto",
  288. textAlign: "right",
  289. cursor: "pointer",
  290. background: "rgba(255,255,255,0.5)",
  291. borderRight: "1px solid black",
  292. borderTopRightRadius: "40%",
  293. borderBottomRightRadius: "40%",
  294. // background:"red"
  295. }
  296. const folditem = resolveDom(styles, 'div')
  297. folditem.setAttribute('id', "foldItem")
  298. folditem.innerText = "<"
  299. panel.appendChild(folditem)
  300.  
  301. folditem.addEventListener('click', () => {
  302. if (currentPosition == "left") {
  303. stickLeft(folditem, panel, textarea)
  304. } else {
  305. stickRight(folditem, panel, textarea)
  306. }
  307. })
  308. return folditem
  309. }
  310. async function init() {
  311. const panel = await createPanel()
  312. const menu = await createMenu(panel)
  313. const textarea = await createTextArea(panel)
  314. const folditem = await createFoldItem(panel, textarea)
  315. }
  316. const script = document.createElement('script')
  317. script.src = "https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"
  318. script.type = "text/javascript"
  319. document.body.insertBefore(script, document.body.children[0])
  320. script.onload = function () {
  321. // console.log($);
  322. init()
  323. }
  324. })();