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

浏览器亮度调节

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

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)
  });
})

首先是修改当前 popupdom 的进度条和进度条的值,然后新的设置 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 暂时没什么思路。

相关推荐
cy玩具14 分钟前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf41 分钟前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据1 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161771 小时前
防抖函数--应用场景及示例
前端·javascript
334554321 小时前
element动态表头合并表格
开发语言·javascript·ecmascript
John.liu_Test1 小时前
js下载excel示例demo
前端·javascript·excel
Yaml42 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事2 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶2 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo2 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx