├── .gitignore
├── toc
├── readme_doc
│ ├── mobile.gif
│ └── sidebar.gif
├── README.md
└── toc.js
├── clickFireworksEffects
├── readme_doc
│ └── PixPin_2024-09-09_13-12-25.png
├── README.md
└── clickFireworksEffects.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .history
2 |
--------------------------------------------------------------------------------
/toc/readme_doc/mobile.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hslr-s/sun-panel-js-plugins/HEAD/toc/readme_doc/mobile.gif
--------------------------------------------------------------------------------
/toc/readme_doc/sidebar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hslr-s/sun-panel-js-plugins/HEAD/toc/readme_doc/sidebar.gif
--------------------------------------------------------------------------------
/clickFireworksEffects/readme_doc/PixPin_2024-09-09_13-12-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hslr-s/sun-panel-js-plugins/HEAD/clickFireworksEffects/readme_doc/PixPin_2024-09-09_13-12-25.png
--------------------------------------------------------------------------------
/clickFireworksEffects/README.md:
--------------------------------------------------------------------------------
1 | # Sun-Panel ClickFireworksEffects (点击烟花特效)
2 | [Sun-Panel](https://sun-panel.top) JavaScript plugins.
3 |
4 | ## 插件开发作者
5 |
6 | - 作者:[AlanWang2333](https://github.com/AlanWang2333)
7 | - 来源:[Discussions](https://github.com/hslr-s/sun-panel/discussions/232)
8 |
9 | ## 说明
10 | --
11 |
12 | ## 效果
13 |
14 | 随意点击触发效果
15 |
16 | 
17 |
18 |
19 | ## 安装教程
20 |
21 | 参考 [README.md](../README.md)
--------------------------------------------------------------------------------
/toc/README.md:
--------------------------------------------------------------------------------
1 | # Sun-Panel TOC
2 | [Sun-Panel](https://sun-panel.top) JavaScript plugins.
3 |
4 | ## 说明
5 | 提取所有分组作为侧边栏目录,点击可以快速跳转到指定分组。需要在 Sun-Panel `v1.5.2-beta24-08-10` 及以上版本。
6 |
7 | ## 效果
8 |
9 | 按钮触发效果
10 |
11 | 
12 |
13 |
14 | 侧边栏效果
15 |
16 | 
17 |
18 | ## 配置说明
19 |
20 | 部分配置可以在 [toc.js](./toc.js) 配置区域查看到
21 |
22 | ```js
23 |
24 | // =========== Config Start ===========
25 | // ------------------------------------
26 | // 距离滚动偏移量
27 | const scrollOffset = 80
28 |
29 | // 显示风格( auto:自动(默认) | mobile:左上角显示触发按钮-移动端风格 | sidebar:常态显示侧栏)
30 | const displayStyle = 'auto'
31 |
32 | // 移动端宽度定义
33 | const mobileWidth = 800
34 |
35 | const SunPanelTOCDomIdName = 'sun-panel-toc-dom'
36 |
37 | // 左上角按钮 SVG 图标
38 | const svgTocMobileBtn = '...'
39 |
40 | // ------------------------------------
41 | // =========== Config End ===========
42 | ```
43 |
44 | ## 安装教程
45 |
46 | 参考 [README.md](../README.md)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sun-Panel JS-PlugIn
2 |
3 | [Sun-Panel](https://sun-panel.top) JavaScript plugins.
4 |
5 | ## 开发说明
6 |
7 | 插件开发暂时没有提供任何规范以及相关助手函数,可以有很高的自由度去开发。通过插件开发,可以实现很多丰富的功能。如果您愿意分享您开发的插件,可以 [Fork](https://github.com/hslr-s/sun-panel-js-plugins/fork) 本项目,然后创建自己的插件并推送。
8 |
9 | ### 目录说明
10 | 根目录中每个插件都是一个插件,每个插件都有相关的使用介绍和说明。
11 |
12 | ### 参与说明:
13 | - 在本项目根目录创建自己的插件目录,并将主文件设置为与目录同名 `.js` 文件
14 | - 在插件内部建立 `readme.md` 文件,写出插件具体的插件的作者信息、介绍、使用说明。
15 | - 为了安全需要,请尽量不要压缩代码,如果代码量巨大可以压缩插件的主文件并提供完整的未压缩源码。
16 | - 请尽量使用匿名函数调用模式,不要过多的使用全局变量,避免导致不必要的问题。
17 |
18 | ## 安装说明
19 |
20 | ### 1. 独立账号 - 自定义页脚引入 `.js` 文件(推荐)
21 |
22 | 此安装方式需要Sun-Panel `v1.5.2-beta24-08-10` 及以上版本生效,低于此版本请使用全局JS的方法。
23 |
24 | #### 开始
25 |
26 | 1. 找到使用的插件主文件(一般与目录同名的`.js`文件),将它放在 Sun-Panel 的自定义目录中。
27 | 2. 在想要使用的账号中,找到自定义页脚设置,然后在里面加入以下代码,
28 | ```html
29 |
30 | ```
31 | `toc.js` 改为插件的主文件,刷新后可以查看效果。
32 |
33 |
34 | ### 2. 独立账号 - 自定义页脚直接放入主文件代码
35 |
36 | 此安装方式需要Sun-Panel `v1.5.2-beta24-08-10` 及以上版本生效,低于此版本请使用全局JS的方法。
37 |
38 | #### 开始
39 |
40 | 找到自定义页脚设置,然后在里面加入以下代码
41 | ```html
42 |
45 | ```
46 |
47 | 刷新后可以查看效果。
48 |
49 | ### 3. 全局生效 针对所有账号生效
50 | 此安装方式需要Sun-Panel `v1.3.0` 及以上版本生效。
51 |
52 | #### 开始
53 | 在全局设置中找到自定义js功能,将主文件的源码,完整的粘贴到里面,保存后刷新页面查看效果
54 |
--------------------------------------------------------------------------------
/clickFireworksEffects/clickFireworksEffects.js:
--------------------------------------------------------------------------------
1 | function clickFireworksEffects() {
2 | let balls = [];
3 | let longPressed = false;
4 | let longPress;
5 | let multiplier = 0;
6 | let width, height;
7 | let origin;
8 | let normal;
9 | let ctx;
10 | const colours = ["#F73859", "#14FFEC", "#00E0FF", "#FF99FE", "#FAF15D"];
11 | const canvas = document.createElement("canvas");
12 | document.body.appendChild(canvas);
13 | canvas.setAttribute("style", "width: 100%; height: 100%; top: 0; left: 0; z-index: 99999; position: fixed; pointer-events: none;");
14 | const pointer = document.createElement("span");
15 | pointer.classList.add("pointer");
16 | document.body.appendChild(pointer);
17 |
18 | if (canvas.getContext && window.addEventListener) {
19 | ctx = canvas.getContext("2d");
20 | updateSize();
21 | window.addEventListener('resize', updateSize, false);
22 | loop();
23 | window.addEventListener("mousedown", function(e) {
24 | pushBalls(randBetween(10, 20), e.clientX, e.clientY);
25 | document.body.classList.add("is-pressed");
26 | longPress = setTimeout(function(){
27 | document.body.classList.add("is-longpress");
28 | longPressed = true;
29 | }, 500);
30 | }, false);
31 | window.addEventListener("mouseup", function(e) {
32 | clearInterval(longPress);
33 | if (longPressed == true) {
34 | document.body.classList.remove("is-longpress");
35 | pushBalls(randBetween(50 + Math.ceil(multiplier), 100 + Math.ceil(multiplier)), e.clientX, e.clientY);
36 | longPressed = false;
37 | }
38 | document.body.classList.remove("is-pressed");
39 | }, false);
40 | window.addEventListener("mousemove", function(e) {
41 | let x = e.clientX;
42 | let y = e.clientY;
43 | pointer.style.top = y + "px";
44 | pointer.style.left = x + "px";
45 | }, false);
46 | } else {
47 | console.log("canvas or addEventListener is unsupported!");
48 | }
49 |
50 |
51 | function updateSize() {
52 | canvas.width = window.innerWidth * 2;
53 | canvas.height = window.innerHeight * 2;
54 | canvas.style.width = window.innerWidth + 'px';
55 | canvas.style.height = window.innerHeight + 'px';
56 | ctx.scale(2, 2);
57 | width = (canvas.width = window.innerWidth);
58 | height = (canvas.height = window.innerHeight);
59 | origin = {
60 | x: width / 2,
61 | y: height / 2
62 | };
63 | normal = {
64 | x: width / 2,
65 | y: height / 2
66 | };
67 | }
68 | class Ball {
69 | constructor(x = origin.x, y = origin.y) {
70 | this.x = x;
71 | this.y = y;
72 | this.angle = Math.PI * 2 * Math.random();
73 | if (longPressed == true) {
74 | this.multiplier = randBetween(14 + multiplier, 15 + multiplier);
75 | } else {
76 | this.multiplier = randBetween(6, 12);
77 | }
78 | this.vx = (this.multiplier + Math.random() * 0.5) * Math.cos(this.angle);
79 | this.vy = (this.multiplier + Math.random() * 0.5) * Math.sin(this.angle);
80 | this.r = randBetween(8, 12) + 3 * Math.random();
81 | this.color = colours[Math.floor(Math.random() * colours.length)];
82 | }
83 | update() {
84 | this.x += this.vx - normal.x;
85 | this.y += this.vy - normal.y;
86 | normal.x = -2 / window.innerWidth * Math.sin(this.angle);
87 | normal.y = -2 / window.innerHeight * Math.cos(this.angle);
88 | this.r -= 0.3;
89 | this.vx *= 0.9;
90 | this.vy *= 0.9;
91 | }
92 | }
93 |
94 | function pushBalls(count = 1, x = origin.x, y = origin.y) {
95 | for (let i = 0; i < count; i++) {
96 | balls.push(new Ball(x, y));
97 | }
98 | }
99 |
100 | function randBetween(min, max) {
101 | return Math.floor(Math.random() * max) + min;
102 | }
103 |
104 | function loop() {
105 | ctx.fillStyle = "rgba(255, 255, 255, 0)";
106 | ctx.clearRect(0, 0, canvas.width, canvas.height);
107 | for (let i = 0; i < balls.length; i++) {
108 | let b = balls[i];
109 | if (b.r < 0) continue;
110 | ctx.fillStyle = b.color;
111 | ctx.beginPath();
112 | ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2, false);
113 | ctx.fill();
114 | b.update();
115 | }
116 | if (longPressed == true) {
117 | multiplier += 0.2;
118 | } else if (!longPressed && multiplier >= 0) {
119 | multiplier -= 0.4;
120 | }
121 | removeBall();
122 | requestAnimationFrame(loop);
123 | }
124 |
125 | function removeBall() {
126 | for (let i = 0; i < balls.length; i++) {
127 | let b = balls[i];
128 | if (b.x + b.r < 0 || b.x - b.r > width || b.y + b.r < 0 || b.y - b.r > height || b.r < 0) {
129 | balls.splice(i, 1);
130 | }
131 | }
132 | }
133 | }
134 | clickFireworksEffects();//调用特效函数
135 |
--------------------------------------------------------------------------------
/toc/toc.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | // =========== Config Start ===========
3 | // ------------------------------------
4 | // 距离滚动偏移量
5 | const scrollOffset = 80
6 |
7 | // 显示风格( auto:自动(默认) | mobile:左上角显示触发按钮-移动端风格 | sidebar:常态显示侧栏)
8 | const displayStyle = 'auto'
9 |
10 | // 移动端宽度定义
11 | const mobileWidth = 800
12 |
13 | const SunPanelTOCDomIdName = 'sun-panel-toc-dom'
14 |
15 | // 左上角按钮 SVG 图标
16 | const svgTocMobileBtn = ''
17 |
18 | // ------------------------------------
19 | // =========== Config End ===========
20 |
21 | // 滚动容器的类名
22 | const scrollContainerElementClassName = '.scroll-container'
23 |
24 | // 一些函数
25 | const isMobile = () => {
26 | if (displayStyle === 'mobile') {
27 | return true
28 | }
29 | else if (displayStyle === 'pc') {
30 | return false
31 | }
32 | const width = window.innerWidth
33 | return width < mobileWidth
34 | }
35 |
36 | function createDom() {
37 | // 检测是否已经存在TOC DOM,存在则删除
38 | (function () {
39 | const element = document.getElementById(SunPanelTOCDomIdName)
40 | if (element) {
41 | element.remove()
42 | }
43 | })()
44 |
45 | const SunPanelTOCDom = document.createElement('div')
46 | SunPanelTOCDom.id = SunPanelTOCDomIdName
47 | document.body.appendChild(SunPanelTOCDom)
48 |
49 | // ========= Add style start =========
50 | const style = document.createElement('style')
51 | const SunPanelTOCDomStyleId = `#${SunPanelTOCDomIdName}`
52 | style.textContent = `
53 | ${SunPanelTOCDomStyleId} #toc-mobile-btn {
54 | top: 20px !important;
55 | left: 20px !important;
56 | position: fixed;
57 | width: 46px;
58 | height: 46px;
59 | background-color: #2a2a2a6b;
60 | color: white;
61 | border-radius: 0.5rem;
62 | display: flex;
63 | justify-content: center;
64 | align-items: center;
65 | cursor: pointer;
66 | }
67 |
68 | ${SunPanelTOCDomStyleId} .hidden {
69 | display: none !important;
70 | }
71 |
72 | ${SunPanelTOCDomStyleId} #toc-sidebar {
73 | width: 40px;
74 | padding: 10px;
75 | position: fixed;
76 | top: 0;
77 | left: 0;
78 | height: 100%;
79 | overflow: hidden;
80 | display: flex;
81 | flex-direction: column;
82 | justify-content: center;
83 | transition: width 0.3s ease, background-color 0.3s ease;
84 | border-top-right-radius: 20px;
85 | border-bottom-right-radius: 20px;
86 | background-color: none;
87 | }
88 |
89 | ${SunPanelTOCDomStyleId} .toc-mobile-btn-svg-container{
90 | width:21px;
91 | height:21px;
92 | }
93 |
94 | ${SunPanelTOCDomStyleId} .toc-sidebar-expansion {
95 | width: 200px !important;
96 | display: flex;
97 | background-color: rgb(42 42 42 / 90%);
98 | box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
99 | }
100 |
101 | ${SunPanelTOCDomStyleId} #toc-sidebar .toc-sidebar-box {
102 | width: 500px;
103 | }
104 |
105 | ${SunPanelTOCDomStyleId} .title-bar-box {
106 | display: flex;
107 | align-items: center;
108 | position: relative;
109 | cursor: pointer;
110 | }
111 |
112 | ${SunPanelTOCDomStyleId} .title-bar-slip {
113 | width: 20px;
114 | height: 6px;
115 | background-color: white;
116 | border-radius: 4px;
117 | margin: 15px 0;
118 | transition: height 0.3s ease, width 0.3s ease;
119 | box-shadow: 2px 0 5px rgba(0, 0, 0, 0.5);
120 | }
121 |
122 | ${SunPanelTOCDomStyleId} .title-bar-title {
123 | opacity: 0;
124 | white-space: nowrap;
125 | transition: opacity 0.3s ease, transform 0.3s ease, margin-left 0.3s ease;
126 | font-size: 14px;
127 | color: white;
128 | }
129 |
130 | ${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-title {
131 | opacity: 1;
132 | margin-left: 10px;
133 | }
134 |
135 | ${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-slip {
136 | box-shadow: none;
137 | }
138 |
139 | ${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-box:hover .title-bar-slip {
140 | width: 40px;
141 | }
142 |
143 | ${SunPanelTOCDomStyleId} .toc-sidebar-expansion .title-bar-box:hover .title-bar-title {
144 | font-size: 20px;
145 | }
146 |
147 | `
148 | // 添加样式到文档头部
149 | SunPanelTOCDom.appendChild(style)
150 |
151 | // ========= Add style end =========
152 |
153 | // 添加移动端菜单按钮
154 | const tocMobileBtn = document.createElement('div')
155 | tocMobileBtn.id = 'toc-mobile-btn'
156 | tocMobileBtn.classList.add('backdrop-blur-[2px]')
157 | SunPanelTOCDom.appendChild(tocMobileBtn)
158 |
159 | const tocMobileBtnSvgcContainer = document.createElement('div')
160 | tocMobileBtnSvgcContainer.innerHTML = svgTocMobileBtn
161 | tocMobileBtnSvgcContainer.classList.add('toc-mobile-btn-svg-container')
162 | tocMobileBtn.appendChild(tocMobileBtnSvgcContainer)
163 |
164 | // 创建侧边栏容器
165 | const sidebar = document.createElement('div')
166 | sidebar.id = 'toc-sidebar'
167 |
168 | const sidebarBox = document.createElement('div')
169 | sidebarBox.className = 'toc-sidebar-box'
170 |
171 | // 查询出所有类名包含 item-group-index- 的元素
172 | const items = document.querySelectorAll('[class*="item-group-index-"]')
173 |
174 | // 遍历并打印每个元素的完整类名
175 | items.forEach((item) => {
176 | item.classList.forEach((className) => {
177 | if (className.startsWith('item-group-index-')) {
178 | const titleBarBox = document.createElement('div')
179 | titleBarBox.className = 'title-bar-box'
180 | // titleBarBox.href = `#${item.id}`
181 | titleBarBox.dataset.groupClassName = className
182 |
183 | // 目录条
184 | const titleBarSlip = document.createElement('div')
185 | titleBarSlip.className = 'title-bar-slip'
186 |
187 | // 创建一个链接
188 | const titleBarTitle = document.createElement('div')
189 | titleBarTitle.className = 'title-bar-title'
190 |
191 | // 获取子元素中 class="group-title" 的内容
192 | const titleElement = item.querySelector('.group-title')
193 | const titleText = titleElement ? titleElement.textContent : item.id
194 | titleBarTitle.textContent = titleText
195 |
196 | titleBarBox.appendChild(titleBarSlip)
197 | titleBarBox.appendChild(titleBarTitle)
198 |
199 | sidebarBox.appendChild(titleBarBox)
200 | }
201 | })
202 | })
203 |
204 | sidebar.appendChild(sidebarBox)
205 |
206 | // 将侧边栏添加到页面中
207 | SunPanelTOCDom.appendChild(sidebar)
208 |
209 | function mobileHideSidebar() {
210 | sidebar.classList.remove('toc-sidebar-expansion')
211 | sidebar.classList.add('hidden')
212 | }
213 |
214 | function hideSidebar() {
215 | sidebar.classList.remove('toc-sidebar-expansion')
216 | }
217 |
218 | function showSidebar() {
219 | sidebar.classList.add('toc-sidebar-expansion')
220 | sidebar.classList.remove('hidden')
221 | }
222 |
223 | // ----------------
224 | // 监听宽度变化开始
225 | // ----------------
226 | function debounce(func, wait) {
227 | let timeout
228 | return function (...args) {
229 | clearTimeout(timeout)
230 | timeout = setTimeout(() => {
231 | func.apply(this, args)
232 | }, wait)
233 | }
234 | }
235 |
236 | function handleResize() {
237 | if (isMobile()) {
238 | tocMobileBtn.classList.remove('hidden')
239 | sidebar.classList.add('hidden')
240 | }
241 | else {
242 | tocMobileBtn.classList.add('hidden')
243 | sidebar.classList.remove('hidden')
244 | }
245 | }
246 |
247 | // 使用防抖函数包装你的处理函数
248 | const debouncedHandleResize = debounce(handleResize, 200)
249 |
250 | // 添加事件监听器
251 | window.addEventListener('resize', debouncedHandleResize)
252 |
253 | // 首次触发
254 | handleResize()
255 |
256 | // ----------------
257 | // 监听宽度变化结束
258 | // ----------------
259 |
260 | // 监听移动端按钮点击
261 | tocMobileBtn.addEventListener('click', () => {
262 | if (sidebar.classList.contains('toc-sidebar-expansion')) {
263 | // 隐藏
264 | mobileHideSidebar()
265 | }
266 | else {
267 | // 显示
268 | showSidebar()
269 | }
270 | })
271 |
272 | // 监听TOC栏失去hover
273 | sidebar.addEventListener('mouseleave', () => {
274 | if (isMobile()) {
275 | // 隐藏
276 | mobileHideSidebar()
277 | }
278 | else {
279 | hideSidebar()
280 | }
281 | })
282 |
283 | // 监听TOC栏获得hover
284 | sidebar.addEventListener('mouseenter', () => {
285 | showSidebar()
286 | })
287 |
288 | // 监听TOC点击事件
289 | document.querySelectorAll('.title-bar-box').forEach((box) => {
290 | box.addEventListener('click', function (event) {
291 | // 检查触发事件的元素是否有 'data-groupClassName' 属性
292 | if (this.dataset.groupClassName) {
293 | // 获取 'data-groupClass' 属性的值
294 | const groupClassName = this.dataset.groupClassName
295 | // 使用属性值作为选择器查询对应的元素
296 | const targetElement = document.querySelector(`.${groupClassName}`)
297 | if (targetElement) {
298 | // 获取目标元素的 'top' 坐标
299 | const targetTop = targetElement.offsetTop
300 | const scrollContainerElement = document.querySelector(scrollContainerElementClassName)
301 | if (scrollContainerElement) {
302 | scrollContainerElement.scrollTo({
303 | top: targetTop - scrollOffset,
304 | behavior: 'smooth', // 平滑滚动
305 | })
306 | }
307 | }
308 | }
309 | })
310 | })
311 | }
312 |
313 | // 判断是否已经存在分组,不存在将定时监听
314 | const items = document.querySelectorAll('[class*="item-group-index-"]')
315 | if (items.length > 0) {
316 | createDom()
317 | return
318 | }
319 |
320 | const interval = setInterval(() => {
321 | const items = document.querySelectorAll('[class*="item-group-index-"]')
322 | if (items.length > 0) {
323 | createDom()
324 | clearInterval(interval)
325 | }
326 | }, 1000)
327 | })()
328 |
--------------------------------------------------------------------------------