Sun-Panel js源码

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

一 、显示概览快速跳转—服务器主机shell输入以下命令

  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. 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. Sun-Panel个性化设置—自定义页脚,粘贴以下内容

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

三 、mp4视频壁纸

  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
    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. Sun-Panel个性化设置—自定义页脚,粘贴以下内容

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

四 、点击特效-散开

  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
    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. 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. Sun-Panel个性化设置—自定义页脚,粘贴以下内容

    1
    <script src="/custom/yanhua.js"></script>
  • 浏览器清空缓存,刷新后见效果