本文为 Hexo Butterfly 主题的优雅改(只引入 js 文件)。

原码参考自:博客魔改日记(3) | Ariasakaの小窝 (yisous.xyz),在其中做了改动:

  • 替换了部分变量和注释名
  • 增加可定制显示漂浮物页面的逻辑

引入方式 1:作为 js 文件直接 inject 到博客中

这种方式在对于多个页面使用漂浮物效果时有用。

使用方法:

  • 需要所有博客页面都显示漂浮物:将 allowAll = true
  • 指定显示漂浮物的页面:将 allowAll = false,在 urlAllowList 中列出需要显示漂浮物的网页相对地址。如 /about/

优点:统一指定需要使用漂浮物的 url。

缺点:还是有那么一点不灵活。

js 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// 漂浮物
// 新增函数
// 首先获取 Url,然后把 Url 通过 // 截成两部分,再从后一部分中截取相对路径。如果截取到的相对路径中有参数,则把参数去掉。
// 获取相对路径
function GetUrlRelativePath()
{
var url = document.location.toString();
var arrUrl = url.split("//");

var start = arrUrl[1].indexOf("/");
var relUrl = arrUrl[1].substring(start);//stop省略,截取从start开始到结尾的所有字符

if(relUrl.indexOf("?") != -1){
relUrl = relUrl.split("?")[0];
}
return relUrl;
}
var allowAll = false; // true 则允许所有网页存在漂浮物
// 允许显示漂浮物的网址列表
var urlAllowList = [
"/about/",
"/link/friends/",
]

var isAllowFloat = false; // 全局变量,允许使用漂浮特效

// 判断
function decide(){
isAllowFloat = true;
if(!allowAll){
// 判断当前页面是否为指定页面
var url = GetUrlRelativePath();
var i = 0;
for(;i<urlAllowList.length;i++){
if(url===urlAllowList[i]){
// console.log(i);
isAllowFloat = true;
break;
}
}
if(i===urlAllowList.length){
isAllowFloat = false;
}
}
console.log(isAllowFloat)
if(isAllowFloat)startFloat();
}

var stop, staticx;
var img = new Image();
img.src = "/image/star_float.png"; // 图片

function Float(x, y, s, r, fn) {
this.x = x;
this.y = y;
this.s = s;
this.r = r;
this.fn = fn;
}

Float.prototype.draw = function(cxt) {
cxt.save();
var xc = 40 * this.s / 4;
cxt.translate(this.x, this.y);
cxt.rotate(this.r);
cxt.drawImage(img, 0, 0, 35 * this.s, 35 * this.s)
//漂浮物大小
cxt.restore();
}

Float.prototype.update = function() {
this.x = this.fn.x(this.x, this.y);
this.y = this.fn.y(this.y, this.y);
this.r = this.fn.r(this.r);
if (this.x > window.innerWidth || this.x < 0 || this.y > window.innerHeight || this.y < 0) {
this.r = getRandom('fnr');
if (Math.random() > 0.4) {
this.x = getRandom('x');
this.y = 0;
this.s = getRandom('s');
this.r = getRandom('r');
} else {
this.x = window.innerWidth;
this.y = getRandom('y');
this.s = getRandom('s');
this.r = getRandom('r');
}
}
}

FloatList = function() {
this.list = [];
}
FloatList.prototype.push = function(float) {
this.list.push(float);
}
FloatList.prototype.update = function() {
for (var i = 0, len = this.list.length; i < len; i++) {
this.list[i].update();
}
}
FloatList.prototype.draw = function(cxt) {
for (var i = 0, len = this.list.length; i < len; i++) {
this.list[i].draw(cxt);
}
}
FloatList.prototype.get = function(i) {
return this.list[i];
}
FloatList.prototype.size = function() {
return this.list.length;
}

function getRandom(option) {
var ret, random;
switch (option) {
case 'x':
ret = Math.random() * window.innerWidth;
break;
case 'y':
ret = Math.random() * window.innerHeight;
break;
case 's':
ret = Math.random();
break;
case 'r':
ret = Math.random() * 6;
break;
case 'fnx':
random = -0.5 + Math.random() * 1;
ret = function(x, y) {
return x + 0.5 * random - 0.6;
//x轴速度
}
;
break;
case 'fny':
random = 0.8 + Math.random() * 0.7
//y轴速度
ret = function(x, y) {
return y + random;
}
;
break;
case 'fnr':
random = Math.random() * 0.03;
ret = function(r) {
return r + random;
}
;
break;
}
return ret;
}

function startFloat() {

requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame;
var canvas = document.createElement('canvas'), cxt;
staticx = true;
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
canvas.setAttribute('style', 'position: fixed;left: 0;top: 0;pointer-events: none;');
canvas.setAttribute('id', 'canvas_float');
document.getElementsByTagName('body')[0].appendChild(canvas);
cxt = canvas.getContext('2d');
var floatList = new FloatList();
for (var i = 0; i < 10; i++) {
//漂浮物数量
var float, randomX, randomY, randomS, randomR, randomFnx, randomFny;
randomX = getRandom('x');
randomY = getRandom('y');
randomR = getRandom('r');
randomS = getRandom('s');
randomFnx = getRandom('fnx');
randomFny = getRandom('fny');
randomFnR = getRandom('fnr');
float = new Float(randomX,randomY,randomS,randomR,{
x: randomFnx,
y: randomFny,
r: randomFnR
});
float.draw(cxt);
floatList.push(float);
}
stop = requestAnimationFrame(function() {
cxt.clearRect(0, 0, canvas.width, canvas.height);
floatList.update();
floatList.draw(cxt);
stop = requestAnimationFrame(arguments.callee);
})
}

