Hexo页脚养鱼效果
前言
页脚养鱼效果我也是折腾了蛮久的,奈何自己太菜,出现了一些问题也不知道怎么解决,查询一些博客,相关的描述也很简单。这里参考了Ln
大佬的博客,总算是搞定了。
Ln’s Blog ——《如何在页脚养鱼》:https://weilining.github.io/204.html
最终效果在本站页脚,向下滑动即可看到。
操作方法
参考文章一共介绍了两种方法,第一种是有针对于Butterfly
主题的修改。第二种方法是通用方法(其他也主题可参考)
方法一
注:方法一直接将原本的js代码搬运放置本地。
-
在
\themes\butterfly\source\js
路径下创建一个fish.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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358fish();
function fish() {
return (
$("footer").append(
'<div class="fish_container" id="jsi-flying-fish-container"></div>'
),
$(".fish_container").css({
"z-index": -1,
width: "100%",
height: "160px",
margin: 0,
padding: 0,
}),
$("#footer-wrap").css({
position: "absolute",
"text-align": "center",
top: 0,
right: 0,
left: 0,
bottom: 0,
}),
this
);
}
var RENDERER = {
POINT_INTERVAL : 5,
FISH_COUNT : 3,
MAX_INTERVAL_COUNT : 50,
INIT_HEIGHT_RATE : 0.5,
THRESHOLD : 50,
init : function(){
this.setParameters();
this.reconstructMethods();
this.setup();
this.bindEvent();
this.render();
},
setParameters : function(){
this.$window = $(window);
this.$container = $('#jsi-flying-fish-container');
this.$canvas = $('<canvas />');
this.context = this.$canvas.appendTo(this.$container).get(0).getContext('2d');
this.points = [];
this.fishes = [];
this.watchIds = [];
},
createSurfacePoints : function(){
var count = Math.round(this.width / this.POINT_INTERVAL);
this.pointInterval = this.width / (count - 1);
this.points.push(new SURFACE_POINT(this, 0));
for(var i = 1; i < count; i++){
var point = new SURFACE_POINT(this, i * this.pointInterval),
previous = this.points[i - 1];
point.setPreviousPoint(previous);
previous.setNextPoint(point);
this.points.push(point);
}
},
reconstructMethods : function(){
this.watchWindowSize = this.watchWindowSize.bind(this);
this.jdugeToStopResize = this.jdugeToStopResize.bind(this);
this.startEpicenter = this.startEpicenter.bind(this);
this.moveEpicenter = this.moveEpicenter.bind(this);
this.reverseVertical = this.reverseVertical.bind(this);
this.render = this.render.bind(this);
},
setup : function(){
this.points.length = 0;
this.fishes.length = 0;
this.watchIds.length = 0;
this.intervalCount = this.MAX_INTERVAL_COUNT;
this.width = this.$container.width();
this.height = this.$container.height();
this.fishCount = this.FISH_COUNT * this.width / 500 * this.height / 500;
this.$canvas.attr({width : this.width, height : this.height});
this.reverse = false;
this.fishes.push(new FISH(this));
this.createSurfacePoints();
},
watchWindowSize : function(){
this.clearTimer();
this.tmpWidth = this.$window.width();
this.tmpHeight = this.$window.height();
this.watchIds.push(setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL));
},
clearTimer : function(){
while(this.watchIds.length > 0){
clearTimeout(this.watchIds.pop());
}
},
jdugeToStopResize : function(){
var width = this.$window.width(),
height = this.$window.height(),
stopped = (width == this.tmpWidth && height == this.tmpHeight);
this.tmpWidth = width;
this.tmpHeight = height;
if(stopped){
this.setup();
}
},
bindEvent : function(){
this.$window.on('resize', this.watchWindowSize);
this.$container.on('mouseenter', this.startEpicenter);
this.$container.on('mousemove', this.moveEpicenter);
this.$container.on('click', this.reverseVertical);
},
getAxis : function(event){
var offset = this.$container.offset();
return {
x : event.clientX - offset.left + this.$window.scrollLeft(),
y : event.clientY - offset.top + this.$window.scrollTop()
};
},
startEpicenter : function(event){
this.axis = this.getAxis(event);
},
moveEpicenter : function(event){
var axis = this.getAxis(event);
if(!this.axis){
this.axis = axis;
}
this.generateEpicenter(axis.x, axis.y, axis.y - this.axis.y);
this.axis = axis;
},
generateEpicenter : function(x, y, velocity){
if(y < this.height / 2 - this.THRESHOLD || y > this.height / 2 + this.THRESHOLD){
return;
}
var index = Math.round(x / this.pointInterval);
if(index < 0 || index >= this.points.length){
return;
}
this.points[index].interfere(y, velocity);
},
reverseVertical : function(){
this.reverse = !this.reverse;
for(var i = 0, count = this.fishes.length; i < count; i++){
this.fishes[i].reverseVertical();
}
},
controlStatus : function(){
for(var i = 0, count = this.points.length; i < count; i++){
this.points[i].updateSelf();
}
for(var i = 0, count = this.points.length; i < count; i++){
this.points[i].updateNeighbors();
}
if(this.fishes.length < this.fishCount){
if(--this.intervalCount == 0){
this.intervalCount = this.MAX_INTERVAL_COUNT;
this.fishes.push(new FISH(this));
}
}
},
render : function(){
requestAnimationFrame(this.render);
this.controlStatus();
this.context.clearRect(0, 0, this.width, this.height);
this.context.fillStyle = 'hsl(0, 0%, 95%)';
for(var i = 0, count = this.fishes.length; i < count; i++){
this.fishes[i].render(this.context);
}
this.context.save();
this.context.globalCompositeOperation = 'xor';
this.context.beginPath();
this.context.moveTo(0, this.reverse ? 0 : this.height);
for(var i = 0, count = this.points.length; i < count; i++){
this.points[i].render(this.context);
}
this.context.lineTo(this.width, this.reverse ? 0 : this.height);
this.context.closePath();
this.context.fill();
this.context.restore();
}
};
var SURFACE_POINT = function(renderer, x){
this.renderer = renderer;
this.x = x;
this.init();
};
SURFACE_POINT.prototype = {
SPRING_CONSTANT : 0.03,
SPRING_FRICTION : 0.9,
WAVE_SPREAD : 0.3,
ACCELARATION_RATE : 0.01,
init : function(){
this.initHeight = this.renderer.height * this.renderer.INIT_HEIGHT_RATE;
this.height = this.initHeight;
this.fy = 0;
this.force = {previous : 0, next : 0};
},
setPreviousPoint : function(previous){
this.previous = previous;
},
setNextPoint : function(next){
this.next = next;
},
interfere : function(y, velocity){
this.fy = this.renderer.height * this.ACCELARATION_RATE * ((this.renderer.height - this.height - y) >= 0 ? -1 : 1) * Math.abs(velocity);
},
updateSelf : function(){
this.fy += this.SPRING_CONSTANT * (this.initHeight - this.height);
this.fy *= this.SPRING_FRICTION;
this.height += this.fy;
},
updateNeighbors : function(){
if(this.previous){
this.force.previous = this.WAVE_SPREAD * (this.height - this.previous.height);
}
if(this.next){
this.force.next = this.WAVE_SPREAD * (this.height - this.next.height);
}
},
render : function(context){
if(this.previous){
this.previous.height += this.force.previous;
this.previous.fy += this.force.previous;
}
if(this.next){
this.next.height += this.force.next;
this.next.fy += this.force.next;
}
context.lineTo(this.x, this.renderer.height - this.height);
}
};
var FISH = function(renderer){
this.renderer = renderer;
this.init();
};
FISH.prototype = {
GRAVITY : 0.4,
init : function(){
this.direction = Math.random() < 0.5;
this.x = this.direction ? (this.renderer.width + this.renderer.THRESHOLD) : -this.renderer.THRESHOLD;
this.previousY = this.y;
this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1);
if(this.renderer.reverse){
this.y = this.getRandomValue(this.renderer.height * 1 / 10, this.renderer.height * 4 / 10);
this.vy = this.getRandomValue(2, 5);
this.ay = this.getRandomValue(0.05, 0.2);
}else{
this.y = this.getRandomValue(this.renderer.height * 6 / 10, this.renderer.height * 9 / 10);
this.vy = this.getRandomValue(-5, -2);
this.ay = this.getRandomValue(-0.2, -0.05);
}
this.isOut = false;
this.theta = 0;
this.phi = 0;
},
getRandomValue : function(min, max){
return min + (max - min) * Math.random();
},
reverseVertical : function(){
this.isOut = !this.isOut;
this.ay *= -1;
},
controlStatus : function(context){
this.previousY = this.y;
this.x += this.vx;
this.y += this.vy;
this.vy += this.ay;
if(this.renderer.reverse){
if(this.y > this.renderer.height * this.renderer.INIT_HEIGHT_RATE){
this.vy -= this.GRAVITY;
this.isOut = true;
}else{
if(this.isOut){
this.ay = this.getRandomValue(0.05, 0.2);
}
this.isOut = false;
}
}else{
if(this.y < this.renderer.height * this.renderer.INIT_HEIGHT_RATE){
this.vy += this.GRAVITY;
this.isOut = true;
}else{
if(this.isOut){
this.ay = this.getRandomValue(-0.2, -0.05);
}
this.isOut = false;
}
}
if(!this.isOut){
this.theta += Math.PI / 20;
this.theta %= Math.PI * 2;
this.phi += Math.PI / 30;
this.phi %= Math.PI * 2;
}
this.renderer.generateEpicenter(this.x + (this.direction ? -1 : 1) * this.renderer.THRESHOLD, this.y, this.y - this.previousY);
if(this.vx > 0 && this.x > this.renderer.width + this.renderer.THRESHOLD || this.vx < 0 && this.x < -this.renderer.THRESHOLD){
this.init();
}
},
render : function(context){
context.save();
context.translate(this.x, this.y);
context.rotate(Math.PI + Math.atan2(this.vy, this.vx));
context.scale(1, this.direction ? 1 : -1);
context.beginPath();
context.moveTo(-30, 0);
context.bezierCurveTo(-20, 15, 15, 10, 40, 0);
context.bezierCurveTo(15, -10, -20, -15, -30, 0);
context.fill();
context.save();
context.translate(40, 0);
context.scale(0.9 + 0.2 * Math.sin(this.theta), 1);
context.beginPath();
context.moveTo(0, 0);
context.quadraticCurveTo(5, 10, 20, 8);
context.quadraticCurveTo(12, 5, 10, 0);
context.quadraticCurveTo(12, -5, 20, -8);
context.quadraticCurveTo(5, -10, 0, 0);
context.fill();
context.restore();
context.save();
context.translate(-3, 0);
context.rotate((Math.PI / 3 + Math.PI / 10 * Math.sin(this.phi)) * (this.renderer.reverse ? -1 : 1));
context.beginPath();
if(this.renderer.reverse){
context.moveTo(5, 0);
context.bezierCurveTo(10, 10, 10, 30, 0, 40);
context.bezierCurveTo(-12, 25, -8, 10, 0, 0);
}else{
context.moveTo(-5, 0);
context.bezierCurveTo(-10, -10, -10, -30, 0, -40);
context.bezierCurveTo(12, -25, 8, -10, 0, 0);
}
context.closePath();
context.fill();
context.restore();
context.restore();
this.controlStatus(context);
}
};
$(function(){
RENDERER.init();
});并在主题配置文件
_config.butterfly.yml
的bottom
处引入该文件:1
2
3inject:
bottom:
- <script src="/js/fish.js"></script> -
在
\themes\butterfly\source\css
目录下新建footer.css
文件,并写入如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16.fish_container{
z-index: -1;
width: "100%";
height: "160px";
margin: 0;
padding: 0;
}
#footer-wrap{
position: absolute;
text-align: center;
padding: 20px 20px;
top: 0;
right: 0;
left: 0;
bottom: 0;
}并在主题配置文件
_config.butterfly.yml
的head
处引入该文件:1
2
3inject:
head:
- <link rel="stylesheet" href="/css/footer.css"> -
保存配置,重新部署,即可看到效果。
方法二
打开站点的主题配置文件_config.butterfly.yml
,找到inject
,在bottom
处直接引入以下链接:
1 | - <script src="https://gcore.jsdelivr.net/gh/xiabo2/CDN@latest/fishes.js"></script> |
方法三
-
找到
footer.pug
文件:\themes\butterfly\layout\includes\footer.pug
,写入以下内容:1
2
3
4
5
6
7#jsi-flying-fish-container.container
script(src='js/fish.js')
style.
@media only screen and (max-width: 767px){
#sidebar_search_box input[type=text]{width:calc(100% - 24px)}
} -
打开站点的主题配置文件
_config.butterfly.yml
,找到inject
,在bottom
处直接引入https://gcore.jsdelivr.net/gh/xiabo2/CDN@latest/fish.js1
- <script src="https://gcore.jsdelivr.net/gh/xiabo2/CDN@latest/fish.js"></script>
注意:引入的js文件需要依赖
jquery
,所以在之前如果没有引用过的就需要在bottom
处的最开始引入:1
- <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
-
这样效果就出现了。但是引入之后页脚会过高,需要修改
footer.styl
文件:\themes\butterfly\source\css\_layout\footer.styl
: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#footer
position: relative
background: $light-blue
background-attachment: local
background-position: bottom
background-size: cover
if hexo-config('footer_bg') != false
&:before
position: absolute
width: 100%
height: 100%
background-color: alpha($dark-black, .1)
content: ''
#footer-wrap
position: absolute
padding: 1.2rem 1rem 1.4rem
color: var(--light-grey)
text-align: center
left: 0
right: 0
top: 0
bottom: 0
a
color: var(--light-grey)
&:hover
text-decoration: underline
.footer-separator
margin: 0 .2rem
.icp-icon
padding: 0 4px
vertical-align: text-bottom
max-height: 1.4em
width auto -
参考文章的技术流程写到这里就结束了,如果还有页脚文字被遮挡的情况,可以修改页脚的透明度,在
\themes\butterfly\source\css
路径下创建一个xxx.css
文件,在文件中添加如下代码:-
页脚半透明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/* 页脚半透明 */
#footer {
background: rgba(255, 255, 255, 0);
color: #000;
border-top-right-radius: 20px;
border-top-left-radius: 20px;
backdrop-filter: saturate(100%) blur(5px)
}
#footer::before {
background: rgba(255,255,255,0)
}
#footer #footer-wrap {
color: var(--font-color);
}
#footer #footer-wrap a {
color: var(--font-color);
} -
页脚全透明
1
2
3
4/* 页脚透明 */
#footer {
background: transparent ;
}
然后将该文件引入到
inject
的head
处:1
- <link rel="stylesheet" href="/css/xxx.css">
-
-
至此操作结束,去看看你的页脚有没有发生变化吧。
补充
还有一个更为简单的方法,引入带有颜色的页脚养鱼,效果如下:
只需要在_config.butterfly.yml
文件中,找到inject
,在bottom
处直接引入:https://uuuuu.cf/js/fishes.js
1 | - <script defer src="https://uuuuu.cf/js/fishes.js"></script> # 页脚养鱼(彩色) |
注意:该操作同样需要在之前引入jquery
。
结尾
在魔改的道路上乐此不疲,虽然有时真的很“崩溃”。