您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在贴文右上新增一个悬浮截图按钮,按下后可以对贴文进行截图保存,方便与其他人分享
当前为
- // ==UserScript==
- // @name Floating Screenshot Button for Facebook Posts
- // @name:zh-TW FaceBook 貼文懸浮截圖按鈕
- // @name:zh-CN FaceBook 贴文悬浮截图按钮
- // @namespace http://tampermonkey.net/
- // @version 2.1
- // @description A floating screenshot button is added to the top-right corner of the post. When clicked, it allows users to capture and save a screenshot of the post, making it easier to share with others.
- // @description:zh-TW 在貼文右上新增一個懸浮截圖按鈕,按下後可以對貼文進行截圖保存,方便與其他人分享
- // @description:zh-CN 在贴文右上新增一个悬浮截图按钮,按下后可以对贴文进行截图保存,方便与其他人分享
- // @author chatgpt
- // @match https://www.facebook.com/*
- // @grant none
- // @require https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- let lastRun = 0; // 上次執行時間,用於防抖動
- const debounceDelay = 1000; // 防抖間隔時間(毫秒)
- // 從貼文元素中取得 FB 貼文 ID
- function getFbidFromPost(post) {
- // 嘗試從貼文中超連結擷取 fbid 或 story_fbid
- const links = Array.from(post.querySelectorAll('a[href*="fbid="], a[href*="story_fbid="]'));
- for (const a of links) {
- try {
- const url = new URL(a.href);
- const fbid = url.searchParams.get('fbid') || url.searchParams.get('story_fbid'); // 從網址參數取得 ID
- if (fbid) return fbid;
- } catch (e) {}
- }
- // 嘗試從 data-ft 屬性字串中用正則抓 top_level_post_id
- try {
- const dataFt = post.getAttribute('data-ft');
- if (dataFt) {
- const match = dataFt.match(/"top_level_post_id":"(\d+)"/);
- if (match) return match[1];
- }
- } catch (e) {}
- // 嘗試從當前頁面網址抓 fbid 或 story_fbid 參數
- try {
- const url = new URL(window.location.href);
- const fbid = url.searchParams.get('fbid') || url.searchParams.get('story_fbid');
- if (fbid) return fbid;
- } catch (e) {}
- return 'unknownFBID'; // 如果全部失敗,回傳預設值
- }
- // 監聽頁面 DOM 變動,動態新增截圖按鈕
- const observer = new MutationObserver(() => {
- const now = Date.now();
- if (now - lastRun < debounceDelay) return; // 防抖
- lastRun = now;
- // 尋找所有貼文容器
- document.querySelectorAll('div.x1lliihq').forEach(post => {
- if (post.dataset.sbtn === '1') return; // 已加入按鈕就跳過
- // 找貼文中的按鈕群組容器
- let btnGroup = post.querySelector('div[role="group"]')
- || post.querySelector('div.xqcrz7y')
- || post.querySelector('div.x1qx5ct2');
- if (!btnGroup) return; // 找不到按鈕群組就跳過
- post.dataset.sbtn = '1'; // 標記已加按鈕
- btnGroup.style.position = 'relative'; // 設相對定位,方便絕對定位按鈕
- // 建立截圖按鈕
- const btn = document.createElement('div');
- btn.textContent = '📸'; // 按鈕文字(相機圖示)
- btn.title = '截圖貼文'; // 提示文字
- Object.assign(btn.style, { // 設定按鈕樣式
- position: 'absolute',
- left: '-40px',
- top: '0',
- width: '32px',
- height: '32px',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- borderRadius: '50%',
- backgroundColor: '#3A3B3C',
- color: 'white',
- cursor: 'pointer',
- zIndex: '9999',
- transition: 'background .2s'
- });
- // 滑鼠移入變色
- btn.addEventListener('mouseenter', () => btn.style.backgroundColor = '#4E4F50');
- // 滑鼠移出還原顏色
- btn.addEventListener('mouseleave', () => btn.style.backgroundColor = '#3A3B3C');
- // 按鈕點擊事件,進行截圖
- btn.addEventListener('click', async e => {
- e.stopPropagation(); // 防止事件冒泡
- btn.textContent = '⏳'; // 顯示等待中圖示
- btn.style.pointerEvents = 'none'; // 禁用按鈕避免重複點擊
- try {
- if (!window.html2canvas) throw new Error('html2canvas 尚未載入');
- // 嘗試展開「查看更多」內容
- const seeMoreCandidates = post.querySelectorAll('span, a, div, button');
- let clicked = false;
- for (const el of seeMoreCandidates) {
- const text = el.innerText?.trim() || el.textContent?.trim();
- if (!text) continue;
- if (
- text.includes('查看更多') ||
- text.includes('See more') || text.includes('See More') ||
- text.includes('…更多') || text.includes('more')
- ) {
- try {
- el.click();
- clicked = true;
- console.log('已點擊查看更多:', el);
- } catch (err) {
- console.warn('點擊查看更多失敗:', err);
- }
- }
- }
- if (clicked) {
- await new Promise(r => setTimeout(r, 1000)); // 等待展開完成
- }
- // 滾動到貼文位置
- post.scrollIntoView({ behavior: 'smooth', block: 'center' }); // 滾動貼文至畫面中央
- await new Promise(r => setTimeout(r, 500)); // 等待 0.5 秒讓畫面穩定
- // 建立檔案名稱
- const fbid = getFbidFromPost(post); // 取得貼文 ID
- const nowDate = new Date();
- const pad = n => n.toString().padStart(2, '0');
- const datetimeStr =
- nowDate.getFullYear().toString() +
- pad(nowDate.getMonth() + 1) +
- pad(nowDate.getDate()) + '_' +
- pad(nowDate.getHours()) + '_' +
- pad(nowDate.getMinutes()) + '_' +
- pad(nowDate.getSeconds());
- // 使用 html2canvas 進行截圖
- const canvas = await html2canvas(post, {
- useCORS: true,
- allowTaint: true,
- backgroundColor: '#1c1c1d', //背景色設定,null 透明,'white' 白,'black' 黑
- scale: 2, //畫質設定,建議使用2,或者window.devicePixelRatio
- logging: false,
- foreignObjectRendering: false
- });
- // 建立下載連結並觸發下載
- const filename = `${fbid}_${datetimeStr}.png`; // 檔名格式:FBID_時間.png
- const link = document.createElement('a');
- link.href = canvas.toDataURL('image/png');
- link.download = filename;
- link.click();
- btn.textContent = '✅'; // 成功顯示勾勾
- } catch (err) {
- console.error('截圖錯誤:', err); // 錯誤輸出到 console
- alert('截圖失敗,請稍後再試'); // 跳出錯誤提示
- btn.textContent = '❌'; // 顯示錯誤圖示
- }
- // 1秒後還原按鈕狀態
- setTimeout(() => {
- btn.textContent = '📸';
- btn.style.pointerEvents = 'auto';
- }, 1000);
- });
- btnGroup.appendChild(btn); // 將按鈕加入按鈕群組
- });
- });
- // 監聽整個 body 的 DOM 變化
- observer.observe(document.body, { childList: true, subtree: true });
- })();