文本大爆炸

仿照锤子的大爆炸,对选中文本进行分词

  1. // ==UserScript==
  2. // @name 文本大爆炸
  3. // @namespace http://tampermonkey.net/
  4. // @author 突徒土兔
  5. // @version 3.1
  6. // @description 仿照锤子的大爆炸,对选中文本进行分词
  7. // @match *://*/*
  8. // @license CC-BY-NC-4.0
  9. // @icon https://s2.loli.net/2024/09/25/6PxlMHA7EZVqwsJ.png
  10. // @require https://cdn.jsdelivr.net/npm/segmentit@2.0.3/dist/umd/segmentit.js
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. // 触发分词按钮
  17. let button = null;
  18. // 弹出窗口
  19. let popupContainer = null;
  20. // 分词器
  21. const segmentit = Segmentit.useDefault(new Segmentit.Segment());
  22. // 是否处于拖动
  23. let isDragging = false;
  24. // 开始拖动的元素
  25. let startElement = null;
  26.  
  27. /**
  28. * 创建样式
  29. */
  30. function createStyles() {
  31. const style = document.createElement("style");
  32. style.textContent = `
  33. .word-explosion-button {
  34. position: absolute;
  35. background-color: rgba(255,255,255, 0.4);
  36. color: #000;
  37. border: none;
  38. border-radius: 50%;
  39. cursor: pointer;
  40. font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  41. font-size: 16px;
  42. box-shadow: 0 2px 10px rgba(0,0,0,0.15);
  43. transition: all 0.3s ease;
  44. z-index: 9999;
  45. width: 30px;
  46. height: 30px;
  47. display: flex;
  48. justify-content: center;
  49. align-items: center;
  50. }
  51. .word-explosion-button:hover {
  52. background-color: rgba(255,255,255, 0.75);
  53. box-shadow: 0 4px 20px rgba(0,0,0,0.25);
  54. transform: scale(1.1);
  55. transition: transform 0.3s ease;
  56. }
  57. .word-explosion-popup {
  58. position: fixed;
  59. top: 50%;
  60. left: 50%;
  61. transform: translate(-50%, -50%);
  62. background-color: rgba(240, 240, 240, 0.8);
  63. backdrop-filter: blur(10px);
  64. padding: 20px;
  65. border-radius: 10px;
  66. box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
  67. z-index: 10000;
  68. max-width: 80%;
  69. max-height: 80%;
  70. overflow: auto;
  71. font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  72. display: flex;
  73. flex-wrap: wrap;
  74. justify-content: center;
  75. align-items: center;
  76. opacity: 0;
  77. animation: fadeIn 0.5s ease forwards;
  78. }
  79.  
  80. @keyframes fadeIn {
  81. from {
  82. opacity: 0;
  83. transform: translate(-50%, -50%) scale(0.9);
  84. }
  85. to {
  86. opacity: 1;
  87. transform: translate(-50%, -50%) scale(1);
  88. }
  89. }
  90. .word-explosion-word {
  91. margin: 2px;
  92. height: 30px; /* 让所有该类所有对象都有一样的高度 */
  93. padding: 4px 8px;
  94. background-color: rgba(255, 255, 255, 0.5);
  95. border: none;
  96. border-radius: 10px;
  97. cursor: pointer;
  98. transition: all 0.3s ease;
  99. font-size: 14px;
  100. display: inline-flex;
  101. align-items: center;
  102. }
  103. .word-explosion-word.selected {
  104. background-color: #0078D4;
  105. color: white;
  106. }
  107. .word-explosion-copy {
  108. display: block;
  109. margin-top: 15px;
  110. padding: 10px 20px;
  111. background-color: #0078D4;
  112. color: white;
  113. border: none;
  114. border-radius: 20px;
  115. cursor: pointer;
  116. font-size: 14px;
  117. transition: all 0.3s ease;
  118. }
  119. .word-explosion-copy:hover {
  120. background-color: #106EBE;
  121. }
  122. `;
  123. document.head.appendChild(style);
  124. }
  125.  
  126. /**
  127. * 创建按钮
  128. * 该函数用于在页面中创建一个按钮,并将其添加到文档的 body 中。
  129. * 按钮的初始状态是隐藏的。
  130. */
  131. function createButton() {
  132. // 创建一个新的按钮元素
  133. button = document.createElement("button");
  134. // 设置按钮的文本内容
  135. button.textContent = "🔨";
  136. // 设置按钮的类名
  137. button.className = "word-explosion-button";
  138. // 设置按钮的初始显示状态为隐藏
  139. button.style.display = "none";
  140. // 将按钮添加到文档的 body 中
  141. document.body.appendChild(button);
  142. }
  143.  
  144. /**
  145. * 显示按钮并将其定位到选中文本旁边
  146. * 该函数用于在用户选择文本后,显示按钮并将按钮定位到选中文本的旁边。
  147. */
  148. function showButtonAtSelection() {
  149. // 获取当前的文本选择对象
  150. const selection = window.getSelection();
  151. // 检查是否有选中的文本
  152. if (selection.rangeCount > 0) {
  153. // 获取选中的第一个范围
  154. const range = selection.getRangeAt(0);
  155. // 获取选中范围的边界矩形
  156. const rect = range.getBoundingClientRect();
  157. // 设置按钮的顶部位置为选中范围的底部加上滚动条的偏移量
  158. button.style.top = `${rect.bottom + window.scrollY + 5}px`;
  159. // 设置按钮的左侧位置为选中范围的左侧加上滚动条的偏移量
  160. button.style.left = `${rect.left + window.scrollX}px`;
  161. // 显示按钮
  162. button.style.display = "block";
  163. }
  164. }
  165.  
  166. /**
  167. * 隐藏按钮
  168. * 该函数用于隐藏按钮。
  169. */
  170. function hideButton() {
  171. // 将按钮的显示状态设置为隐藏
  172. button.style.display = "none";
  173. }
  174.  
  175. /**
  176. * 创建弹出窗口
  177. * 该函数用于创建一个弹出窗口,并将其添加到文档的 body 中。
  178. * 弹出窗口的初始状态是隐藏的。
  179. */
  180. function createPopup() {
  181. // 创建一个新的 div 元素作为弹出窗口的容器
  182. popupContainer = document.createElement("div");
  183. // 设置弹出窗口的类名为 "word-explosion-popup"
  184. popupContainer.className = "word-explosion-popup";
  185. // 设置弹出窗口的初始显示状态为隐藏
  186. popupContainer.style.display = "none";
  187. // 将弹出窗口添加到文档的 body 中
  188. document.body.appendChild(popupContainer);
  189.  
  190. // 添加事件监听器,用于实现拖动选择功能
  191. popupContainer.addEventListener("mousedown", onMouseDown);
  192. document.addEventListener("mousemove", onMouseMove);
  193. document.addEventListener("mouseup", onMouseUp);
  194.  
  195. // 添加事件监听器,用于隐藏弹出窗口
  196. document.addEventListener("click", (event) => {
  197. // 如果点击事件的目标不在弹出窗口内且不在按钮内,则隐藏弹出窗口
  198. if (
  199. !popupContainer.contains(event.target) &&
  200. !button.contains(event.target)
  201. ) {
  202. hidePopup();
  203. }
  204. });
  205. }
  206.  
  207. /**
  208. * 显示弹出窗口
  209. * 该函数用于显示弹出窗口,并将分词结果显示在弹出窗口中。
  210. * @param {Array} words - 分词结果数组
  211. */
  212. function showPopup(words) {
  213. // 清空弹出窗口的内容
  214. popupContainer.innerHTML = "";
  215. // 遍历分词结果数组
  216. words.forEach((word) => {
  217. // 创建一个新的按钮元素
  218. const wordButton = document.createElement("button");
  219. // 设置按钮的文本内容为分词结果
  220. wordButton.textContent = word;
  221. // 设置按钮的类名为 "word-explosion-word"
  222. wordButton.className = "word-explosion-word";
  223. // 为按钮添加点击事件监听器,用于切换 "selected" 类
  224. wordButton.addEventListener("click", () =>
  225. wordButton.classList.toggle("selected")
  226. );
  227. // 将按钮添加到弹出窗口中
  228. popupContainer.appendChild(wordButton);
  229. });
  230.  
  231. // 创建一个新的按钮元素,用于复制选中的文本
  232. const copyButton = document.createElement("button");
  233. // 设置按钮的文本内容为 "复制选中文本"
  234. copyButton.textContent = "复制选中文本";
  235. // 设置按钮的类名为 "word-explosion-copy"
  236. copyButton.className = "word-explosion-copy";
  237. // 设置按钮的宽度为 100%
  238. copyButton.style.width = "100%";
  239. // 为按钮添加点击事件监听器,用于复制选中的文本
  240. copyButton.addEventListener("click", copySelectedWords);
  241. // 将按钮添加到弹出窗口中
  242. popupContainer.appendChild(copyButton);
  243.  
  244. // 显示弹出窗口
  245. popupContainer.style.display = "flex";
  246. }
  247.  
  248. /**
  249. * 隐藏弹出窗口
  250. * 该函数用于隐藏弹出窗口。
  251. */
  252. function hidePopup() {
  253. // 将弹出窗口的显示状态设置为隐藏
  254. popupContainer.style.display = "none";
  255. }
  256.  
  257. /**
  258. * 分词函数
  259. * 该函数用于对输入的文本进行分词,并返回分词结果数组。
  260. * @param {string} text - 需要分词的文本
  261. * @returns {Array} - 分词结果数组
  262. */
  263. function wordExplosion(text) {
  264. // 使用 segmentit 库对文本进行分词,并提取分词结果
  265. let result = segmentit.doSegment(text).map((item) => item.w);
  266. // 初始化一个空数组来存储带有空格的分词结果
  267. let newResult = [];
  268. // 分词过程中丢失了空格
  269. // 遍历原始文本,插入空格
  270. let textIndex = 0;
  271. for (let i = 0; i < result.length; i++) {
  272. newResult.push(result[i]);
  273. textIndex += result[i].length;
  274. while (textIndex < text.length && text[textIndex] === " ") {
  275. newResult.push(" ");
  276. textIndex++;
  277. }
  278. }
  279. // 在控制台输出分词结果
  280. console.log(`分词结果:\n${newResult}`);
  281. // 返回分词结果数组,如果结果为空则返回空数组
  282. return newResult || [];
  283. }
  284.  
  285. /**
  286. * 复制选中的单词
  287. * 该函数用于将选中的单词复制到剪贴板,并弹出提示。
  288. */
  289. function copySelectedWords() {
  290. // 获取所有选中的单词按钮
  291. const selectedWords = Array.from(
  292. popupContainer.querySelectorAll(".word-explosion-word.selected")
  293. )
  294. // 提取每个按钮的文本内容
  295. .map((button) => button.textContent)
  296. // 将所有选中的单词连接成一个字符串
  297. .join("");
  298. // 将选中的单词复制到剪贴板
  299. navigator.clipboard
  300. .writeText(selectedWords)
  301. .then(() => {
  302. // 复制成功后弹出提示
  303. alert(`已复制:\n${selectedWords}`);
  304. })
  305. .catch((err) => {
  306. // 复制失败时在控制台输出错误信息
  307. console.error("复制失败: ", err);
  308. });
  309. }
  310.  
  311. /**
  312. * 监听选择事件
  313. * 该函数用于监听用户的选择事件,并在用户选择文本后显示按钮。
  314. */
  315. document.addEventListener("selectionchange", function () {
  316. // 获取当前的文本选择对象
  317. const selection = window.getSelection();
  318. // 检查选中的文本是否不为空
  319. if (selection.toString().trim() !== "") {
  320. // 显示按钮并将其定位到选中文本旁边
  321. showButtonAtSelection();
  322. } else {
  323. // 隐藏按钮
  324. hideButton();
  325. }
  326. });
  327.  
  328. /**
  329. * 监听按钮点击事件
  330. * 该函数用于在用户点击按钮后,对选中的文本进行分词,并显示弹出窗口。
  331. */
  332. function onButtonClick() {
  333. // 获取当前的文本选择对象
  334. const selection = window.getSelection();
  335. // 获取选中的文本
  336. const text = selection.toString();
  337. // 对选中的文本进行分词
  338. const words = wordExplosion(text);
  339. // 显示弹出窗口并将分词结果显示在弹出窗口中
  340. showPopup(words);
  341. // 隐藏按钮
  342. hideButton();
  343. }
  344.  
  345. let longPressTimer = null;
  346. const longPressThreshold = 200; // 长按阈值,单位为毫秒
  347.  
  348. /**
  349. * 处理鼠标按下事件
  350. * 该函数用于处理鼠标按下事件,实现长按选择功能。
  351. * @param {MouseEvent} event - 鼠标按下事件对象
  352. */
  353. function onMouseDown(event) {
  354. // 检查鼠标按下的目标是否为单词按钮
  355. if (event.target.classList.contains("word-explosion-word")) {
  356. // 设置一个定时器,用于检测长按操作
  357. longPressTimer = setTimeout(() => {
  358. // 如果长按时间超过阈值,则开始拖动选择
  359. isDragging = true;
  360. // 记录开始拖动的元素
  361. startElement = event.target;
  362. // 为开始拖动的元素添加 "selected" 类
  363. startElement.classList.add("selected");
  364. }, longPressThreshold);
  365. }
  366. }
  367.  
  368. /**
  369. * 处理鼠标移动事件
  370. * 该函数用于处理鼠标移动事件,实现拖动选择功能。
  371. * @param {MouseEvent} event - 鼠标移动事件对象
  372. */
  373. function onMouseMove(event) {
  374. // 检查是否正在进行拖动选择
  375. if (isDragging && startElement) {
  376. // 获取当前鼠标位置下的元素
  377. const currentElement = document.elementFromPoint(
  378. event.clientX,
  379. event.clientY
  380. );
  381. // 检查当前元素是否为单词按钮且不是开始拖动的元素
  382. if (
  383. currentElement &&
  384. currentElement.classList.contains("word-explosion-word") &&
  385. currentElement !== startElement
  386. ) {
  387. // 为当前元素添加 "selected" 类
  388. currentElement.classList.add("selected");
  389. }
  390. }
  391. }
  392.  
  393. /**
  394. * 处理鼠标松开事件
  395. * 该函数用于处理鼠标松开事件,结束拖动选择功能。
  396. * @param {MouseEvent} event - 鼠标松开事件对象
  397. */
  398. function onMouseUp(event) {
  399. // 清除长按定时器
  400. clearTimeout(longPressTimer);
  401. // 结束拖动选择
  402. isDragging = false;
  403. // 清空开始拖动的元素
  404. startElement = null;
  405. }
  406.  
  407. /**
  408. * 初始化脚本
  409. * 该函数用于初始化脚本,创建样式、按钮和弹出窗口,并添加事件监听器。
  410. */
  411. function init() {
  412. // 创建样式
  413. createStyles();
  414. // 创建按钮
  415. createButton();
  416. // 创建弹出窗口
  417. createPopup();
  418. // 为按钮添加点击事件监听器
  419. button.addEventListener("click", onButtonClick);
  420. }
  421.  
  422. // 初始化脚本
  423. init();
  424. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址