Sun-Panel-js源码

插件集地址:https://github.com/hslr-s/sun-panel-js-plugins
Sun-Panel:https://doc.sun-panel.top/zh_cn

一 、显示概览快速跳转

1 、创建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
touch /mnt/mydisk/home/sun-panel/config/custom/index.js && cat > /mnt/mydisk/home/sun-panel/config/custom/index.js <<'EOF'
(function () {
// =========== Config Start ===========
// ------------------------------------
// 距离滚动偏移量
const scrollOffset = 80

// 显示风格( auto:自动(默认) | mobile:左上角显示触发按钮-移动端风格 | sidebar:常态显示侧栏)
const displayStyle = 'auto'

// 移动端宽度定义
const mobileWidth = 800

const SunPanelTOCDomIdName = 'sun-panel-toc-dom'

// 左上角按钮 SVG 图标
const svgTocMobileBtn = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M17.5 4.5c-1.95 0-4.05.4-5.5 1.5c-1.45-1.1-3.55-1.5-5.5-1.5c-1.45 0-2.99.22-4.28.79C1.49 5.62 1 6.33 1 7.14v11.28c0 1.3 1.22 2.26 2.48 1.94c.98-.25 2.02-.36 3.02-.36c1.56 0 3.22.26 4.56.92c.6.3 1.28.3 1.87 0c1.34-.67 3-.92 4.56-.92c1 0 2.04.11 3.02.36c1.26.33 2.48-.63 2.48-1.94V7.14c0-.81-.49-1.52-1.22-1.85c-1.28-.57-2.82-.79-4.27-.79M21 17.23c0 .63-.58 1.09-1.2.98c-.75-.14-1.53-.2-2.3-.2c-1.7 0-4.15.65-5.5 1.5V8c1.35-.85 3.8-1.5 5.5-1.5c.92 0 1.83.09 2.7.28c.46.1.8.51.8.98z"/><path fill="currentColor" d="M13.98 11.01c-.32 0-.61-.2-.71-.52c-.13-.39.09-.82.48-.94c1.54-.5 3.53-.66 5.36-.45c.41.05.71.42.66.83s-.42.71-.83.66c-1.62-.19-3.39-.04-4.73.39c-.08.01-.16.03-.23.03m0 2.66c-.32 0-.61-.2-.71-.52c-.13-.39.09-.82.48-.94c1.53-.5 3.53-.66 5.36-.45c.41.05.71.42.66.83s-.42.71-.83.66c-1.62-.19-3.39-.04-4.73.39a1 1 0 0 1-.23.03m0 2.66c-.32 0-.61-.2-.71-.52c-.13-.39.09-.82.48-.94c1.53-.5 3.53-.66 5.36-.45c.41.05.71.42.66.83s-.42.7-.83.66c-1.62-.19-3.39-.04-4.73.39a1 1 0 0 1-.23.03"/></svg>'

// ------------------------------------
// =========== Config End ===========

// 滚动容器的类名
const scrollContainerElementClassName = '.scroll-container'

// 一些函数
const isMobile = () => {
if (displayStyle === 'mobile') {
return true
}
else if (displayStyle === 'pc') {
return false
}
const width = window.innerWidth
return width < mobileWidth
}

function createDom() {
// 检测是否已经存在TOC DOM,存在则删除
(function () {
const element = document.getElementById(SunPanelTOCDomIdName)
if (element) {
element.remove()
}
})()

const SunPanelTOCDom = document.createElement('div')
SunPanelTOCDom.id = SunPanelTOCDomIdName
document.body.appendChild(SunPanelTOCDom)

// ========= Add style start =========
const style = document.createElement('style')
const SunPanelTOCDomStyleId = `#${SunPanelTOCDomIdName}`
style.textContent = `
${SunPanelTOCDomStyleId} #toc-mobile-btn {
top: 20px !important;
left: 20px !important;
position: fixed;
width: 46px;
height: 46px;
background-color: #2a2a2a6b;
color: white;
border-radius: 0.5rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}

${SunPanelTOCDomStyleId} .hidden {
display: none !important;
}

${SunPanelTOCDomStyleId} #toc-sidebar {
width: 40px;
padding: 10px;
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
transition: width 0.3s ease, background-color 0.3s ease;
border-top-right-radius: 20px;
border-bottom-right-radius: 20px;
background-color: none;
}

${SunPanelTOCDomStyleId} .toc-mobile-btn-svg-container{
width:21px;
height:21px;
}

${SunPanelTOCDomStyleId} .toc-sidebar-expansion {
width: 200px !important;
display: flex;
background-color: rgb(42 42 42 / 90%);
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
}

${SunPanelTOCDomStyleId} #toc-sidebar .toc-sidebar-box {
width: 500px;
}

${SunPanelTOCDomStyleId} .title-bar-box {
display: flex;
align-items: center;
position: relative;
cursor: pointer;
}

${SunPanelTOCDomStyleId} .title-bar-slip {
width: 20px;
height: 6px;
background-color: white;
border-radius: 4px;
margin: 15px 0;
transition: height 0.3s ease, width 0.3s ease;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.5);
}

${SunPanelTOCDomStyleId} .title-bar-title {
opacity: 0;
white-space: nowrap;
transition: opacity 0.3s ease, transform 0.3s ease, margin-left 0.3s ease;
font-size: 14px;
color: white;
}

${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-title {
opacity: 1;
margin-left: 10px;
}

${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-slip {
box-shadow: none;
}

${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-box:hover .title-bar-slip {
width: 40px;
}

${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-box:hover .title-bar-title {
font-size: 20px;
}

`
// 添加样式到文档头部
SunPanelTOCDom.appendChild(style)

// ========= Add style end =========

// 添加移动端菜单按钮
const tocMobileBtn = document.createElement('div')
tocMobileBtn.id = 'toc-mobile-btn'
tocMobileBtn.classList.add('backdrop-blur-[2px]')
SunPanelTOCDom.appendChild(tocMobileBtn)

const tocMobileBtnSvgcContainer = document.createElement('div')
tocMobileBtnSvgcContainer.innerHTML = svgTocMobileBtn
tocMobileBtnSvgcContainer.classList.add('toc-mobile-btn-svg-container')
tocMobileBtn.appendChild(tocMobileBtnSvgcContainer)

// 创建侧边栏容器
const sidebar = document.createElement('div')
sidebar.id = 'toc-sidebar'

const sidebarBox = document.createElement('div')
sidebarBox.className = 'toc-sidebar-box'

// 查询出所有类名包含 item-group-index- 的元素
const items = document.querySelectorAll('[class*="item-group-index-"]')

// 遍历并打印每个元素的完整类名
items.forEach((item) => {
item.classList.forEach((className) => {
if (className.startsWith('item-group-index-')) {
const titleBarBox = document.createElement('div')
titleBarBox.className = 'title-bar-box'
// titleBarBox.href = `#${item.id}`
titleBarBox.dataset.groupClassName = className

// 目录条
const titleBarSlip = document.createElement('div')
titleBarSlip.className = 'title-bar-slip'

// 创建一个链接
const titleBarTitle = document.createElement('div')
titleBarTitle.className = 'title-bar-title'

// 获取子元素中 class="group-title" 的内容
const titleElement = item.querySelector('.group-title')
const titleText = titleElement ? titleElement.textContent : item.id
titleBarTitle.textContent = titleText

titleBarBox.appendChild(titleBarSlip)
titleBarBox.appendChild(titleBarTitle)

sidebarBox.appendChild(titleBarBox)
}
})
})

sidebar.appendChild(sidebarBox)

// 将侧边栏添加到页面中
SunPanelTOCDom.appendChild(sidebar)

function mobileHideSidebar() {
sidebar.classList.remove('toc-sidebar-expansion')
sidebar.classList.add('hidden')
}

function hideSidebar() {
sidebar.classList.remove('toc-sidebar-expansion')
}

function showSidebar() {
sidebar.classList.add('toc-sidebar-expansion')
sidebar.classList.remove('hidden')
}

// ----------------
// 监听宽度变化开始
// ----------------
function debounce(func, wait) {
let timeout
return function (...args) {
clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}

function handleResize() {
if (isMobile()) {
tocMobileBtn.classList.remove('hidden')
sidebar.classList.add('hidden')
}
else {
tocMobileBtn.classList.add('hidden')
sidebar.classList.remove('hidden')
}
}

// 使用防抖函数包装你的处理函数
const debouncedHandleResize = debounce(handleResize, 200)

// 添加事件监听器
window.addEventListener('resize', debouncedHandleResize)

// 首次触发
handleResize()

// ----------------
// 监听宽度变化结束
// ----------------

// 监听移动端按钮点击
tocMobileBtn.addEventListener('click', () => {
if (sidebar.classList.contains('toc-sidebar-expansion')) {
// 隐藏
mobileHideSidebar()
}
else {
// 显示
showSidebar()
}
})

// 监听TOC栏失去hover
sidebar.addEventListener('mouseleave', () => {
if (isMobile()) {
// 隐藏
mobileHideSidebar()
}
else {
hideSidebar()
}
})

// 监听TOC栏获得hover
sidebar.addEventListener('mouseenter', () => {
showSidebar()
})

// 监听TOC点击事件
document.querySelectorAll('.title-bar-box').forEach((box) => {
box.addEventListener('click', function (event) {
// 检查触发事件的元素是否有 'data-groupClassName' 属性
if (this.dataset.groupClassName) {
// 获取 'data-groupClass' 属性的值
const groupClassName = this.dataset.groupClassName
// 使用属性值作为选择器查询对应的元素
const targetElement = document.querySelector(`.${groupClassName}`)
if (targetElement) {
// 获取目标元素的 'top' 坐标
const targetTop = targetElement.offsetTop
const scrollContainerElement = document.querySelector(scrollContainerElementClassName)
if (scrollContainerElement) {
scrollContainerElement.scrollTo({
top: targetTop - scrollOffset,
behavior: 'smooth', // 平滑滚动
})
}
}
}
})
})
}

// 判断是否已经存在分组,不存在将定时监听
const items = document.querySelectorAll('[class*="item-group-index-"]')
if (items.length > 0) {
createDom()
return
}

const interval = setInterval(() => {
const items = document.querySelectorAll('[class*="item-group-index-"]')
if (items.length > 0) {
createDom()
clearInterval(interval)
}
}, 1000)
})()
EOF

2 、引用 index.js

Sun-Panel个性化设置,自定义页脚,粘贴以下内容

1
<script src="/custom/index.js"></script>

二 、页面雪花特效

1 、创建 xuehua.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
touch /mnt/mydisk/home/sun-panel/config/custom/xuehua.js && cat > /mnt/mydisk/home/sun-panel/config/custom/xuehua.js <<'EOF'
function snow() {
// 1、定义一片雪花模板
var flake = document.createElement('div');
// 定义雪花字符 ❄❉❅❆✻✼❇❈❊✥✺
flake.innerHTML = '❄';
flake.style.cssText = 'position:absolute;color:#fff;';

//获取页面的高度 相当于雪花下落结束时Y轴的位置
var documentHieght = window.innerHeight;
//获取页面的宽度,利用这个数来算出,雪花开始时left的值
var documentWidth = window.innerWidth;

//定义生成一片雪花的毫秒数,不建议低于100
var millisec =200;
//2、设置第一个定时器,周期性定时器,每隔一段时间(millisec)生成一片雪花;
setInterval(function() { //页面加载之后,定时器就开始工作
//随机生成雪花下落 开始 时left的值,相当于开始时X轴的位置
var startLeft = Math.random() * documentWidth;

//随机生成雪花下落 结束 时left的值,相当于结束时X轴的位置
var endLeft = Math.random() * documentWidth;

//随机生成雪花大小
var flakeSize = 3 + 20 * Math.random();

//随机生成雪花下落持续时间,时间越大下落越慢
var durationTime = 6000 + 10000 * Math.random();

//随机生成雪花下落 开始 时的透明度
var startOpacity = 0.7 + 0.3 * Math.random();

//随机生成雪花下落 结束 时的透明度
var endOpacity = 0.2 + 0.2 * Math.random();

//克隆一个雪花模板
var cloneFlake = flake.cloneNode(true);

//第一次修改样式,定义克隆出来的雪花的样式
cloneFlake.style.cssText += `
left: ${startLeft}px;
opacity: ${startOpacity};
font-size:${flakeSize}px;
top:-25px;
transition:${durationTime}ms;`;

//拼接到页面中
document.body.appendChild(cloneFlake);

//设置第二个定时器,一次性定时器,
//当第一个定时器生成雪花,并在页面上渲染出来后,修改雪花的样式,让雪花动起来;
setTimeout(function() {
//第二次修改样式
cloneFlake.style.cssText += `
left: ${endLeft}px;
top:${documentHieght}px;
opacity:${endOpacity};`;

//4、设置第三个定时器,当雪花落下后,删除雪花。
setTimeout(function() {
cloneFlake.remove();
}, durationTime);
}, 0);

}, millisec);
}
snow();
EOF

2 、引用 xuehua.js

Sun-Panel个性化设置–自定义页脚,粘贴以下内容

1
<script src="/custom/xuehua.js"></script>

三 、mp4视频壁纸

1 、创建mp4.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
touch /mnt/mydisk/home/sun-panel/config/custom/mp4.js && cat > /mnt/mydisk/home/sun-panel/config/custom/mp4.js <<'EOF'
document.addEventListener('DOMContentLoaded', (event) => {
// 用于添加视频背景的函数
const addVideoBackground = (wallpaperDiv) => {
// 创建video元素
const video = document.createElement('video');

// 设置视频属性
video.autoplay = true; // 使用属性而不是setAttribute,因为autoplay是一个布尔属性
video.loop = true;
video.muted = true;
video.playsInline = true; // 注意这里是驼峰式写法,但在HTML中应使用playsinline

// 应用CSS样式到video元素
video.style.position = 'absolute';
video.style.top = '0';
video.style.left = '0';
video.style.width = '100%';
video.style.height = '100%';
video.style.objectFit = 'cover';

// 定义多个视频源
const videoSources = [
'/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-01.mp4',
'/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-02.mp4',
'/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-03.mp4',
'/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-04.mp4',
'/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-05.mp4',
// '/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-06.mp4',
// '/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-07.mp4',
// '/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-08.mp4',
// '/uploads/%E5%8A%A8%E6%80%81%E5%A3%81%E7%BA%B8-09.mp4'
// 在这里可以添加更多的视频源
];

// 从视频源数组中随机选择一个
const randomSource = videoSources[Math.floor(Math.random() * videoSources.length)];

// 创建source元素并设置随机选择的视频源
const source = document.createElement('source');
source.src = randomSource;
source.type = 'video/mp4';

// 将source元素添加到video元素中
video.appendChild(source);

// 监听视频加载元数据事件,尝试播放视频
video.addEventListener('loadedmetadata', () => {
video.play().catch((error) => {
// 如果播放失败,可以在这里处理错误,例如显示一个备用图片或消息
console.error('视频播放失败:', error);
});
});

// 将video元素添加到指定的wallpaperDiv中
wallpaperDiv.appendChild(video);
};

// 使用MutationObserver监视DOM变化
const observer = new MutationObserver((mutationsList, observer) => {
// 查找匹配的.cover.wallpaper元素
const wallpaperDiv = document.querySelector('.cover.wallpaper');

if (wallpaperDiv && !wallpaperDiv.querySelector('video')) {
// 添加视频背景
addVideoBackground(wallpaperDiv);
// 注意:我们不再断开观察者,以便它能够继续监视未来的变化
}
});

// 启动观察者监视document.body的变化
observer.observe(document.body, { childList: true, subtree: true });
});

EOF

2 、引用 mp4.js

Sun-Panel个性化设置–自定义页脚,粘贴以下内容

1
<script src="/custom/mp4.js"></script>

四 、点击特效-散开

1 、创建 dianji.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
touch /mnt/mydisk/home/sun-panel/config/custom/dianji.js && cat > /mnt/mydisk/home/sun-panel/config/custom/dianji.js <<'EOF'
class Circle {
constructor({ origin, speed, color, angle, context }) {
this.origin = origin; // 初始位置
this.position = { ...this.origin }; // 当前位置
this.color = color; // 颜色
this.speed = speed; // 速度
this.angle = angle; // 角度
this.context = context; // 绘制上下文
this.renderCount = 0; // 渲染计数
}

draw() {
// 绘制圆形
this.context.fillStyle = this.color;
this.context.beginPath();
this.context.arc(this.position.x, this.position.y, 2, 0, Math.PI * 2);
this.context.fill();
}

move() {
// 移动圆形
this.position.x = (Math.sin(this.angle) * this.speed) + this.position.x;
this.position.y = (Math.cos(this.angle) * this.speed) + this.position.y + (this.renderCount * 0.3);
this.renderCount++;
}
}

class Boom {
constructor({ origin, context, circleCount = 10, area }) {
this.origin = origin; // 初始位置
this.context = context; // 绘制上下文
this.circleCount = circleCount; // 圆形数量
this.area = area; // 区域
this.stop = false; // 停止标志
this.circles = []; // 圆形数组
}

randomArray(range) {
// 从数组中随机取值
const length = range.length;
const randomIndex = Math.floor(length * Math.random());
return range[randomIndex];
}

randomColor() {
// 随机生成颜色
const range = ['8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
return '#' + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range);
}

randomRange(start, end) {
// 在范围内生成随机数
return (end - start) * Math.random() + start;
}

init() {
// 初始化Boom对象
for (let i = 0; i < this.circleCount; i++) {
const circle = new Circle({
context: this.context,
origin: this.origin,
color: this.randomColor(),
angle: this.randomRange(Math.PI - 1, Math.PI + 1),
speed: this.randomRange(1, 6)
});
this.circles.push(circle);
}
}

move() {
// 移动所有圆形,并检测是否超出区域
this.circles.forEach((circle, index) => {
if (circle.position.x > this.area.width || circle.position.y > this.area.height) {
return this.circles.splice(index, 1);
}
circle.move();
});
if (this.circles.length === 0) {
this.stop = true;
}
}

draw() {
// 绘制所有圆形
this.circles.forEach(circle => circle.draw());
}
}

class CursorSpecialEffects {
constructor() {
// 鼠标特效主类
this.computerCanvas = document.createElement('canvas'); // 计算用Canvas
this.renderCanvas = document.createElement('canvas'); // 渲染用Canvas

this.computerContext = this.computerCanvas.getContext('2d'); // 计算用上下文
this.renderContext = this.renderCanvas.getContext('2d'); // 渲染用上下文

this.globalWidth = window.innerWidth; // 全局宽度
this.globalHeight = window.innerHeight; // 全局高度

this.booms = []; // Boom对象数组
this.running = false; // 运行标志
}

handleMouseDown(e) {
// 处理鼠标点击事件
const boom = new Boom({
origin: { x: e.clientX, y: e.clientY },
context: this.computerContext,
area: {
width: this.globalWidth,
height: this.globalHeight
}
});
boom.init();
this.booms.push(boom);
this.running || this.run();
}

handlePageHide() {
// 处理页面隐藏事件
this.booms = [];
this.running = false;
}

init() {
// 初始化方法
const style = this.renderCanvas.style;
style.position = 'fixed';
style.top = style.left = 0;
style.zIndex = '999999999999999999999999999999999999999999';
style.pointerEvents = 'none';

style.width = this.renderCanvas.width = this.computerCanvas.width = this.globalWidth;
style.height = this.renderCanvas.height = this.computerCanvas.height = this.globalHeight;

document.body.append(this.renderCanvas);

window.addEventListener('mousedown', this.handleMouseDown.bind(this));
window.addEventListener('pagehide', this.handlePageHide.bind(this));
}

run() {
// 运行方法
this.running = true;
if (this.booms.length === 0) {
return this.running = false;
}

requestAnimationFrame(this.run.bind(this));

this.computerContext.clearRect(0, 0, this.globalWidth, this.globalHeight);
this.renderContext.clearRect(0, 0, this.globalWidth, this.globalHeight);

this.booms.forEach((boom, index) => {
if (boom.stop) {
return this.booms.splice(index, 1);
}
boom.move();
boom.draw();
});
this.renderContext.drawImage(this.computerCanvas, 0, 0, this.globalWidth, this.globalHeight);
}
}

const cursorSpecialEffects = new CursorSpecialEffects();
cursorSpecialEffects.init();
EOF

2 、引用 dianji.js

Sun-Panel个性化设置–自定义页脚,粘贴以下内容

1
<script src="/custom/dianji.js"></script>

五 、点击特效-烟花

1 、创建 yanhua.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
touch /mnt/mydisk/home/sun-panel/config/custom/index.js && cat > /mnt/mydisk/home/sun-panel/config/custom/yanhua.js <<'EOF'
function clickFireworksEffects() {
let balls = [];
let longPressed = false;
let longPress;
let multiplier = 0;
let width, height;
let origin;
let normal;
let ctx;
const colours = ["#F73859", "#14FFEC", "#00E0FF", "#FF99FE", "#FAF15D"];
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.setAttribute("style", "width: 100%; height: 100%; top: 0; left: 0; z-index: 99999; position: fixed; pointer-events: none;");
const pointer = document.createElement("span");
pointer.classList.add("pointer");
document.body.appendChild(pointer);

if (canvas.getContext && window.addEventListener) {
ctx = canvas.getContext("2d");
updateSize();
window.addEventListener('resize', updateSize, false);
loop();
window.addEventListener("mousedown", function(e) {
pushBalls(randBetween(10, 20), e.clientX, e.clientY);
document.body.classList.add("is-pressed");
longPress = setTimeout(function(){
document.body.classList.add("is-longpress");
longPressed = true;
}, 500);
}, false);
window.addEventListener("mouseup", function(e) {
clearInterval(longPress);
if (longPressed == true) {
document.body.classList.remove("is-longpress");
pushBalls(randBetween(50 + Math.ceil(multiplier), 100 + Math.ceil(multiplier)), e.clientX, e.clientY);
longPressed = false;
}
document.body.classList.remove("is-pressed");
}, false);
window.addEventListener("mousemove", function(e) {
let x = e.clientX;
let y = e.clientY;
pointer.style.top = y + "px";
pointer.style.left = x + "px";
}, false);
} else {
console.log("canvas or addEventListener is unsupported!");
}


function updateSize() {
canvas.width = window.innerWidth * 2;
canvas.height = window.innerHeight * 2;
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
ctx.scale(2, 2);
width = (canvas.width = window.innerWidth);
height = (canvas.height = window.innerHeight);
origin = {
x: width / 2,
y: height / 2
};
normal = {
x: width / 2,
y: height / 2
};
}
class Ball {
constructor(x = origin.x, y = origin.y) {
this.x = x;
this.y = y;
this.angle = Math.PI * 2 * Math.random();
if (longPressed == true) {
this.multiplier = randBetween(14 + multiplier, 15 + multiplier);
} else {
this.multiplier = randBetween(6, 12);
}
this.vx = (this.multiplier + Math.random() * 0.5) * Math.cos(this.angle);
this.vy = (this.multiplier + Math.random() * 0.5) * Math.sin(this.angle);
this.r = randBetween(8, 12) + 3 * Math.random();
this.color = colours[Math.floor(Math.random() * colours.length)];
}
update() {
this.x += this.vx - normal.x;
this.y += this.vy - normal.y;
normal.x = -2 / window.innerWidth * Math.sin(this.angle);
normal.y = -2 / window.innerHeight * Math.cos(this.angle);
this.r -= 0.3;
this.vx *= 0.9;
this.vy *= 0.9;
}
}

function pushBalls(count = 1, x = origin.x, y = origin.y) {
for (let i = 0; i < count; i++) {
balls.push(new Ball(x, y));
}
}

function randBetween(min, max) {
return Math.floor(Math.random() * max) + min;
}

function loop() {
ctx.fillStyle = "rgba(255, 255, 255, 0)";
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < balls.length; i++) {
let b = balls[i];
if (b.r < 0) continue;
ctx.fillStyle = b.color;
ctx.beginPath();
ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2, false);
ctx.fill();
b.update();
}
if (longPressed == true) {
multiplier += 0.2;
} else if (!longPressed && multiplier >= 0) {
multiplier -= 0.4;
}
removeBall();
requestAnimationFrame(loop);
}

function removeBall() {
for (let i = 0; i < balls.length; i++) {
let b = balls[i];
if (b.x + b.r < 0 || b.x - b.r > width || b.y + b.r < 0 || b.y - b.r > height || b.r < 0) {
balls.splice(i, 1);
}
}
}
}
clickFireworksEffects();//调用特效函数
EOF

2 、引用 dianji.js

Sun-Panel个性化设置–自定义页脚,粘贴以下内容

1
<script src="/custom/yanhua.js"></script>

浏览器清空缓存,刷新后见效果