前端仔尝试开发的浏览器插件(页面亮度调节)

浏览器亮度调节

支持调整浏览器中某个页面的亮度;而不需要调整整块屏幕的亮度。

gif图中的是自己开发的 塔防小游戏 感兴趣可以玩玩,要是喜欢可以给个star Github

前言

作为一名小前端,想着浏览器插件技术要求也只需:html,css,js,就萌生了开发一个简单插件试试水的想法,权当丰富技能了。这是第一次尝试开发的浏览器插件比较简陋,轻喷。

使用场景

  1. 当你觉得某个页面太亮的时候,有些时候的页面是一片白色等浅色背景的,会让你觉得很刺眼,如果使用暗色主题的插件,又会影响到页面其他地方的样式。

  2. 当你的页面在做正经工作(摸鱼)时,你可以把这个标签页提出来,开启该插件,把亮度调低,这样就能让别人从其他角度难以看到你该页面中的内容。(屏幕其他地方还是亮的,一般人的眼睛都会被亮的地方所吸引)

插件使用方法

下载该文件的压缩包,地址点这里

然后打开 管理扩展插件,或者在浏览器地址中直接输入: chrome://extensions/

选择解压后的压缩包的文件夹就可以直接使用了。

插件开发流程

思路

在浏览器页面中设置 fixed 定位一个盒子上去,使用 boxShadow 作为遮罩层,调节 rgab(0,0,0,opacity) 中的透明度作为调节亮度。

结构

写一个插件结构其实很简单(没接触前我还以为会很麻烦),要是没什么特别需求,其实只需要一个 js 文件和一个 manifest.json 的配置文件就可以了。

配置 json

创建 manifest.json 文件

json 复制代码
{
  "name": "browser-mask",
  "description": "Provide a mask layer for the browser.",
  "version": "1.0.1",
  "manifest_version": 3,
  "action": {
    "default_title": "浏览器遮罩层",
    "default_popup": "src/popup.html",
    "default_icon": "images/icon.png"
  },
  "icons": {
    "16": "images/icon.png"
  },
  "permissions": ["storage", "scripting", "activeTab"]
}

最上面三个属性跟 package.json 一样没什么好说的。

  • manifest_version: 这个是 manifest 的版本,2版本的不支持上架了Chrome了。

  • action.default_title: 配置右上角图标标题(鼠标hover就能看到)。

  • action.default_popup: 点击右上角图标打开的那个弹出层页面。

  • action.default_icon: 右上角图标。

  • icons: 这个是管理扩展插件页面中的图标。浏览器地址输入: chrome://extensions/ 直接进入

  • permissions: 开启一些 api 的权限。分别是:storage 开启缓存api;scripting 是开启往页面注入 script 代码的api;activeTab 用于开启标签页api。

popup.js (主要代码)

这是该插件的主要样式了。

html css 没什么好说的,跟正常写一个网页那样。

js 也和写原生大体相同,只是需要注意一些 api 的使用,以及注意变量的作用域等。

插件开始执行

首先检查内存中是否有 opacity 字段,没有就赋值,有的话就给进度条和进度文本赋值。至于这里为什么要用 storage 除了需要缓存之前的值外,还有其他原因,openMask 中会介绍到。

js 复制代码
/** 初始的透明度 */
const INIT_OPACITY = 40;

const progress = document.querySelector('#progress')
const progressValue = document.querySelector('#progressValue')

// ... 其他的一些缓存值,不多叙述了
chrome.storage.sync.get('opacity', ({ opacity: opa }) => {
  if(!opa) {
    chrome.storage.sync.set({ opacity: INIT_OPACITY })
  } else {
    progress.value = opa
    progressValue.textContent = opa
  }
});

打开遮罩层

点击打开按钮,获取到的 tab 就是当前浏览器激活选中的标签页面,然后给这个标签页执行 executeOpenMask 注入执行函数 。

js 复制代码
// #open 是打开按钮的 id 
const openBtn = document.querySelector('#openBtn')
openBtn.addEventListener('click', async () => {
  const tabId = await getTabId();
  const isOpen = await getStorageSync('isOpen')
  if(!isOpen) { // 打开mask
    executeOpenMask(tabId)
  } else { // 关闭mask
    chrome.scripting.executeScript({
      target: { tabId },
      func: closeMask,
    });
  }
  openBtn.textContent = isOpen ? '打开' : '关闭'
  chrome.storage.sync.set({isOpen: !isOpen})
})

/** 获取当前点击的tabId */
async function getTabId() {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  return tab.id
}

/** 获取storage */
function getStorageSync(key) {
  return new Promise((resolve) => {
    chrome.storage.sync.get(key, (item) => {
      resolve(item[key])
    })
  })
}

executeOpenMask

获取 maskInfo(用于存放一些遮罩层的信息,用于缓存而已),往当前标签页中注入 openMask 执行函数,已经传递 maskInfo 参数;再注入 mask.css 的css代码,感兴趣可以直接看源码,就不占文章篇幅了。

