文章导出成pdf

将一些主流的网站的文章,去除掉一些无关部分直接启动浏览器自带打印功能

  1. // ==UserScript==
  2. // @name 文章导出成pdf
  3. // @namespace https://github.com/Vanisper/web-article-to-pdf
  4. // @version 1.3.2
  5. // @author Vanisper
  6. // @license MIT
  7. // @icon https://vitejs.dev/logo.svg
  8. // @defaulticon 将一些主流的网站的文章,去除掉一些无关部分直接启动浏览器自带打印功能
  9. // @match https://www.bilibili.com/read/cv*
  10. // @match https://www.cnblogs.com/*/p/*
  11. // @match https://www.cnblogs.com/*/archive/*
  12. // @match https://blog.csdn.net/*/article/details/*
  13. // @match https://www.jianshu.com/p/*
  14. // @match https://juejin.cn/post/*
  15. // @match https://segmentfault.com/a/*
  16. // @match https://mp.weixin.qq.com/s/*
  17. // @match https://mp.weixin.qq.com/s?*
  18. // @match https://zhuanlan.zhihu.com/p/*
  19. // @match https://www.zhihu.com/question/*/answer/*
  20. // @match https://www.zhihu.com/question/*
  21. // @require https://cdn.jsdelivr.net/npm/vue@3.2.47/dist/vue.global.prod.js
  22. // @description 将一些主流的网站的文章,去除掉一些无关部分直接启动浏览器自带打印功能
  23. // ==/UserScript==
  24.  
  25. (e=>{const t=document.createElement("style");t.dataset.source="vite-plugin-monkey",t.textContent=e,document.head.append(t)})(" *{padding:0;margin:0}button[data-v-13bc6f6b]{font-weight:700;color:#fff;border-radius:2rem;width:95.02px;height:42.66px;border:none;background-color:#3653f8;display:flex;justify-content:center;align-items:center}button .span-mother[data-v-13bc6f6b]{display:flex;overflow:hidden}button .span-mother span[data-v-13bc6f6b]{display:flex;justify-content:center;align-items:center}button .span-mother2[data-v-13bc6f6b]{display:flex;position:absolute;overflow:hidden}button .span-mother2 span[data-v-13bc6f6b]{transform:translateY(-1.2em);display:flex;justify-content:center;align-items:center}button:hover .span-mother[data-v-13bc6f6b]{position:absolute}button:hover .span-mother span[data-v-13bc6f6b]{transform:translateY(1.2em)}button:hover .span-mother2 span[data-v-13bc6f6b]{transform:translateY(0)}@keyframes spin-19ff1008{to{transform:rotate(360deg)}}#loading[data-v-19ff1008]{position:fixed;display:flex;top:0;left:0;width:100%;height:100%;background-color:#8ae79d82;z-index:9999}#loading .spinner[data-v-19ff1008]{margin:auto;width:40px;height:40px;border-radius:50%;border:3px solid transparent;border-top-color:#fff;animation:spin-19ff1008 .8s ease infinite}.setpdf[data-v-af70e9ff]{position:fixed;top:100px;right:24px;box-sizing:border-box;cursor:pointer;user-select:none;transition:opacity .2s ease .1s;z-index:9998} ");
  26.  
  27. (function (vue) {
  28. 'use strict';
  29.  
  30. var __defProp = Object.defineProperty;
  31. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  32. var __publicField = (obj, key, value) => {
  33. __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  34. return value;
  35. };
  36. class useDraggable {
  37. // dom初始top
  38. constructor(element) {
  39. __publicField(this, "draggableElement");
  40. __publicField(this, "isDragging");
  41. __publicField(this, "isDraggable");
  42. __publicField(this, "startPosition");
  43. __publicField(this, "currentX");
  44. // dom初始left
  45. __publicField(this, "currentY");
  46. __publicField(this, "startDragging", (event) => {
  47. const elementRect = this.draggableElement.getBoundingClientRect();
  48. this.startPosition = { x: elementRect.x, y: elementRect.y };
  49. this.currentX = elementRect.left;
  50. this.currentY = elementRect.top;
  51. this.isDragging = true;
  52. this.startPosition = {
  53. x: event.clientX,
  54. y: event.clientY
  55. };
  56. window.addEventListener("mousemove", this.dragging);
  57. window.addEventListener("mouseup", this.stopDragging);
  58. });
  59. __publicField(this, "dragging", (event) => {
  60. if (this.isDragging && this.draggableElement) {
  61. const maxLeft = document.documentElement.clientWidth - this.draggableElement.offsetWidth;
  62. const maxTop = document.documentElement.clientHeight - this.draggableElement.offsetHeight;
  63. const offsetX = event.clientX - this.startPosition.x;
  64. const offsetY = event.clientY - this.startPosition.y;
  65. let left = this.currentX + offsetX;
  66. let top = this.currentY + offsetY;
  67. if (offsetX < 0) {
  68. left = Math.max(0, left);
  69. } else {
  70. left = Math.min(maxLeft, left);
  71. }
  72. if (offsetY < 0) {
  73. top = Math.max(0, top);
  74. } else {
  75. top = Math.min(maxTop, top);
  76. }
  77. this.draggableElement.style.position = "fixed";
  78. this.draggableElement.style.top = top + "px";
  79. this.draggableElement.style.left = left + "px";
  80. }
  81. });
  82. __publicField(this, "stopDragging", (event) => {
  83. this.isDragging = false;
  84. window.removeEventListener("mousemove", this.dragging, false);
  85. window.removeEventListener("mouseup", this.stopDragging, false);
  86. });
  87. __publicField(this, "resetPosition", () => {
  88. const elementRect = this.draggableElement.getBoundingClientRect();
  89. this.draggableElement.style.position = "fixed";
  90. if (window.innerWidth < elementRect.right) {
  91. this.draggableElement.style.left = window.innerWidth - elementRect.width + "px";
  92. }
  93. if (window.innerHeight < elementRect.bottom) {
  94. this.draggableElement.style.top = window.innerHeight - elementRect.height + "px";
  95. }
  96. if (elementRect.top < 0) {
  97. this.draggableElement.style.top = "0px";
  98. }
  99. });
  100. this.draggableElement = element;
  101. this.isDraggable = element.getAttribute("draggable") === "true" || element.getAttribute("draggable") === "" ? true : false;
  102. this.isDragging = false;
  103. const elementRect = element.getBoundingClientRect();
  104. this.startPosition = { x: elementRect.x, y: elementRect.y };
  105. this.currentX = elementRect.left;
  106. this.currentY = elementRect.top;
  107. this.init();
  108. }
  109. init() {
  110. var _a;
  111. this.unDraggable();
  112. const elementRect = this.draggableElement.getBoundingClientRect();
  113. this.draggableElement.style.position = "fixed";
  114. this.draggableElement.style.left = elementRect.left + "px";
  115. this.draggableElement.style.top = elementRect.top + "px";
  116. this.draggableElement.style.right = "unset";
  117. window.addEventListener("resize", this.resetPosition);
  118. (_a = this.draggableElement) == null ? void 0 : _a.addEventListener("mousedown", this.startDragging);
  119. }
  120. draggable() {
  121. var _a;
  122. (_a = this.draggableElement) == null ? void 0 : _a.setAttribute("draggable", "true");
  123. this.isDraggable = true;
  124. }
  125. unDraggable() {
  126. var _a;
  127. (_a = this.draggableElement) == null ? void 0 : _a.setAttribute("draggable", "false");
  128. this.isDraggable = false;
  129. }
  130. preventDefault(event) {
  131. event.preventDefault();
  132. }
  133. destroy() {
  134. var _a;
  135. (_a = this.draggableElement) == null ? void 0 : _a.removeEventListener("mousedown", this.startDragging);
  136. window.removeEventListener("resize", this.resetPosition);
  137. }
  138. }
  139. const _hoisted_1$1 = { class: "span-mother" };
  140. const _hoisted_2$1 = { class: "span-mother2" };
  141. const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
  142. __name: "button1",
  143. props: {
  144. text: {
  145. type: String,
  146. default: ""
  147. }
  148. },
  149. setup(__props) {
  150. const props = __props;
  151. vue.ref(props.text.length);
  152. return (_ctx, _cache) => {
  153. return vue.openBlock(), vue.createElementBlock("button", null, [
  154. vue.createElementVNode("span", _hoisted_1$1, [
  155. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.text, (item, index) => {
  156. return vue.openBlock(), vue.createElementBlock("span", {
  157. key: item,
  158. style: vue.normalizeStyle({ transition: `${0.1 * index + 0.1}s` })
  159. }, vue.toDisplayString(item), 5);
  160. }), 128))
  161. ]),
  162. vue.createElementVNode("span", _hoisted_2$1, [
  163. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.text, (item, index) => {
  164. return vue.openBlock(), vue.createElementBlock("span", {
  165. key: item,
  166. style: vue.normalizeStyle({ transition: `${0.1 * index + 0.1}s` })
  167. }, vue.toDisplayString(item), 5);
  168. }), 128))
  169. ])
  170. ]);
  171. };
  172. }
  173. });
  174. const _export_sfc = (sfc, props) => {
  175. const target = sfc.__vccOpts || sfc;
  176. for (const [key, val] of props) {
  177. target[key] = val;
  178. }
  179. return target;
  180. };
  181. const button1 = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-13bc6f6b"]]);
  182. const _sfc_main$1 = {};
  183. const _withScopeId = (n) => (vue.pushScopeId("data-v-19ff1008"), n = n(), vue.popScopeId(), n);
  184. const _hoisted_1 = { id: "loading" };
  185. const _hoisted_2 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "spinner" }, null, -1));
  186. const _hoisted_3 = [
  187. _hoisted_2
  188. ];
  189. function _sfc_render(_ctx, _cache) {
  190. return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, _hoisted_3);
  191. }
  192. const loading1 = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-19ff1008"]]);
  193. const bilibiliRules = [{
  194. url: "https?://www.bilibili.com/read/cv\\w+",
  195. name: "bilibili",
  196. class: "#bili-header-container, div.article-breadcrumb, div.right-side-bar.on.is-mini-page, #comment-wrapper, div.fixed-top-header,#readRecommendInfo, div.interaction-info",
  197. style: `@media print {
  198. #app > div > div.article-container {}
  199. }`
  200. }];
  201. const cnblogsRules = [
  202. {
  203. url: "https?://www\\.cnblogs\\.com/\\w+/(p|archive)/\\w+",
  204. name: "cnblogs",
  205. class: "#top_nav, #header, #sideBar,#blog_post_info,#post_next_prev,#topics > div > div.postDesc,#comment_form,#footer,#top_nav,#header,#mylinks,#mytopmenu,body > div.footer,#leftcontent,#blog_post_info_block,#comment_form",
  206. style: `@media print {
  207. #main {
  208. display: flex !important;
  209. }
  210. #mainContent {
  211. max-width: 1080px!important;
  212. min-width: 720px!important;
  213. width: 100%!important;
  214. }
  215. #post_detail {
  216. display: flex !important;
  217. justify-content: center;
  218. }
  219. }`
  220. }
  221. ];
  222. const csdnRules = [
  223. {
  224. url: "https?://blog\\.csdn\\.net/\\w+/article/details/\\w+",
  225. name: "csdn",
  226. class: "#mainBox > aside, #toolbarBox #csdn-toolbar, body > div:nth-child(49) > div, #toolBarBox, #mainBox > main > div.recommend-box, #recommendNps, #copyright-box, #treeSkill > div, .csdn-side-toolbar, .left-toolbox",
  227. style: `
  228. @media print {
  229. #mainBox > main > div.blog-content-box {
  230. position: absolute; top: 0; left: 0; max-width: 1080px; z-index: 999;
  231. }
  232. main div.blog-content-box pre.set-code-hide {
  233. height: auto; overflow-y: auto;
  234. }
  235. main div.blog-content-box pre.set-code-hide .hide-preCode-box {
  236. display: none;
  237. }
  238. #article_content .markdown_views pre.prettyprint * {
  239. white-space: pre-wrap; word-break: break-word; word-wrap: normal;
  240. }
  241. #toolBarBox, #csdn-toolbar, .recommend-right, .recommend-right1, .blog_container_aside {
  242. display: none !important;
  243. }
  244. }`,
  245. copyrightTarget: ".blog-content-box"
  246. }
  247. ];
  248. const jianshuRules = [{
  249. url: "https?://www.jianshu.com/p/\\w+",
  250. name: "jianshu",
  251. class: "#__next > header, #__next aside,#__next > div._3Pnjry, #__next > footer, #__next > div > div > div._gp-ck > section:nth-child(1) > div._13lIbp, #__next > div > div > div._gp-ck > section:nth-child(2), #__next > div._21bLU4._3kbg6I > div > div._gp-ck > section:nth-child(5),#note-page-comment",
  252. style: `@media print {
  253. #__next > div._21bLU4._3kbg6I > div > div._gp-ck > section:nth-child(1) {
  254. position: absolute; top: 0; left: 0;z-index: 999;
  255. max-width: 1080px;
  256. min-width: 1080px;
  257. width: 1080px;
  258. }
  259. }`
  260. }];
  261. const juejinRules = [
  262. {
  263. url: "https?://juejin.cn/post/\\w+",
  264. name: "juejin",
  265. class: "#juejin > div.view-container > div, #juejin > div.view-container > main > div > div.article-suspended-panel.dynamic-data-ready,#juejin > div.view-container > main > div > div.main-area.article-area > div.wrap.category-course-recommend,#comment-box,#juejin > div.view-container > main > div > div.main-area.recommended-area.shadow,#juejin > div.view-container > main > div > div.recommended-links.main-area,#juejin > div.view-container > main > div > div.sidebar.sidebar,#juejin > div.global-component-box, #juejin > div.view-container > main > div > div.main-area.article-area > div.article-end > div.column-container,#juejin > div.view-container > main > div > div.main-area.article-area > div.article-end > div.extension-banner,#juejin > div.recommend-box,#juejin > div.view-container > main > div > div.main-area.article-area > div.action-box.action-bar",
  266. style: `@media print {
  267. article {
  268. position: absolute; top: 0; left: 0;z-index: 999;
  269. max-width: 1080px;
  270. min-width: 1080px;
  271. width: 1080px;
  272. }
  273. }`
  274. }
  275. ];
  276. const segmentfaultRules = [
  277. {
  278. url: "https?://segmentfault.com/a/\\w+",
  279. name: "segmentfault",
  280. class: ".fix-bottom-action-wrap, nav, .right-side, .sticky-wrap, #comment-area, div.article-content div.card.mt-4",
  281. style: `@media print {
  282. .fmt pre {max-height: unset !important;
  283. }
  284. }`,
  285. copyright: ""
  286. }
  287. ];
  288. const weixinRules = [
  289. {
  290. url: "https?://mp.weixin.qq.com/s(\\?|/)\\w+",
  291. name: "weixin",
  292. class: "#js_base_container > div.rich_media_area_extra, #js_pc_qr_code",
  293. style: `@media print {}`
  294. }
  295. ];
  296. const zhihuRules = [
  297. {
  298. url: "https?://zhuanlan.zhihu.com/p/\\w+",
  299. name: "zhihu",
  300. class: ".Catalog, .ColumnPageHeader-Wrapper, .RichContent-actions, .RichContent-actions, .Post-NormalSub, .Post-SideActions, .complementary, .CornerAnimayedFlex",
  301. style: `@media print {
  302. article > div, article > header {
  303. max-width: 1080px;
  304. min-width: 1080px;
  305. width: 1080px;
  306. }
  307. }`
  308. },
  309. // 知乎提问板块-指定某个回答
  310. {
  311. url: "https?://www.zhihu.com/question/\\d+/answer/\\d+",
  312. name: "zhihu",
  313. class: ".AppHeader, .Question-sideColumn, .ContentItem-actions, .CornerButtons, .MoreAnswers, .ViewAll",
  314. style: `
  315. @media print {
  316. .ListShortcut{
  317. width: 100%;
  318. }
  319. .Question-mainColumn {
  320. width: 100%;
  321. }
  322. }`
  323. },
  324. {
  325. url: "https?://www.zhihu.com/question/\\d+",
  326. name: "zhihu",
  327. class: ".AppHeader, .Question-sideColumn, .ContentItem-actions, .CornerButtons, .MoreAnswers, .ViewAll",
  328. style: `
  329. @media print {
  330. .ListShortcut{
  331. width: 100%;
  332. }
  333. .Question-mainColumn {
  334. width: 100%;
  335. }
  336. }`,
  337. hideDefault: true,
  338. javascript: () => {
  339. var _a;
  340. const listItem = (_a = document.querySelector(".AnswersNavWrapper")) == null ? void 0 : _a.querySelectorAll(".List-item");
  341. if (!listItem)
  342. return false;
  343. const printItem = (doc, url) => {
  344. if (!doc)
  345. return false;
  346. window.open(
  347. url,
  348. "_blank",
  349. "width=1080,height=800,menubar=yes,scrollbars=yes,resizable=yes"
  350. );
  351. };
  352. listItem.forEach((item, index) => {
  353. var _a2, _b;
  354. const box = item.querySelector(".AnswerItem-authorInfo");
  355. const name = (_b = (_a2 = item.querySelector(".AnswerItem")) == null ? void 0 : _a2.attributes.getNamedItem("name")) == null ? void 0 : _b.value;
  356. if (!box || !name)
  357. return false;
  358. const printBtn = document.createElement("button");
  359. printBtn.innerHTML = "打印当前回答";
  360. printBtn.className = "print-btn_" + index;
  361. const url = window.location.href + `/answer/${name}`;
  362. printBtn.onclick = () => {
  363. printItem(item, url);
  364. };
  365. box.append(printBtn);
  366. });
  367. }
  368. }
  369. ];
  370. const rules = [
  371. ...bilibiliRules,
  372. ...cnblogsRules,
  373. ...csdnRules,
  374. ...jianshuRules,
  375. ...juejinRules,
  376. ...segmentfaultRules,
  377. ...weixinRules,
  378. ...zhihuRules
  379. ];
  380. const _sfc_main = /* @__PURE__ */ vue.defineComponent({
  381. __name: "App",
  382. setup(__props) {
  383. const dragDomRef = vue.ref();
  384. let draggableInstance;
  385. const isShow = vue.ref(false);
  386. function preventDefault(e) {
  387. e.preventDefault();
  388. }
  389. const action = () => {
  390. stopScroll.value = false;
  391. isShow.value = true;
  392. window.addEventListener("wheel", preventDefault, { passive: false });
  393. window.scrollTo(0, 0);
  394. smoothScrollToBottom();
  395. };
  396. const stopScroll = vue.ref(false);
  397. const stop = () => {
  398. stopScroll.value = true;
  399. };
  400. function smoothScrollToBottom() {
  401. var scrollHeight = document.body.scrollHeight;
  402. var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
  403. var clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
  404. if (stopScroll.value) {
  405. isShow.value = false;
  406. window.removeEventListener("wheel", preventDefault);
  407. return;
  408. }
  409. if (scrollTop + clientHeight >= scrollHeight) {
  410. isShow.value = false;
  411. window.removeEventListener("wheel", preventDefault);
  412. setTimeout(() => {
  413. window.print();
  414. }, 200);
  415. return;
  416. } else {
  417. window.scrollTo({
  418. top: scrollTop + clientHeight,
  419. behavior: "auto"
  420. // 可以设置成光滑的
  421. });
  422. setTimeout(smoothScrollToBottom, 1e3);
  423. }
  424. }
  425. const currentUrl = window.location.href;
  426. let curr = {
  427. url: "",
  428. name: "",
  429. class: "",
  430. style: "",
  431. /** 版权信息将插入的css选择器 */
  432. target: "body"
  433. };
  434. const flag = vue.ref(false);
  435. vue.onMounted(() => {
  436. flag.value = rules.some((e) => {
  437. var reg = new RegExp(e.url);
  438. if (reg.test(currentUrl)) {
  439. curr.url = currentUrl;
  440. curr.name = e.name;
  441. curr.class = e.class;
  442. curr.style = e.style;
  443. curr.target = e.copyrightTarget || "body";
  444. curr.js = e.javascript;
  445. curr.hideDefault = e.hideDefault;
  446. return true;
  447. }
  448. });
  449. if (flag.value) {
  450. var style2 = document.createElement("style");
  451. style2.innerHTML = `@media print {${curr.class}, .mod, button.setpdf, .web-article-to-pdf { display: none!important;} div.setpdf-copyright { display: block!important; }} ${curr.style}`;
  452. document.head.appendChild(style2);
  453. const target = document.querySelector(curr.target);
  454. const copyright = document.createElement("div");
  455. copyright.setAttribute("style", "display:none");
  456. copyright.setAttribute("class", "setpdf-copyright");
  457. copyright.appendChild(document.createTextNode("来源:"));
  458. const link = document.createElement("a");
  459. link.textContent = currentUrl;
  460. link.setAttribute("href", currentUrl);
  461. copyright.appendChild(link);
  462. target.appendChild(copyright);
  463. setTimeout(() => {
  464. draggableInstance = new useDraggable(dragDomRef.value.$el);
  465. curr.js && (() => {
  466. curr.js();
  467. })();
  468. }, 200);
  469. curr.hideDefault && (flag.value = false);
  470. }
  471. });
  472. vue.onUnmounted(() => {
  473. draggableInstance.destroy();
  474. });
  475. return (_ctx, _cache) => {
  476. return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
  477. vue.withDirectives(vue.createVNode(button1, {
  478. onClick: action,
  479. class: "setpdf",
  480. text: "导出PDF",
  481. draggable: "true",
  482. ref_key: "dragDomRef",
  483. ref: dragDomRef
  484. }, null, 512), [
  485. [vue.vShow, flag.value]
  486. ]),
  487. vue.withDirectives(vue.createVNode(loading1, { onDblclick: stop }, null, 512), [
  488. [vue.vShow, flag.value && isShow.value]
  489. ])
  490. ], 64);
  491. };
  492. }
  493. });
  494. const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-af70e9ff"]]);
  495. vue.createApp(App).mount(
  496. (() => {
  497. const app = document.createElement("div");
  498. app.classList.add("web-article-to-pdf");
  499. document.body.append(app);
  500. return app;
  501. })()
  502. );
  503.  
  504. })(Vue);

QingJ © 2025

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