中秋佳节 丨 我给掘金换了套皮肤主题🎉🎉🎉

2,153 阅读4分钟

我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛

中秋创意投稿大赛

一年一度中秋佳节至.

偶然看到掘金的中秋活动, 又要和技术挂钩, 又要和中秋挂钩, 很多文章都写烂了....

「文章本天成, 妙手偶得之」

突然发现, 掘金的页面实在太简单了, 说错话了, 是简洁优雅, 隽永大气.

然后趁此活动机会, 我就来帮掘金大大换个皮肤吧~

技术选型:

chrome插件开发

本文乃初级chrome开发, 同时涉及到一些canvas知识, 各位看官觉得操作太辣了勿怪!!!

看一下效果:

index.gif

准备工序

首先, 开发一个chrome插件, 最重要的就是他的清单文件manifest.json.

manifest_version

清单版本, 这里写2就完事了.

browser_action

这个是插件显示的一些配置.

default_icon是默认图标. 这里我用的是一个小苹果的图标.

WechatIMG333.png

default_title这个默认标题.

default_popup这个是我们这个插件的默认页面.

WechatIMG334.png

content_scripts

这里是向页面中注入cssjs代码.

matches这个配置项, 就是我们这个插件生效的页面, 我这里只匹配掘金的域名https://juejin.cn/*.

js是向页面注入的js代码.我这里引入了一个自己的代码js/content-script.jsjs/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结构, 并找出我们需要修改的唯一节点.

例如, 我们找一个最大的背景.

WechatIMG335.png

然后注入css修改.

#juejin {
    background: url("//yun.duiba.com.cn/aurora/assets/a73036f55b0c256849761eb2c8cb30e4a839d63e.jpeg") repeat-y !important;
    background-size: cover;
}

这些所有的页面修改都是傻瓜操作了, 唯一需要注意的就是你的设计能力和审美观念.

接下来, 我们来给我们自己的头像加一个玉兔的边框.

WechatIMG336.png

这里我们需要创建一个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);
    });
}

WechatIMG337.png

给掘金放个烟花

这里我们使用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));
}

WechatIMG340.png

但是烟花播放之后, 何时移除呢?

当透明度大于一定程度的时候移除, 这个时候每个烟花粒子就绘制完成了.

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';

WechatIMG341.png

短短100多行代码, 就实现了一个烟花播放的特效, 接着我们将脚本注入到页面, 就可以放烟花了.

删除广告

笔者不经意间发现, 掘金的广告虽然清秀, 但是笔者仍然不想要, 于是, 我们就给他删除掉吧.

WechatIMG338.png

//删除广告
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
    }
});

这样子就大功告成了.

WechatIMG339.png

结尾

笔者这里只是简单设计了一下, 页面上的很多东西都可以设置的.
朋友们也可以按照自己的喜好来设计自己的掘金页面.

说白了, 我就是为了奖品来的, 花两小时搞了下, 博大家一笑. 哈哈哈~~~

都这么舔掘金爸爸了, 还不得推荐、点赞起来🍉🍉🍉

欢迎大家拍砖指正, 笔者功力尚浅, 如有不当之处请斧正!

文章粗浅, 望诸位不吝您的评论和点赞~ 注: 本文系作者呕心沥血之作, 转载须声明

参考:

github.com/sxei/chrome…