js 复制代码
/** 注入打开 mask 的代码 */
async function executeOpenMask(tabId) {
  const maskInfo = await getStorageSync('maskInfo')
  // 获取当前激活的tab的id,注入执行 func
  chrome.scripting.executeScript({
    target: { tabId }, 
    // 这里是传递给 func 的参数
    args: [maskInfo], 
    func: openMask
  });
  chrome.scripting.insertCSS({
    target: { tabId },
    // 这里的路径要按项目根目录开始
    files: ['src/mask.css'] 
  });
  if(maskInfo.isMove) {
    setTimeout(() => {
      chrome.scripting.executeScript({
        target: { tabId },
        args: [maskInfo],
        func: openMaskMove,
      });
    }, 10);
  }
}

openMask

打开遮罩层,这里需要从 storage 中读取 opacity;css 中通过 var(--opacity) 就可获取到值。

css 复制代码
/* mask.css */
#maskWrap {
  box-shadow: 50vmax 50vmax 0 50vmax rgba(0, 0, 0, var(--opacity, 0.4));
}
js 复制代码
function openMask(maskInfo) {
  chrome.storage.sync.get('opacity', ({ opacity }) => {
    const maskWrap = document.createElement('div')
    maskWrap.setAttribute('id', 'maskWrap')
    maskWrap.style.transform = `translate(${maskInfo.x}px, ${maskInfo.y}px)`
    // 给 maskWrap 添加变量
    maskWrap.style.setProperty('--size', 22)
    maskWrap.style.setProperty('--opacity', opacity / 100)
    document.body.appendChild(maskWrap)
  });
}

因为 openMask 是注入的一个函数,存在作用域的问题,是无法读取到外部定义的 opacity。不要问我怎么知道的,因为我刚开始就是这样写的...

js 复制代码
// 这种方式是不正确的,无法使用
let opacity = 40;
function openMask() {
  const maskWrap = document.createElement('div')
  maskWrap.style.setProperty('--opacity', opacity / 100)
  // ....
}

增(减)亮度

增加亮度就是监听增加按钮的点击事件,点击时获取 storage,然后调用 changeOpacity 函数(用来设值和改变界面ui的)即可。

js 复制代码
document.querySelector('.add').addEventListener('click', () => {
  chrome.storage.sync.get('opacity', ({ opacity }) => {
    changeOpacity(opacity + 10)
  });
})

首先是修改当前 popup 中 dom 的进度条和进度条的值,然后新的设置 storage。

接着就是修改标签页中的亮度(遮罩层透明度),注入一个触发的函数。

js 复制代码
function changeOpacity(newOpacity) {
  let opacity = Math.min(Math.max(Math.round(newOpacity), 0), 100);
  progress.value = opacity
  progressValue.textContent = opacity
  chrome.storage.sync.set({ opacity })
  chrome.tabs.query({ active: true, currentWindow: true }).then(([tab]) => {
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      args: [opacity],
      func: injectChangeOpacity,
    });
  })
}
function injectChangeOpacity(opacity) {
  const maskDiv = document.querySelector('#mask')
  if(maskDiv) {
    maskDiv.style.boxShadow = `0 0 0 100vmax rgba(0, 0, 0, ${opacity / 100})`
  }
}

拖动进度条,遮罩层移动等操作就基本只是js的知识了,只需要加些拖动等的事件监听就好了,跟浏览器插件知识关系不大,这里就不一一叙述了,想知道的话可以看源码。

至此这个简陋的插件,大体内容就这些了。

待解决的问题

  1. 当打开遮罩后,刷新页面不会重新打开遮罩,需要点击关闭再重新打开,(记录每个页面对应遮罩是否打开)。

  2. 移动遮罩层时需要完善宽高问题(目前采用100vmax(浏览器可见区域宽高的最大值))。

  3. 遮罩层需要支持缩放,可控制来遮罩页面固定区域。

因为本人刚接触,不太熟悉浏览器插件的技术,第1点实现起来有点困难;

对于2,3点,由于是采用 box-shadow 来遮罩页面的,宽高的不同调整 box-shadow 暂时没什么思路。

相关推荐
deli00710 分钟前
1 个汉字算几个 token?实测中英文 prompt 与代码的编码差异
前端
无限聚核16 分钟前
抖音 certificate.prepare 之后怎么验券?和美团 msconsume 差在哪,无限聚核
前端·微信小程序
2603_9658966226 分钟前
Grid 网格布局|CSS 二维页面排版体系
前端·css
有味道的男人41 分钟前
搜好货平台关键词商品搜索接口:分页容错与商品数据轻量化提取方案
服务器·前端·javascript
lovingsoft1 小时前
Chrome Lighthouse 详解
前端·chrome
Qlly2 小时前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝2 小时前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦2 小时前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡2 小时前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI2 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战