window.onresize = function() {
if(!isAllowFloat)return;
var canvasSnow = document.getElementById('canvas_float');
canvasSnow.width = window.innerWidth;
canvasSnow.height = window.innerHeight;
}

function stopp(e) {
if (!e && document.getElementById("canvas_float")) {
var child = document.getElementById("canvas_float");
child.parentNode.removeChild(child);
window.cancelAnimationFrame(stop);
} else if (e && !document.getElementById("canvas_float")) {
decide();
}
}

window.addEventListener("DOMContentLoaded",decide);

引入方式 2:只在单个普通文章中使用

使用本方式时,开启 Pjax 会导致漂浮物应用范围错乱

就像本页面展示的效果那样。

  • 优点:省去博客每次都要请求一个 js 文件。
  • 缺点:不灵活。

直接在文章末尾插入 script,等 markdown 文件渲染成 html 后就相当于只注入这段脚本到当前页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<script>

var stop, staticx;
var img = new Image();
img.src = "/image/star_float.png"; // 图片

function Float(x, y, s, r, fn) {
this.x = x;
this.y = y;
this.s = s;
this.r = r;
this.fn = fn;
}

Float.prototype.draw = function(cxt) {
cxt.save();
var xc = 40 * this.s / 4;
cxt.translate(this.x, this.y);
cxt.rotate(this.r);
cxt.drawImage(img, 0, 0, 35 * this.s, 35 * this.s)
//漂浮物大小
cxt.restore();
}

Float.prototype.update = function() {
this.x = this.fn.x(this.x, this.y);
this.y = this.fn.y(this.y, this.y);
this.r = this.fn.r(this.r);
if (this.x > window.innerWidth || this.x < 0 || this.y > window.innerHeight || this.y < 0) {
this.r = getRandom('fnr');
if (Math.random() > 0.4) {
this.x = getRandom('x');
this.y = 0;
this.s = getRandom('s');
this.r = getRandom('r');
} else {
this.x = window.innerWidth;
this.y = getRandom('y');
this.s = getRandom('s');
this.r = getRandom('r');
}
}
}

FloatList = function() {
this.list = [];
}
FloatList.prototype.push = function(float) {
this.list.push(float);
}
FloatList.prototype.update = function() {
for (var i = 0, len = this.list.length; i < len; i++) {
this.list[i].update();
}
}
FloatList.prototype.draw = function(cxt) {
for (var i = 0, len = this.list.length; i < len; i++) {
this.list[i].draw(cxt);
}
}
FloatList.prototype.get = function(i) {
return this.list[i];
}
FloatList.prototype.size = function() {
return this.list.length;
}

function getRandom(option) {
var ret, random;
switch (option) {
case 'x':
ret = Math.random() * window.innerWidth;
break;
case 'y':
ret = Math.random() * window.innerHeight;
break;
case 's':
ret = Math.random();
break;
case 'r':
ret = Math.random() * 6;
break;
case 'fnx':
random = -0.5 + Math.random() * 1;
ret = function(x, y) {
return x + 0.5 * random - 0.6;
//x轴速度
}
;
break;
case 'fny':
random = 0.8 + Math.random() * 0.7
//y轴速度
ret = function(x, y) {
return y + random;
}
;
break;
case 'fnr':
random = Math.random() * 0.03;
ret = function(r) {
return r + random;
}
;
break;
}
return ret;
}

function startFloat() {

requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame;
var canvas = document.createElement('canvas'), cxt;
staticx = true;
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
canvas.setAttribute('style', 'position: fixed;left: 0;top: 0;pointer-events: none;');
canvas.setAttribute('id', 'canvas_float');
document.getElementsByTagName('body')[0].appendChild(canvas);
cxt = canvas.getContext('2d');
var floatList = new FloatList();
for (var i = 0; i < 10; i++) {
//漂浮物数量
var float, randomX, randomY, randomS, randomR, randomFnx, randomFny;
randomX = getRandom('x');
randomY = getRandom('y');
randomR = getRandom('r');
randomS = getRandom('s');
randomFnx = getRandom('fnx');
randomFny = getRandom('fny');
randomFnR = getRandom('fnr');
float = new Float(randomX,randomY,randomS,randomR,{
x: randomFnx,
y: randomFny,
r: randomFnR
});
float.draw(cxt);
floatList.push(float);
}
stop = requestAnimationFrame(function() {
cxt.clearRect(0, 0, canvas.width, canvas.height);
floatList.update();
floatList.draw(cxt);
stop = requestAnimationFrame(arguments.callee);
})
}

window.onresize = function() {
if(!isAllowFloat)return;
var canvasSnow = document.getElementById('canvas_float');
canvasSnow.width = window.innerWidth;
canvasSnow.height = window.innerHeight;
}

function stopp(e) {
if (!e && document.getElementById("canvas_float")) {
var child = document.getElementById("canvas_float");
child.parentNode.removeChild(child);
window.cancelAnimationFrame(stop);
} else if (e && !document.getElementById("canvas_float")) {
//decide();
}
}

startFloat();
</script>

这段脚本其实就是把页面判断逻辑删去了。注意选择的图片路径。

未来计划