✨ 我是流萤白沙的「文章捕手」,擅长在文字的星海中打捞精华。每当新的篇章诞生,我就会像整理贝壳一样,将思想的闪光点串成珍珠项链~
本文介绍了如何在Hexo博客中实现樱花漂浮效果的插件。首先,作者在已有的基础上进行修改,创建了适用于shokax主题的插件。文章详细描述了插件的创建步骤,包括在博客根目录下创建JavaScript和Pug文件,并提供了相应的代码。功能代码部分涵盖了漂浮物的生成、更新和绘制逻辑,同时允许用户自定义漂浮物的显示页面和图片。最后,提供了使用方法,指导用户如何设置插件以便在特定页面显示漂浮效果,增强了博客的视觉效果和互动性。
# 前言
在逛其他人博客时,发现很多都有动态效果,我的虽然也有一点(大轮播图和波浪),但左看右看还是感觉单调了一点,于是我发挥聪明才智去抄了一份,做成了插件实现了这个樱花效果。(其实就是把别人代码抄过来改成我能用的格式)
参考文章 Hexo 博客添加漂浮物效果(可定制显示页面) | 半方池水半方田。
感谢 @wuanqin 大佬提供的源码。
# 实现
# 创建插件
适用于 shokax
主题,如果是其他主题,可以使用 Hexo Injector 或主题提供的注入功能实现。
- 在
博客根目录/scripts
文件夹里创建sakura.js
,如果没有scripts
就创建一个。 - 在
博客根目录/views
文件夹里创建sakura.pug
,如果没有view
就创建一个。
# 功能代码
- 将下面代码复制到
sakura.js
里。
hexo.extend.filter.register('theme_inject', function(injects) { | |
injects.bodyEnd.file('sakura','views/sakura.pug',{}, {cache: false}); | |
}); |
- 将下面代码复制到
sakura.pug
里。
script. | |
// 漂浮物 | |
// 新增函数 | |
// 首先获取 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 = true; // true 则允许所有网页存在漂浮物 | |
// 允许显示漂浮物的网址列表 | |
var urlAllowList = [ | |
"/about/", | |
"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 = "/assets/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); |
js 格式代码如下。
// 漂浮物 | |
// 新增函数 | |
// 首先获取 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); |
# 使用方法
- 需要所有博客页面都显示漂浮物:将
allowAll = true
- 指定显示漂浮物的页面:将
allowAll = false
,在 urlAllowList 中列出需要显示漂浮物的网页相对地址。如/about/
。 - 指定漂浮物图片:把
img.src = "/assets/float.png"
,替换成你喜欢的图片 URL。
都做完之后记得素质三连:hexo clean && hexo g && hexo d