Beautify Your Blog Using Javascript
本篇文章介绍了一些可以在Hexo的Fluid主题中实现的博客美化的效果
目录
实现雪花的效果
首先要在 _config.fluid.yml
的 custom_js:
选项中指定实现雪花效果的 js
文件的路径,具体的路径及其设置参考
官方文档的配置指南 | Hexo Fluid 用户手册。我的配置文件如下:
1# 指定自定义 .js 文件路径,支持列表;路径是相对 source 目录,如 /js/custom.js 对应存放目录 source/js/custom.js
2# Specify the path of your custom js file, support list. The path is relative to the source directory, such as `/js/custom.js` corresponding to the directory `source/js/custom.js`
3custom_js:
4 - /js/custom.js
5 - /js/duration.js
6 - /js/snow.js
7 - /js/firework.js
配置完这个文件之后,在相应路径下创建相关的 js
文件,记得文件名和路径要和 yml
文件中指定的一致,然后在相应的 js
文件中写入如下的代码就可以实现相应的效果:
1/**
2 * 雪花飘落效果:
3 * @Source: https://blog.musnow.top/posts/138502038/index.html
4 * @Source: https://cnhuazhu.gitee.io/2021/02/24/Hexo%E9%AD%94%E6%94%B9/Hexo%E6%B7%BB%E5%8A%A0%E9%9B%AA%E8%8A%B1%E5%8A%A8%E6%80%81%E6%95%88%E6%9E%9C%E8%83%8C%E6%99%AF/index.html
5 * @Source: https://cnhuazhu.top/butterfly/2021/02/24/Hexo%E9%AD%94%E6%94%B9/Hexo%E6%B7%BB%E5%8A%A0%E9%9B%AA%E8%8A%B1%E5%8A%A8%E6%80%81%E6%95%88%E6%9E%9C%E8%83%8C%E6%99%AF/
6 */
7
8/** 第一个版本的雪花 */
9if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
10 // 移动端不显示
11}
12else {
13 document.write('<canvas id="snow" style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:100;pointer-events:none"></canvas>');
14
15 window && (() => {
16 let e = {
17 flakeCount: 50,
18 minDist: 150,
19 color: "255, 255, 255",
20 size: 2,
21 speed: .5,
22 opacity: .2,
23 stepsize: .5
24 };
25 const t = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function (e) {
26 window.setTimeout(e, 1e3 / 60)
27 }
28 ;
29 window.requestAnimationFrame = t;
30 const i = document.getElementById("snow"),
31 n = i.getContext("2d"),
32 o = e.flakeCount;
33 let a = -100,
34 d = -100,
35 s = [];
36 i.width = window.innerWidth,
37 i.height = window.innerHeight;
38 const h = () => {
39 n.clearRect(0, 0, i.width, i.height);
40 const r = e.minDist;
41 for (let t = 0; t < o; t++) {
42 let o = s[t];
43 const h = a,
44 w = d,
45 m = o.x,
46 c = o.y,
47 p = Math.sqrt((h - m) * (h - m) + (w - c) * (w - c));
48 if (p < r) {
49 const e = (h - m) / p,
50 t = (w - c) / p,
51 i = r / (p * p) / 2;
52 o.velX -= i * e,
53 o.velY -= i * t
54 } else
55 o.velX *= .98,
56 o.velY < o.speed && o.speed - o.velY > .01 && (o.velY += .01 * (o.speed - o.velY)),
57 o.velX += Math.cos(o.step += .05) * o.stepSize;
58 n.fillStyle = "rgba(" + e.color + ", " + o.opacity + ")",
59 o.y += o.velY,
60 o.x += o.velX,
61 (o.y >= i.height || o.y <= 0) && l(o),
62 (o.x >= i.width || o.x <= 0) && l(o),
63 n.beginPath(),
64 n.arc(o.x, o.y, o.size, 0, 2 * Math.PI),
65 n.fill()
66 }
67 t(h)
68 }
69 , l = e => {
70 e.x = Math.floor(Math.random() * i.width),
71 e.y = 0,
72 e.size = 3 * Math.random() + 2,
73 e.speed = 1 * Math.random() + .5,
74 e.velY = e.speed,
75 e.velX = 0,
76 e.opacity = .5 * Math.random() + .3
77 }
78 ;
79 document.addEventListener("mousemove", (e => {
80 a = e.clientX,
81 d = e.clientY
82 }
83 )),
84 window.addEventListener("resize", (() => {
85 i.width = window.innerWidth,
86 i.height = window.innerHeight
87 }
88 )),
89 (() => {
90 for (let t = 0; t < o; t++) {
91 const t = Math.floor(Math.random() * i.width)
92 , n = Math.floor(Math.random() * i.height)
93 , o = 3 * Math.random() + e.size
94 , a = 1 * Math.random() + e.speed
95 , d = .5 * Math.random() + e.opacity;
96 s.push({
97 speed: a,
98 velX: 0,
99 velY: a,
100 x: t,
101 y: n,
102 size: o,
103 stepSize: Math.random() / 30 * e.stepsize,
104 step: 0,
105 angle: 180,
106 opacity: d
107 })
108 }
109 h()
110 }
111 )()
112 }
113 )();
114}
115
116// 移动端不显示
117if (!(navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
118 (function ($) {
119 $.fn.snow = function (options) {
120 var $flake = $('<div id="snowbox" />').css({ 'position': 'absolute', 'z-index': '9999', 'top': '-50px' }).html('❄'),
121 documentheight = $(document).height(),
122 documentwidth = $(document).width(),
123 defaults = {
124 minsize: 10,
125 maxsize: 20,
126 newon: 1000,
127 flakecolor: "#afdaef" /* 此处可以定义雪花颜色,若要白色可以改为#ffffff */
128 },
129 options = $.extend({}, defaults, options);
130 var interval = setInterval(function () {
131 var startpositionleft = Math.random() * documentwidth - 100,
132 startopacity = 0.5 + Math.random(),
133 sizeflake = options.minsize + Math.random() * options.maxsize,
134 endpositiontop = documentheight - 200,
135 endpositionleft = startpositionleft - 500 + Math.random() * 500,
136 durationfall = documentheight * 10 + Math.random() * 5000;
137 $flake.clone().appendTo('body').css({
138 left: startpositionleft,
139 opacity: startopacity,
140 'font-size': sizeflake,
141 color: options.flakecolor
142 }).animate({
143 top: endpositiontop,
144 left: endpositionleft,
145 opacity: 0.2
146 }, durationfall, 'linear', function () {
147 $(this).remove();
148 });
149 }, options.newon);
150 };
151 })(jQuery);
152
153 $(function () {
154 $.fn.snow({
155 minsize: 5, /* 定义雪花最小尺寸 */
156 maxsize: 25,/* 定义雪花最大尺寸 */
157 newon: 800 /* 定义密集程度,数字越小越密集 */
158 });
159 });
160}
顺便把另外一个版本的雪花也贴上来:
1/** 另一个版本的雪花 */
2function snowFall(snow) {
3 /* 可配置属性 */
4 snow = snow || {};
5 this.maxFlake = snow.maxFlake || 600; /* 最多片数 */
6 this.flakeSize = snow.flakeSize || 10; /* 雪花形状 */
7 this.fallSpeed = snow.fallSpeed || 1; /* 坠落速度 */
8}
9
10/* 兼容写法 */
11requestAnimationFrame = window.requestAnimationFrame ||
12 window.mozRequestAnimationFrame ||
13 window.webkitRequestAnimationFrame ||
14 window.msRequestAnimationFrame ||
15 window.oRequestAnimationFrame ||
16 function (callback) {
17 setTimeout(callback, 1000 / 60);
18 };
19
20cancelAnimationFrame = window.cancelAnimationFrame ||
21 window.mozCancelAnimationFrame ||
22 window.webkitCancelAnimationFrame ||
23 window.msCancelAnimationFrame ||
24 window.oCancelAnimationFrame;
25
26/* 开始下雪 */
27snowFall.prototype.start = function () {
28 /* 创建画布 */
29 snowCanvas.apply(this);
30 /* 创建雪花形状 */
31 createFlakes.apply(this);
32 /* 画雪 */
33 drawSnow.apply(this);
34}
35
36/* 创建画布 */
37function snowCanvas() {
38 /* 添加Dom结点 */
39 var snowcanvas = document.createElement("canvas");
40 snowcanvas.id = "snowfall";
41 snowcanvas.width = window.innerWidth;
42 snowcanvas.height = document.documentElement.scrollHeight; // 使用scrollHeight获取完整文档高度
43 snowcanvas.setAttribute("style", "position:fixed; top: 0; left: 0; z-index: 1; pointer-events: none;");
44 document.body.appendChild(snowcanvas);
45 this.canvas = snowcanvas;
46 this.ctx = snowcanvas.getContext("2d");
47 /* 窗口大小改变的处理 */
48 window.onresize = function () {
49 snowcanvas.width = window.innerWidth;
50 snowcanvas.height = document.documentElement.scrollHeight;
51 }
52}
53
54/* 雪运动对象 */
55function flakeMove(canvasWidth, canvasHeight, flakeSize, fallSpeed) {
56 this.x = Math.floor(Math.random() * canvasWidth); /* x坐标 */
57 this.y = Math.floor(Math.random() * canvasHeight); /* y坐标 */
58 this.size = Math.random() * flakeSize + 2; /* 形状 */
59 this.maxSize = flakeSize; /* 最大形状 */
60 this.speed = Math.random() * 1 + fallSpeed; /* 坠落速度 */
61 this.fallSpeed = fallSpeed; /* 坠落速度 */
62 this.velY = this.speed; /* Y方向速度 */
63 this.velX = 0; /* X方向速度 */
64 this.stepSize = Math.random() / 30; /* 步长 */
65 this.step = 0 /* 步数 */
66}
67
68flakeMove.prototype.update = function () {
69 var x = this.x,
70 y = this.y;
71 /* 左右摆动(余弦) */
72 this.velX *= 0.98;
73 if (this.velY <= this.speed) {
74 this.velY = this.speed
75 }
76 this.velX += Math.cos(this.step += .05) * this.stepSize;
77
78 this.y += this.velY;
79 this.x += this.velX;
80 /* 飞出边界的处理 */
81 if (this.x >= canvas.width || this.x <= 0 || this.y >= canvas.height || this.y <= 0) {
82 this.reset(canvas.width, canvas.height)
83 }
84};
85
86/* 飞出边界-放置最顶端继续坠落 */
87flakeMove.prototype.reset = function (width, height) {
88 this.x = Math.floor(Math.random() * width);
89 this.y = 0;
90 this.size = Math.random() * this.maxSize + 2;
91 this.speed = Math.random() * 1 + this.fallSpeed;
92 this.velY = this.speed;
93 this.velX = 0;
94};
95
96// 渲染雪花-随机形状(此处可修改雪花颜色!!!)
97flakeMove.prototype.render = function (ctx) {
98 var snowFlake = ctx.createRadialGradient(this.x, this.y - window.scrollY, 0, this.x, this.y - window.scrollY, this.size);
99 snowFlake.addColorStop(0, "rgba(255, 255, 255, 0.9)"); /* 此处是雪花颜色,默认是白色 */
100 snowFlake.addColorStop(.5, "rgba(255, 255, 255, 0.5)"); /* 若要改为其他颜色,请自行查 */
101 snowFlake.addColorStop(1, "rgba(255, 255, 255, 0)"); /* 找16进制的RGB 颜色代码。 */
102 ctx.save();
103 ctx.fillStyle = snowFlake;
104 ctx.beginPath();
105 ctx.arc(this.x, this.y - window.scrollY, this.size, 0, Math.PI * 2);
106 ctx.fill();
107 ctx.restore();
108};
109
110/* 创建雪花-定义形状 */
111function createFlakes() {
112 var maxFlake = this.maxFlake,
113 flakes = this.flakes = [],
114 canvas = this.canvas;
115 for (var i = 0; i < maxFlake; i++) {
116 flakes.push(new flakeMove(canvas.width, canvas.height, this.flakeSize, this.fallSpeed))
117 }
118}
119
120/* 画雪 */
121function drawSnow() {
122 var maxFlake = this.maxFlake,
123 flakes = this.flakes;
124 ctx = this.ctx, canvas = this.canvas, that = this;
125 /* 清空雪花 */
126 ctx.clearRect(0, 0, canvas.width, canvas.height);
127 for (var e = 0; e < maxFlake; e++) {
128 flakes[e].update();
129 flakes[e].render(ctx);
130 }
131 /* 一帧一帧的画 */
132 this.loop = requestAnimationFrame(function () {
133 drawSnow.apply(that);
134 });
135}
136
137/** 只有一种雪花就取消注释下面这段代码,然后注释掉后面的新雪花的代码 */
138/* 调用及控制方法 */
139// var snow = new snowfall({
140// maxflake: 60
141// });
142// snow.start();
143
144/** 新雪花代码 */
145(function ($) {
146 $.fn.snow = function (options) {
147 var $flake = $('<div class="snowflake" />').css({
148 'position': 'absolute',
149 'z-index': '9999',
150 'top': '-50px'
151 }).html('❄'),
152 documentHeight = $(document).height(),
153 documentWidth = $(document).width(),
154 defaults = {
155 minSize: 10,
156 maxSize: 20,
157 newOn: 1000,
158 flakeColor: "#AFDAEF" /* 此处可以定义雪花颜色,若要白色可以改为#FFFFFF */
159 },
160 options = $.extend({}, defaults, options);
161
162 var interval = setInterval(function () {
163 var startPositionLeft = Math.random() * documentWidth - 100,
164 startOpacity = 0.5 + Math.random(),
165 sizeFlake = options.minSize + Math.random() * options.maxSize,
166 endPositionTop = documentHeight - 200,
167 endPositionLeft = startPositionLeft - 500 + Math.random() * 500,
168 durationFall = documentHeight * 10 + Math.random() * 5000;
169
170 $flake.clone().appendTo('body').css({
171 left: startPositionLeft,
172 opacity: startOpacity,
173 'font-size': sizeFlake,
174 color: options.flakeColor
175 }).animate({
176 top: endPositionTop,
177 left: endPositionLeft,
178 opacity: 0.2
179 }, durationFall, 'linear', function () {
180 $(this).remove();
181 });
182 }, options.newOn);
183 };
184})(jQuery);
185
186$(function () {
187 $.fn.snow({
188 minSize: 5, /* 定义雪花最小尺寸 */
189 maxSize: 25, /* 定义雪花最大尺寸 */
190 newOn: 800 /* 定义密集程度,数字越小越密集 */
191 });
192
193 // 调用新的雪花效果
194 var snowflake = new snowFall({
195 maxFlake: 30
196 });
197 snowflake.start();
198});
实现鼠标点击的烟花效果
加入 js
代码的步骤和前面的实现雪花的步骤类似,这里就不再赘述,下面直接给出实现的代码:
1/** 鼠标点击放礼花
2 * @Source: https://cnwjy.site/2022/01/04/Hexo-Fluid%E7%BE%8E%E5%8C%96/
3 */
4class Circle {
5 constructor({ origin, speed, color, angle, context }) {
6 this.origin = origin
7 this.position = { ...this.origin }
8 this.color = color
9 this.speed = speed
10 this.angle = angle
11 this.context = context
12 this.renderCount = 0
13 }
14
15 draw() {
16 this.context.fillStyle = this.color
17 this.context.beginPath()
18 this.context.arc(this.position.x, this.position.y, 2, 0, Math.PI * 2)
19 this.context.fill()
20 }
21
22 move() {
23 this.position.x = (Math.sin(this.angle) * this.speed) + this.position.x
24 this.position.y = (Math.cos(this.angle) * this.speed) + this.position.y + (this.renderCount * 0.3)
25 this.renderCount++
26 }
27 }
28
29 class Boom {
30 constructor ({ origin, context, circleCount = 16, area }) {
31 this.origin = origin
32 this.context = context
33 this.circleCount = circleCount
34 this.area = area
35 this.stop = false
36 this.circles = []
37 }
38
39 randomArray(range) {
40 const length = range.length
41 const randomIndex = Math.floor(length * Math.random())
42 return range[randomIndex]
43 }
44
45 randomColor() {
46 const range = ['8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
47 return '#' + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range)
48 }
49
50 randomRange(start, end) {
51 return (end - start) * Math.random() + start
52 }
53
54 init() {
55 for(let i = 0; i < this.circleCount; i++) {
56 const circle = new Circle({
57 context: this.context,
58 origin: this.origin,
59 color: this.randomColor(),
60 angle: this.randomRange(Math.PI - 1, Math.PI + 1),
61 speed: this.randomRange(1, 6)
62 })
63 this.circles.push(circle)
64 }
65 }
66
67 move() {
68 this.circles.forEach((circle, index) => {
69 if (circle.position.x > this.area.width || circle.position.y > this.area.height) {
70 return this.circles.splice(index, 1)
71 }
72 circle.move()
73 })
74 if (this.circles.length == 0) {
75 this.stop = true
76 }
77 }
78
79 draw() {
80 this.circles.forEach(circle => circle.draw())
81 }
82 }
83
84 class CursorSpecialEffects {
85 constructor() {
86 this.computerCanvas = document.createElement('canvas')
87 this.renderCanvas = document.createElement('canvas')
88
89 this.computerContext = this.computerCanvas.getContext('2d')
90 this.renderContext = this.renderCanvas.getContext('2d')
91
92 this.globalWidth = window.innerWidth
93 this.globalHeight = window.innerHeight
94
95 this.booms = []
96 this.running = false
97 }
98
99 handleMouseDown(e) {
100 const boom = new Boom({
101 origin: { x: e.clientX, y: e.clientY },
102 context: this.computerContext,
103 area: {
104 width: this.globalWidth,
105 height: this.globalHeight
106 }
107 })
108 boom.init()
109 this.booms.push(boom)
110 this.running || this.run()
111 }
112
113 handlePageHide() {
114 this.booms = []
115 this.running = false
116 }
117
118 init() {
119 const style = this.renderCanvas.style
120 style.position = 'fixed'
121 style.top = style.left = 0
122 style.zIndex = '999999999999999999999999999999999999999999'
123 style.pointerEvents = 'none'
124
125 style.width = this.renderCanvas.width = this.computerCanvas.width = this.globalWidth
126 style.height = this.renderCanvas.height = this.computerCanvas.height = this.globalHeight
127
128 document.body.append(this.renderCanvas)
129
130 window.addEventListener('mousedown', this.handleMouseDown.bind(this))
131 window.addEventListener('pagehide', this.handlePageHide.bind(this))
132 }
133
134 run() {
135 this.running = true
136 if (this.booms.length == 0) {
137 return this.running = false
138 }
139
140 requestAnimationFrame(this.run.bind(this))
141
142 this.computerContext.clearRect(0, 0, this.globalWidth, this.globalHeight)
143 this.renderContext.clearRect(0, 0, this.globalWidth, this.globalHeight)
144
145 this.booms.forEach((boom, index) => {
146 if (boom.stop) {
147 return this.booms.splice(index, 1)
148 }
149 boom.move()
150 boom.draw()
151 })
152 this.renderContext.drawImage(this.computerCanvas, 0, 0, this.globalWidth, this.globalHeight)
153 }
154 }
155
156 const cursorSpecialEffects = new CursorSpecialEffects()
157 cursorSpecialEffects.init()
实现浏览器搞笑标题效果
下面的代码可以实现当前页面不在和在博客时显示不同的浏览器标题的效果,具体修改到博客中与上面的操作类似,不再赘述。
1/**
2 * 浏览器搞笑标题:@Source:https://asteri5m.gitee.io/archives/Fluid%E9%AD%94%E6%94%B9%E7%AC%94%E8%AE%B0.html
3 */
4var OriginTitle = document.title;
5var titleTime;
6document.addEventListener('visibilitychange', function() {
7 if (document.hidden) {
8 $('[rel="icon"]').attr('href', "/funny.ico");
9 document.title = '╭(°A°`)╮ 页面崩溃啦 ~';
10 clearTimeout(titleTime);
11 } else {
12 $('[rel="icon"]').attr('href', "/img/newtubiao.png");
13 document.title = '(ฅ>ω<*ฅ) 噫又好啦 ~' + OriginTitle;
14 titleTime = setTimeout(function() {
15 document.title = OriginTitle;
16 }, 2000);
17 }
18});