废话开篇:最近看到掘金大神自定义网页插件,非常🐮🍺,搜索了一下如何实现 Safari 浏览器插件,发现用 Xcode 新建一个浏览器扩展工程就可以本地实现插件效果。
一、效果展示
效果比较简单,在掘金网站右下角创建了一个泡泡发射器,发射新春快乐这几个字。
二、前期准备
1、Xcode 创建 Safari 浏览器下网页扩展工程。
2、配置插件关联的网站及需要执行的JS文件。
3、编写吐 JS 代码。
4、开启 Safari 个人偏好设置,允许浏览器自定义插件。
5、Safari 加载掘金官网,自动加载并执行插件下的 JS 文件
三、创建 Xcode 网页扩展插件
1、创建 Xcode 工程
首页,选择 macOS ,再选择 Safari 扩展
2、配置插件
找到 manifest.json 文件
修改里面的 content_scripts 参数
js:插件需要执行的 JS 文件。
matches:插件应用的浏览器网址。
3、运行项目
出现上述运行结果后点击下面的按钮,然后自动打开了 Safari 扩展的设置。
四、浏览器设置
为了能显示自定义扩展,需要在 Safari浏览器开发选项下允许浏览器添加未签名的扩展
command + , 打开 Safari 浏览器个人偏好设置
点击编辑网站
这里选择对 掘金官网 允许执行扩展。
五、编写吐泡泡 JS
最后,需要自定义一下 吐泡泡JS脚本,纯js实现,直接上代码
(function createBubble(window){
let bubble = {}
bubble.titleIndex = 0;
bubble.titles = ['新','春','快','乐'];
//初始化
bubble.initBubble = function(){
//获取主页用户名称
let h1s = document.getElementsByTagName('h1');
for(let i = 0;i < h1s.length;i++){
let h1 = h1s[i];
if(h1.className != null && h1.className == "username"){
let title = h1.textContent + '新年快乐';
this.titles = title.split("");
}
}
let launchPadElement = document.createElement('div');
let windowHeight = document.documentElement.clientHeight;
let windowWidth = document.documentElement.clientWidth;
let launchPadElementWidth = 40;
let launchPadElementHeight = 40;
launchPadElement.style.width = launchPadElementWidth + 'px';
launchPadElement.style.height = launchPadElementHeight + 'px';
launchPadElement.style.lineHeight = launchPadElementHeight + 'px';
launchPadElement.style.zIndex = '999';
//取消双击选中效果
launchPadElement.style.webkitUserSelect = 'none';
launchPadElement.style.mozUserSelect = 'none';
launchPadElement.style.msUserSelect = 'none';
launchPadElement.style.borderRadius = launchPadElementWidth / 2.0 + 'px';
launchPadElement.style.position = 'fixed';
launchPadElement.style.top = windowHeight - (launchPadElementHeight * 2) + 'px';
launchPadElement.style.left = windowWidth - (launchPadElementWidth * 2) +'px';
launchPadElement.style.backgroundColor = 'bisque';
launchPadElement.style.textAlign = 'center';
launchPadElement.innerHTML = '🧧'
let that = this;
launchPadElement.onclick = function (){
that.draw(this);
}
let bodys = document.getElementsByTagName('body');
bodys[0].append(launchPadElement);
}
//二次贝塞尔曲线
bubble.twoBezier = function (t, p1, cp, p2){
const [x1, y1] = p1;
const [cx, cy] = cp;
const [x2, y2] = p2;
let x = (1 - t) * (1 - t) * x1 + 2 * t * (1 - t) * cx + t * t * x2;
let y = (1 - t) * (1 - t) * y1 + 2 * t * (1 - t) * cy + t * t * y2;
return [x, y];
}
//绘制bubble
bubble.draw = function(launchPadElement){
let top = launchPadElement.offsetTop;
let left = launchPadElement.offsetLeft;
let distanceLeft = 1000;
let distanceTop = 950;
let p1 = [left,top];
let p2 = [left - distanceLeft,top];
let cp = [left - distanceLeft / 2.0 + Math.random() * 300,top - distanceTop + Math.random() * 400];
let that = this;
var t = 0;
//创建标签
let bubbleElement = document.createElement('canvas');
let windowHeight = document.documentElement.clientHeight;
let windowWidth = document.documentElement.clientWidth;
let bubbleElementWidth = 40;
let bubbleElementHeight = 40;
bubbleElement.style.width = bubbleElementWidth + 'px';
bubbleElement.style.height = bubbleElementHeight + 'px';
bubbleElement.style.position = 'fixed';
bubbleElement.style.top = windowHeight - (bubbleElementHeight * 2) + 'px';
bubbleElement.style.left = windowWidth - (bubbleElementWidth * 2) +'px';
let bodys = document.getElementsByTagName('body');
bodys[0].append(bubbleElement);
//获取画板
var ctx = bubbleElement.getContext("2d");
//锯齿修复
if (window.devicePixelRatio) {
bubbleElement.style.width = bubbleElementWidth + "px";
bubbleElement.style.height = bubbleElementHeight + "px";
bubbleElement.height = bubbleElementHeight * window.devicePixelRatio;
bubbleElement.width = bubbleElementWidth * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}
//绘制泡泡
ctx.strokeStyle = "gray";
ctx.lineWidth = 0.8;
ctx.beginPath();
ctx.arc(bubbleElementWidth / 2.0,bubbleElementHeight / 2.0,bubbleElementWidth / 2.0,0,Math.PI * 2,false);
ctx.stroke();
//绘制文字
ctx.fillStyle = 'red';
ctx.font = 19 + 'px "微软雅黑"';
ctx.textBaseline = "bottom";
ctx.textAlign = "center";
ctx.fillText( this.titles[this.titleIndex],bubbleElementWidth / 2.0,bubbleElementHeight / 1.2);
this.titleIndex += 1;
if(this.titleIndex == this.titles.length){
this.titleIndex = 0;
}
//进行贝塞尔曲线路径绘制
let timer = setInterval(function(){
t += 0.008;
[pointX,pointY] = that.twoBezier(t,p1,cp,p2);
bubbleElement.style.top = pointY + 'px';
bubbleElement.style.left = pointX +'px';
if(t >= 0.9){
//清除定时器及泡泡
clearInterval(timer);
bubbleElement.remove();
}
},20);
}
window.bubble = bubble;
})(window)
//延迟一秒自动执行
setTimeout(function(){
bubble.initBubble()
},1000);
上面就是全部的代码,而作为插件使用,扩展会自动引用并执行。
打开资源审查,可以看到插件的脚本被默认加载了。
这里会在初始化的时候先进行主页用户名称的获取,效果如下:
六、总结与思考
简单的本地自定义 Safari 浏览器插件就完成了。这里只是对系统功能的对接,没什么特别高深的东西,大神勿笑[抱拳][抱拳][抱拳]