我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛”
中秋创意投稿大赛
一年一度中秋佳节至.
偶然看到掘金的中秋活动, 又要和技术挂钩, 又要和中秋挂钩, 很多文章都写烂了....
「文章本天成, 妙手偶得之」
突然发现, 掘金的页面实在太简单了, 说错话了, 是简洁优雅, 隽永大气.
然后趁此活动机会, 我就来帮掘金大大换个皮肤吧~
技术选型:
chrome插件开发
本文乃初级chrome开发, 同时涉及到一些canvas知识, 各位看官觉得操作太辣了勿怪!!!
看一下效果:
准备工序
首先, 开发一个chrome插件, 最重要的就是他的清单文件manifest.json
.
manifest_version
清单版本, 这里写2就完事了.
browser_action
这个是插件显示的一些配置.
default_icon
是默认图标. 这里我用的是一个小苹果的图标.
default_title
这个默认标题.
default_popup
这个是我们这个插件的默认页面.
content_scripts
这里是向页面中注入css
、js
代码.
matches
这个配置项, 就是我们这个插件生效的页面, 我这里只匹配掘金的域名https://juejin.cn/*
.
js
是向页面注入的js代码.我这里引入了一个自己的代码js/content-script.js
和js/jquery.min.js
.
css
是向页面注入的css代码, 一般来说, 这里的样式表的优先级是很高的, 再高可以使用!important
.
run_at
这个是我们注入的脚本运行的时机, 这里document_end
指的是页面加载完成之后.
下面是我的的插件清单:
{
"manifest_version": 2,
"name": "jueJinSkin",
"version": "0.0.1",
"description": "中秋活动-掘金",
"icons": {
"16": "assets/icon.png",
"48": "assets/icon.png",
"128": "assets/icon.png"
},
"browser_action": {
"default_icon": "assets/icon.png",
"default_title": "中秋到了",
"default_popup": "index.html"
},
"content_scripts": [{
"matches": ["https://juejin.cn/*"],
"js": ["js/jquery.min.js", "js/content-script.js"],
"css": ["css/index.css"],
"run_at": "document_end"
}]
}
分析并修改掘金页面
我们想要给一个页面换皮, 一定要仔细分析他的页面的dom结构, 并找出我们需要修改的唯一节点.
例如, 我们找一个最大的背景.
然后注入css修改.
#juejin {
background: url("//yun.duiba.com.cn/aurora/assets/a73036f55b0c256849761eb2c8cb30e4a839d63e.jpeg") repeat-y !important;
background-size: cover;
}
这些所有的页面修改都是傻瓜操作了, 唯一需要注意的就是你的设计能力和审美观念.
接下来, 我们来给我们自己的头像加一个玉兔的边框.
这里我们需要创建一个div
并插入到头像的盒子.
function changeDom() {
let appLink = $(".main-nav").find(".nav-list").find(".menu").get(0);
var border = document.createElement("div");
border.classList = "avatar-con";
appLink.append(border);
}
然后给avatar-con
赋予样式.
.avatar-con{
position: absolute;
top: 46%;
left: 67%;
width: 4rem;
height: 4rem;
transform: translateX(-50%) translateY(-50%);
background: url("//yun.duiba.com.cn/aurora/assets/465d1b221a4a0845df5474fe2a6254d18707a154.png") no-repeat center/100%;
pointer-events: none;
}
这样子我们就成功给头像添加了一个小兔子边框了.
其他的操作都是类似的, 比如我们给点赞、评论等按钮添加中秋元素的边框.
//文章左边点评
function changeTextLeftBtn() {
var btnArr = [];
btnArr.push($(".like-btn"));
btnArr.push($(".comment-btn"));
btnArr.push($(".collect-btn"));
btnArr.push($(".report-btn"));
btnArr.push($(".weibo-btn"));
btnArr.push($(".qq-btn"));
btnArr.push($(".wechat-btn"));
btnArr.forEach(item => {
const border = document.createElement("div");
border.classList = "like-btn-con";
item.prepend(border);
});
}
给掘金放个烟花
这里我们使用canvas来绘制烟花, 这种操作网上也都玩烂了, 我这里也有个, 大家可以看下.
首先, 我们创建一个canvas
插入到页面body.
$("body").append('<canvas id="canvas"></canvas>');
然后获取该canvas
上下文.
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const cW = canvas.width = window.innerWidth;
const cH = canvas.height = window.innerHeight;
我们思考, 如何使用canvas做一个烟花:
- 烟花是由一个点四散开来
- 每个点移动一段曲线, 同时会从透明到凝实
- 不断绘制颜色由浅到深的圆点
- 使用requestAnimationFrame进行界面循环
- 简单写一个粒子发射器
实现方法很简单哈.
我们先来实现一个粒子:
function Vector(x, y) {
return {
x,
y
};
}
// 获取范围内的随机数
function randomCount(min, max) {
return Math.random() * (max - min) + min
}
//一个粒子
function Particle(position, _H) {
/** 位置 */
this.position = position;
/** 上一帧位置 */
this.lastPosition = position;
/** 方向弧度 */
this.angle = randomCount(0, Math.PI * 2);
/** 速度 */
this.speed = randomCount(2, 10);
/** 衰减系数 */
this.F = 0.96;
/** 重力 */
this.G = 1.2;
/** 色相 */
this.H = randomCount(_H - 30, _H + 30);
/** 饱和度 */
this.S = "100%";
/** 亮度 */
this.L = `${randomCount(50, 80)}%`;
/** 透明度 */
this.A = 0;
/** 透明度衰减 */
this.AD = randomCount(0.02, 0.03);
/** 更新 */
Particle.prototype.update = function (_index) {
this.draw();
this.lastPosition = this.position;
this.speed *= this.F;
const _x = this.position.x + this.speed * Math.cos(this.angle);
const _y = this.position.y + this.speed * Math.sin(this.angle) + this.G;
this.position = new Vector(_x, _y);
this.A += this.AD;
if (this.A > 0.85) {
particleList.splice(_index, 1);
}
}
/** 绘制 */
Particle.prototype.draw = function () {
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(this.lastPosition.x, this.lastPosition.y);
ctx.strokeStyle = `hsla(${this.H}, ${this.S}, ${this.L}, ${this.A}`
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.stroke();
}
}
Particle.prototype.update
方法需要每一帧执行. 这样子我们的烟花的每一个粒子就能绘制得到了.
这样子, 我们new多个个粒子已经可以得到烟花的雏型了.
/** 粒子 */
var particleList = [];
for (let i = 0; i < 60; i++) {
particleList.push(new Particle(this.position, this.H));
}
但是烟花播放之后, 何时移除呢?
当透明度大于一定程度的时候移除, 这个时候每个烟花粒子就绘制完成了.
if (this.A > 0.85) {
particleList.splice(_index, 1);
}
接下来, 我们需要绘制烟花上升的部分了. 这里更简单, 和刚才的粒子如出一辙, 当烟花上升到一定高度, 就开始爆炸开来.
/** 放烟花 */
function PlayFire(position, _height) {
/** 位置 */
this.position = position;
/** 上一帧位置 */
this.lastPosition = position;
/** 爆炸高度 */
this.boomH = position.y - _height;
/** 烟花高度 */
this.height = _height;
/** 每次移动距离 */
this.dis = 14.2;
/** 色相 */
this.H = randomCount(0, 360);
/** 饱和度 */
this.S = "100%";
/** 亮度 */
this.L = `${randomCount(50, 80)}%`;
/** 透明度 */
this.A = 1;
/** 更新 */
PlayFire.prototype.update = function (_index) {
this.draw();
this.lastPosition = this.position;
const _y = this.position.y - this.dis;
if (this.position.y <= this.boomH) {
for (let i = 0; i < 60; i++) {
particleList.push(new Particle(this.position, this.H));
}
fireList.splice(_index, 1);
} else {
this.position = new Vector(this.position.x, _y);
}
}
/** 绘制 */
PlayFire.prototype.draw = function () {
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(this.lastPosition.x, this.lastPosition.y);
ctx.strokeStyle = `hsla(${this.H}, ${this.S}, ${this.L}, ${this.A}`
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.stroke();
}
}
这样子, 我们就绘制出了一个类似的烟花播放.
最后, 我们在加入帧控制, 每组烟花播放8个, 每180帧循环一次.
/** 粒子 */
var particleList = [];
/** 烟花桶 */
var fireList = [];
/** 一波间隔 */
var oneInterval = 180;
var oneTimer = 180;
/** 自动播放间隔 */
var fireinterval = 20;
/** 帧计时-每一个 */
var timer = 0;
function render() {
window.requestAnimationFrame(render);
particleList.forEach((item, index) => {
item.update(index);
});
fireList.forEach((item, index) => {
item.update(index);
});
if (oneTimer >= oneInterval) {
if (timer >= fireinterval) {
var playfire = new PlayFire({
x: randomCount(200, 1500),
y: 1000
}, 800);
fireList.push(playfire)
timer = 0;
} else {
timer++;
}
}
if (oneTimer >= oneInterval + fireinterval * 8) {
oneTimer = 0;
timer = 0;
} else {
oneTimer++;
}
}
这个时候, 我们发现烟花炸开同时就消失了, 没有层次感.
我们在每一次渲染之前, 叠加一个透明色rgba(0, 0, 0, 0.2)
.这样子层次感就出来了.
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
ctx.fillRect(0, 0, cW, cH);
ctx.globalCompositeOperation = 'lighter';
短短100多行代码, 就实现了一个烟花播放的特效, 接着我们将脚本注入到页面, 就可以放烟花了.
删除广告
笔者不经意间发现, 掘金的广告虽然清秀, 但是笔者仍然不想要, 于是, 我们就给他删除掉吧.
//删除广告
function deleteAdvertise() {
var appSidebarBlock = $(".app-download-sidebar-block");
appSidebarBlock.remove();
var bannerBlock = $(".banner-block");
bannerBlock.remove();
var wechatSidebarBlock = $(".wechat-sidebar-block");
wechatSidebarBlock.remove();
var moreBlock = $(".more-block");
moreBlock.remove();
var stickyBlock = $(".sticky-block");
stickyBlock.remove();
}
找到广告节点, 删除掉就ok了, 但是有些逛ago是动态植入的, 这种的就麻烦一点, 需要监听页面dom变化去删除. 还好掘金的比较正常.
加入看板娘
这个很多同学都不陌生.
向页面先注入L2D的js.
"js/L2Dwidget.min.js","js/L2Dwidget.0.min.js","js/jquery.min.js"
然后设置一些配置项:
L2Dwidget.init({
"display": {
"superSample": 2,
"width": 200,
"height": 400,
"position": "right",
"hOffset": 0,
"vOffset": 0
}
});
这样子就大功告成了.
结尾
笔者这里只是简单设计了一下, 页面上的很多东西都可以设置的.
朋友们也可以按照自己的喜好来设计自己的掘金页面.
说白了, 我就是为了奖品来的, 花两小时搞了下, 博大家一笑. 哈哈哈~~~
都这么舔掘金爸爸了, 还不得推荐、点赞起来🍉🍉🍉
欢迎大家拍砖指正, 笔者功力尚浅, 如有不当之处请斧正!
文章粗浅, 望诸位不吝您的评论和点赞~ 注: 本文系作者呕心沥血之作, 转载须声明
参考: