hexo butterfly 养鱼来了

js

没有请创建 blog/source/js/fishes.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
358
359
360
361
362
363
fish();
function fish() {
return (
$("footer").append(
'<div class="container" id="jsi-flying-fish-container"></div>'
),
$(".container").css({
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: 2,
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.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.skyGradient = this.context.createLinearGradient(0, 0, 0, this.height);
this.skyGradient.addColorStop(0, "hsl(180, 80%, 70%)");
this.skyGradient.addColorStop(1, "hsl(180, 80%, 90%)");

this.seaGradient = this.context.createLinearGradient(0, 0, 0, this.height);
this.seaGradient.addColorStop(0, "hsl(180, 80%, 60%)");
this.seaGradient.addColorStop(0.5, "hsl(180, 80%, 50%)");
this.seaGradient.addColorStop(1, "hsl(210, 80%, 50%)");

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);
},
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);
},
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.fillStyle = this.skyGradient;
this.context.fillRect(0, 0, this.width, this.height);
this.context.fillStyle = this.seaGradient;
this.context.beginPath();
this.context.moveTo(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.height);
this.context.closePath();
this.context.fill();

for (var i = 0, count = this.fishes.length; i < count; i++) {
this.fishes[i].render(this.context);
}
},
};
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.y = this.getRandomValue(
(this.renderer.height * 6) / 10,
(this.renderer.height * 9) / 10
);
this.previousY = this.y;
this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1);
this.vy = this.getRandomValue(-5, -2);
this.ay = this.getRandomValue(-0.2, -0.05);
this.theta = 0;
this.phi = 0;
this.isOut = false;
},
getRandomValue: function (min, max) {
return min + (max - min) * Math.random();
},
controlStatus: function (context) {
this.previousY = this.y;
this.x += this.vx;
this.y += this.vy;
this.vy += this.ay;

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.fillStyle = "hsl(180, 30%, 80%)";
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.fillStyle = "hsl(180, 30%, 80%)";
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));
context.fillStyle = "hsl(220, 80%, 40%)";
context.beginPath();
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.strokeStyle = "hsl(220, 80%, 40%)";
context.beginPath();
context.arc(-15, -3, 5, 0, Math.PI * 2, false);
context.stroke();

context.fillStyle = "hsl(220, 80%, 40%)";
context.beginPath();
context.arc(-15, -3, 3, 0, Math.PI * 2, false);
context.fill();
context.restore();

this.controlStatus(context);
},
};
$(function () {
RENDERER.init();
});

配置

开启 pjax

_configy.butterfly.yml 相应位置修改

1
2
3
4
5
6

inject:
head:
bottom:
- <script defer src="https://rmt.dogedoge.com/fetch/~/source/jsdelivr/npm/jquery@latest/dist/jquery.min.js"></script>
- <script defer data-pjax src="/js/fishes.js"></script>

未开启 pjax

_configy.butterfly.yml 相应位置修改

1
2
3
4
5
inject:
head:
bottom:
- <script defer src="https://rmt.dogedoge.com/fetch/~/source/jsdelivr/npm/jquery@latest/dist/jquery.min.js"></script>
- <script defer src="/js/fishes.js"></script>

前言

页脚养鱼效果我也是折腾了蛮久的,奈何自己太菜,出现了一些问题也不知道怎么解决,查询一些博客,相关的描述也很简单。这里参考了Ln大佬的博客,总算是搞定了。

Ln’s Blog ——《如何在页脚养鱼》:https://weilining.github.io/204.html

最终效果在本站页脚,向下滑动即可看到。


操作方法

参考文章一共介绍了两种方法,第一种是有针对于Butterfly主题的修改。第二种方法是通用方法(其他也主题可参考)

方法一

打开站点的主题配置文件_config.butterfly.yml,找到inject,在bottom处直接引入以下链接:

1
- <script src="https://gcore.jsdelivr.net/gh/xiabo2/CDN@latest/fishes.js"></script>

方法二

  1. 找到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)}
    }
  2. 打开站点的主题配置文件_config.butterfly.yml,找到inject,在bottom处直接引入https://gcore.jsdelivr.net/gh/xiabo2/CDN@latest/fish.js

    1
    - <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>
  3. 这样效果就出现了。但是引入之后页脚会过高,需要修改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
  4. 参考文章的技术流程写到这里就结束了,如果还有页脚文字被遮挡的情况,可以修改页脚的透明度,在\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 !important;
      }

    然后将该文件引入到injecthead处:

    1
    - <link rel="stylesheet" href="/css/xxx.css">
  5. 至此操作结束,去看看你的页脚有没有发生变化吧。

补充

还有一个更为简单的方法,引入带有颜色的页脚养鱼,效果如下:

img

只需要在_config.butterfly.yml文件中,找到inject,在bottom处直接引入:https://blog.4t.pw/js/wz/fishes.js

1
2
3
4
5
- <script src="https://cdn.staticfile.org/jquery/3.6.3/jquery.min.js"></script>
- <script defer src="https://blog.4t.pw/js/wz/fishes.js"></script> # 页脚养鱼(彩色)
# 黑白个人喜好添加
- <script defer src="https://blog.4t.pw/js/wz/fish.js"></script> # 页脚养鱼(黑白)

下边空白消失css

1
2
3
4
5
.container>canvas {
position: absolute;
left: 0px;
top: 10px;
}

注意:该操作同样需要在之前引入jquery。可以添加版权栏jquery可能会和主题冲突注意


结尾

在魔改的道路上乐此不疲,虽然有时真的很“崩溃”。