",
5 | "main": "index.js",
6 | "files": [
7 | "src",
8 | "dist"
9 | ],
10 | "directories": {
11 | "doc": "docs"
12 | },
13 | "scripts": {
14 | "test": "echo \"Error: no test specified\" && exit 1",
15 | "dev": "npx webpack --config config/webpack.dev.js",
16 | "build": "npx webpack --config config/webpack.prod.js && npx postcss ./src/style/simple-memory.css > ./dist/simple-memory.css",
17 | "analyz": "npx webpack --config config/webpack.dev.js && npx webpack-bundle-analyzer",
18 | "docs:build": "vitepress build docs/v2",
19 | "docs:preview": "vitepress preview docs/v2",
20 | "docs:dev": "vitepress dev docs/v2"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/wangyang0210/cnblogs-theme.git"
25 | },
26 | "keywords": [],
27 | "author": "wangyang0210",
28 | "license": "ISC",
29 | "bugs": {
30 | "url": "https://github.com/wangyang0210/cnblogs-theme/issues"
31 | },
32 | "homepage": "https://github.com/wangyang0210/cnblogs-theme#readme",
33 | "devDependencies": {
34 | "@popperjs/core": "^2.11.8",
35 | "compression-webpack-plugin": "^10.0.0",
36 | "css-loader": "^5.2.7",
37 | "css-minimizer-webpack-plugin": "^5.0.1",
38 | "cssnano": "^6.1.2",
39 | "cssnano-preset-advanced": "^5.3.10",
40 | "exports-loader": "^2.0.0",
41 | "filemanager-webpack-plugin": "^7.0.0",
42 | "html-loader": "^2.1.2",
43 | "html-webpack-plugin": "^5.6.3",
44 | "imports-loader": "^2.0.0",
45 | "json5": "^2.2.3",
46 | "mini-css-extract-plugin": "^1.6.2",
47 | "postcss": "^8.4.49",
48 | "postcss-cli": "^10.1.0",
49 | "style-loader": "^2.0.0",
50 | "toml": "^3.0.0",
51 | "vitepress": "^1.5.0",
52 | "webpack": "^5.97.1",
53 | "webpack-bundle-analyzer": "^4.10.2",
54 | "webpack-cli": "^4.10.0",
55 | "webpack-dev-server": "^4.15.2",
56 | "webpack-merge": "^5.10.0"
57 | },
58 | "browserslist": {
59 | "development": [
60 | "last 1 chrome version",
61 | "last 1 firefox version",
62 | "last 1 safari version"
63 | ],
64 | "production": [
65 | ">0.2%",
66 | "not dead",
67 | "nop op_mini all"
68 | ]
69 | },
70 | "dependencies": {
71 | "markdown-it-task-lists": "^2.1.1"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('cssnano')({
4 | preset: ['default', {
5 | discardComments: {
6 | removeAll: true,
7 | },
8 | }]
9 | }),
10 | ],
11 | };
12 |
--------------------------------------------------------------------------------
/src/components/articleDirectory/articleDirectory.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:18
6 | * ----------------------------------------------
7 | * @describe: 文章目录处理
8 | */
9 | import '../../style/articleDirectory.css';
10 | import articleDirectoryTemp from '../../template/articleDirectory.html';
11 | await $.__tools.dynamicLoadingJs($.__config.default.bootstrap).catch((e) => console.error('bootstrap.js', e));
12 | export default function main() {
13 | let body = $('body');
14 | let postBody = $('#cnblogs_post_body');
15 | let header = postBody.find(':header');
16 |
17 | if (header.length) {
18 | const tagList = header.map((index, element) => parseInt(element.tagName.replace(/H/g, ''))).get();
19 | const html = header
20 | .map((index, element) => {
21 | const obj = $(element);
22 | const h = parseInt(obj[0].tagName.replace(/H/g, ''));
23 | let hid = obj.attr('id');
24 | const titleId = `tid-${$.__tools.randomString(6)}`;
25 | obj.attr('tid', titleId);
26 | if (!hid || /^[\W|\d]+.*/.test(hid)) {
27 | if (hid) {
28 | const tocObj = $(`.toc a[href="#${hid}"]`);
29 | tocObj.length && tocObj.attr('href', `#${titleId}`);
30 | }
31 | hid = titleId;
32 | obj.attr('id', hid);
33 | }
34 |
35 | const num = [...new Set(tagList)].sort().indexOf(h);
36 | const str = num === 0 || num === -1 ? '' : ' '.repeat(num);
37 | const text = str + obj.text().replace(//g, '>');
38 | return `
${text}`;
39 | })
40 | .get()
41 | .join('');
42 |
43 | let dirHtml = $.__tools.tempReplacement(articleDirectoryTemp, 'dirHtml', html);
44 | postBody.append(dirHtml);
45 |
46 | body.attr('data-bs-spy', 'scroll');
47 | body.attr('data-bs-target', '#articleDirectory');
48 | body.attr('data-bs-offset', '0');
49 | body.attr('tabindex', '0');
50 | body.scrollspy({ target: '#articleDirectory' });
51 |
52 | if (!$.__config.articleDirectory.autoWidthScroll) {
53 | $('#articleDirectory ul li').addClass('articleDirectory-overflow');
54 | $('#articleDirectory ul li a').addClass('articleDirectory-overflow');
55 | }
56 |
57 | $.__event.scroll.handle.push(() => {
58 | let articleDirectory = $('#articleDirectory');
59 | if (
60 | $.__event.scroll.temScroll < $.__event.scroll.docScroll &&
61 | $.__event.scroll.homeScroll <= $.__event.scroll.docScroll
62 | ) {
63 | articleDirectory.addClass('articleDirectoryFixed');
64 | }
65 |
66 | if (
67 | $.__event.scroll.temScroll > $.__event.scroll.docScroll &&
68 | $.__event.scroll.homeScroll >= $.__event.scroll.docScroll
69 | ) {
70 | articleDirectory.removeClass('articleDirectoryFixed');
71 | }
72 | });
73 |
74 | $.__event.resize.handle.push(() => {
75 | const bodyWidth = parseFloat(document.body.clientWidth),
76 | articleDirectory = $('#articleDirectory');
77 | if (articleDirectory.length > 0) {
78 | let mainContentWidth = $('#home').outerWidth(false),
79 | listWidth = articleDirectory.outerWidth(true);
80 | let bothWidth = (bodyWidth - mainContentWidth) / 2,
81 | rightPx = bothWidth - listWidth - 5,
82 | sideToolbarTop = $('.main-header').outerHeight();
83 |
84 | switch ($.__config.articleDirectory.position) {
85 | case 'left':
86 | articleDirectory.css({
87 | top: sideToolbarTop + 5 + 'px',
88 | left: (rightPx > 0 ? rightPx : 5) + 'px',
89 | width: (bothWidth > 190 && bothWidth < 260 ? bothWidth - 10 : listWidth) + 'px',
90 | });
91 | break;
92 | case 'right':
93 | default:
94 | articleDirectory.css({
95 | top: sideToolbarTop + 5 + 'px',
96 | right: (rightPx > 0 ? rightPx : 5) + 'px',
97 | width: (bothWidth > 190 && bothWidth < 260 ? bothWidth - 10 : listWidth) + 'px',
98 | });
99 | break;
100 | }
101 |
102 | if (bodyWidth <= $.__config.articleDirectory.minBodyWeight || bothWidth <= 190) {
103 | articleDirectory.hide();
104 | } else {
105 | articleDirectory.show();
106 | }
107 | }
108 | });
109 |
110 | $('#articleDirectory .nav-link').click(function () {
111 | let titleH = $(`:header[tid="${$(this).attr('goto')}"]`);
112 | titleH.length && $.__tools.actScroll(titleH.offset().top + 3, 500);
113 | });
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/components/articleSuffix/articleSuffix.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:18
6 | * ----------------------------------------------
7 | * @describe: 文章后缀处理
8 | */
9 | import '../../style/articleSuffix.css';
10 | import suffixTemp from '../../template/articleSuffix.html';
11 |
12 | export default function main() {
13 | // 图片
14 | let imgUrl = $.__config.articleSuffix.imgUrl ? $.__config.articleSuffix.imgUrl : $.__config.info.avatar ? $.__config.info.avatar : $.__config.default.avatar;
15 |
16 | // 本文作者 & 本文链接
17 | const articleAuthor = $('#articleAuthor');
18 | const articleSource = $('#articleSource');
19 | const author = articleAuthor?.val() || $.__config.info.name;
20 | const source = articleSource?.val() || $.__status.url;
21 | const homeUrl = articleSource?.val() || $.__status.homeUrl;
22 | const origin = articleSource.length ? '原' : '本';
23 |
24 | let aboutHtml = $.__config.articleSuffix.aboutHtml || `评论和私信会在第一时间回复。或者
直接私信我。`;
25 |
26 | // 版权声明
27 | let copyrightHtml =
28 | $.__config.articleSuffix.copyrightHtml ||
29 | `本博客所有文章除特别声明外,均采用
BY-NC-SA 许可协议。转载请注明出处!`;
30 |
31 | // 声援博主
32 | let supportHtml =
33 | $.__config.articleSuffix.supportHtml ||
34 | `如果您觉得文章对您有帮助,可以点击文章右下角
【推荐】一下。`;
35 |
36 | let re = [
37 | ['origin', origin],
38 | ['imgUrl', imgUrl],
39 | ['homeUrl', homeUrl],
40 | ['author', author],
41 | ['source', source],
42 | ['aboutHtml', aboutHtml],
43 | ['copyrightHtml', copyrightHtml],
44 | ['supportHtml', supportHtml],
45 | ];
46 | let suffixHtml = $.__tools.batchTempReplacement(suffixTemp, re);
47 | $('#cnblogs_post_body').append(suffixHtml);
48 |
49 | // 版权声明 - COPY
50 | const config = $.__config.articleSuffix.copyInfo;
51 | const { enable, length, copyright = copyrightHtml } = config;
52 |
53 | if (enable) {
54 | const separator = '———————————————————————————————————————————————';
55 | const newline = '\n';
56 | const htmlSeparator = `
\n${separator}
\n`;
57 |
58 | document.body.addEventListener('copy', (event) => {
59 | const selection = window.getSelection().toString();
60 | if (selection && selection.length > length) {
61 | event.preventDefault();
62 | const clipboardData = event.clipboardData || window.clipboardData;
63 |
64 | if (clipboardData) {
65 | const cleanedCopyright = copyright.replace(/<\/?.+?>/g, '').replace(/ /g, '');
66 | const htmlData = `${selection}${htmlSeparator}${copyright}
\n作者:${author}
\n原文链接:${source}
\n`;
67 | const textData = `${selection}${newline}${separator}${newline}${cleanedCopyright}${newline}作者:${author}${newline}原文链接:${source}${newline}`;
68 |
69 | clipboardData.setData('text/html', htmlData);
70 | clipboardData.setData('text/plain', textData);
71 | }
72 | }
73 | });
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/background/particles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:23
6 | * ----------------------------------------------
7 | * @describe: 背景鼠标滚动动画
8 | */
9 | import particlesTemp from '../../template/particles.html';
10 | import '../../style/particles.css';
11 | await $.__tools.dynamicLoadingJs($.__config.default.gsap).catch((e) => console.error('gsap.js', e));
12 | export default function main(options) {
13 | $('#footer').after(particlesTemp);
14 | let wrapper = document.getElementById('particles'),
15 | ela = wrapper.querySelector('.particles-layer--1'),
16 | elb = wrapper.querySelector('.particles-layer--2'),
17 | elc = wrapper.querySelector('.particles-layer--3'),
18 | particlesList = [
19 | { el: ela, opacity: 0.07, speed: 0.06 },
20 | { el: elb, opacity: 0.07, speed: 0.04 },
21 | { el: elc, opacity: 0.07, speed: 0.05 },
22 | ];
23 | particlesList.forEach((l) => {
24 | gsap.to(l.el, 0.6, { delay: Math.random(), opacity: l.opacity });
25 | });
26 | document.addEventListener('mousemove', particlesMousemove);
27 | function particlesMousemove(t) {
28 | let e = { x: window.innerWidth / 2, y: window.innerHeight / 2 },
29 | n = { x: t.clientX || t.pageX, y: t.clientY || t.pageY },
30 | r = { x: e.x - n.x, y: e.y - n.y };
31 | particlesList.forEach((l) => {
32 | gsap.to(l.el, 1, { x: r.x * l.speed, y: r.y * l.speed });
33 | });
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/background/season.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-09-03 01:18
6 | * ----------------------------------------------
7 | * @describe: 可以自定义下落物品的背景特效
8 | */
9 |
10 | export default function main(options) {
11 | let SakuraList;
12 | let img = new Image();
13 | img.src = options.img;
14 | let size = options.size;
15 |
16 | function Sakura(x, y, s, r, fn) {
17 | this.x = x;
18 | this.y = y;
19 | this.s = s;
20 | this.r = r;
21 | this.fn = fn;
22 | }
23 |
24 | Sakura.prototype.draw = function (cxt) {
25 | cxt.save();
26 | cxt.translate(this.x, this.y);
27 | cxt.rotate(this.r);
28 | cxt.drawImage(img, 0, 0, size * this.s, size * this.s);
29 | cxt.restore();
30 | };
31 | Sakura.prototype.update = function () {
32 | this.x = this.fn.x(this.x, this.y);
33 | this.y = this.fn.y(this.y, this.y);
34 | this.r = this.fn.r(this.r);
35 | if (this.x > window.innerWidth || this.x < 0 || this.y > window.innerHeight || this.y < 0) {
36 | this.r = getRandom('fnr');
37 | if (Math.random() > 0.4) {
38 | this.x = getRandom('x');
39 | this.y = 0;
40 | this.s = getRandom('s');
41 | this.r = getRandom('r');
42 | } else {
43 | this.x = window.innerWidth;
44 | this.y = getRandom('y');
45 | this.s = getRandom('s');
46 | this.r = getRandom('r');
47 | }
48 | }
49 | };
50 |
51 | SakuraList = function () {
52 | this.list = [];
53 | };
54 | SakuraList.prototype.push = function (sakura) {
55 | this.list.push(sakura);
56 | };
57 | SakuraList.prototype.update = function () {
58 | for (let i = 0, len = this.list.length; i < len; i++) {
59 | this.list[i].update();
60 | }
61 | };
62 | SakuraList.prototype.draw = function (cxt) {
63 | for (let i = 0, len = this.list.length; i < len; i++) {
64 | this.list[i].draw(cxt);
65 | }
66 | };
67 | SakuraList.prototype.get = function (i) {
68 | return this.list[i];
69 | };
70 | SakuraList.prototype.size = function () {
71 | return this.list.length;
72 | };
73 |
74 | function getRandom(option) {
75 | let ret, random;
76 | switch (option) {
77 | case 'x':
78 | ret = Math.random() * window.innerWidth;
79 | break;
80 | case 'y':
81 | ret = Math.random() * window.innerHeight;
82 | break;
83 | case 's':
84 | ret = Math.random();
85 | break;
86 | case 'r':
87 | ret = Math.random() * 6;
88 | break;
89 | case 'fnx':
90 | random = -0.5 + Math.random();
91 | ret = function (x, y) {
92 | return x + 0.5 * random - 1.7;
93 | };
94 | break;
95 | case 'fny':
96 | random = 1.5 + Math.random() * 0.7;
97 | ret = function (x, y) {
98 | return y + random;
99 | };
100 | break;
101 | case 'fnr':
102 | random = Math.random() * 0.03;
103 | ret = function (r) {
104 | return r + random;
105 | };
106 | break;
107 | }
108 | return ret;
109 | }
110 |
111 | function startSakura() {
112 | requestAnimationFrame =
113 | window.requestAnimationFrame ||
114 | window.mozRequestAnimationFrame ||
115 | window.webkitRequestAnimationFrame ||
116 | window.msRequestAnimationFrame ||
117 | window.oRequestAnimationFrame;
118 | let canvas = document.createElement('canvas'),
119 | cxt;
120 | canvas.height = window.innerHeight;
121 | canvas.width = window.innerWidth;
122 | canvas.setAttribute('style', 'position: fixed;left: 0;top: 0;pointer-events: none;');
123 | canvas.setAttribute('id', 'canvas_sakura');
124 | canvas.style.zIndex = '999999999999999999999999999999999999999999';
125 | document.getElementsByTagName('body')[0].appendChild(canvas);
126 | cxt = canvas.getContext('2d');
127 | let sakuraList = new SakuraList();
128 | for (let i = 0; i < 50; i++) {
129 | let sakura, randomX, randomY, randomS, randomR, randomFnx, randomFny, randomFnR;
130 | randomX = getRandom('x');
131 | randomY = getRandom('y');
132 | randomR = getRandom('r');
133 | randomS = getRandom('s');
134 | randomFnx = getRandom('fnx');
135 | randomFny = getRandom('fny');
136 | randomFnR = getRandom('fnr');
137 | sakura = new Sakura(randomX, randomY, randomS, randomR, {
138 | x: randomFnx,
139 | y: randomFny,
140 | r: randomFnR,
141 | });
142 | sakura.draw(cxt);
143 | sakuraList.push(sakura);
144 | }
145 | requestAnimationFrame(function fn() {
146 | cxt.clearRect(0, 0, canvas.width, canvas.height);
147 | sakuraList.update();
148 | sakuraList.draw(cxt);
149 | requestAnimationFrame(fn);
150 | });
151 | }
152 |
153 | img.onload = function () {
154 | startSakura();
155 | };
156 | }
157 |
--------------------------------------------------------------------------------
/src/components/banner/banner.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:19
6 | * ----------------------------------------------
7 | * @describe: banner背景图片处理
8 | */
9 | import bannerTemp from '../../template/banner.html';
10 |
11 | export default function main() {
12 | $('#sidebar_news').prepend(bannerTemp);
13 |
14 | /**
15 | * 设置banner背景图片,初始化高度
16 | * (该处理需在loading结束之前处理)
17 | */
18 | (() => {
19 | let mainHeader = $('#main-header');
20 | let topImg, bgImg, height;
21 |
22 | // 设置图片
23 | if ($.__status.pageType === 'home') {
24 | topImg = $.__config.banner.home.background.length
25 | ? $.__config.banner.home.background
26 | : ['https://images.cnblogs.com/cnblogs_com/wangyang0210/1943283/o_220917053600_wallhaven-6k3oox.webp'];
27 | } else {
28 | topImg = $.__config.banner.article.background.length
29 | ? $.__config.banner.article.background
30 | : ['https://images.cnblogs.com/cnblogs_com/wangyang0210/1943283/o_220917053937_wallhaven-j5mz95.webp'];
31 | height = '40vh';
32 | $('#homeTopTitle').hide();
33 | $('.scroll-down').hide();
34 | $('#home').css('margin-top', '40vh');
35 | $('#cb_post_title_url').addClass('post-del-title');
36 | }
37 |
38 | // 设置高度
39 | if (height) mainHeader.css('height', height);
40 |
41 | // banner动效
42 | if ($.__config.animate.bannerImages?.enable) {
43 | // 开启图片自动切换
44 | import(
45 | /* webpackChunkName: "banner-images" */ /* webpackPrefetch: true */ '../bannerImages/bannerImages'
46 | ).then((module) => {
47 | let bannerImages = module.default;
48 | bannerImages(
49 | 'main-header',
50 | topImg,
51 | $.__config.animate.bannerImages.options.itemNum,
52 | $.__config.animate.bannerImages.options.time,
53 | $.__config.animate.bannerImages.options.sort,
54 | $.__config.animate.bannerImages.options.current < 0
55 | ? $.__tools.randomNum(0, topImg.length - 1)
56 | : $.__config.animate.bannerImages.options.current
57 | );
58 | });
59 | } else {
60 | // 随机指定一个图片
61 | if (topImg.length > 1) bgImg = topImg[$.__tools.randomNum(0, topImg.length - 1)];
62 | else bgImg = topImg[0] || '';
63 |
64 | mainHeader.css({
65 | background: `#222 url("${encodeURI(bgImg)}") center center no-repeat`,
66 | 'background-size': 'cover',
67 | });
68 | }
69 |
70 | // Banner文字是否可选
71 | if (!$.__config.banner.titleSelect) $('.main-header-content.inner').addClass('textUnselect');
72 | })();
73 |
74 | // 添加事件监听
75 | $.__event.scroll.handle.push(() => {
76 | const openButton = $('#open-button');
77 | const { temScroll, docScroll, homeScroll } = $.__event.scroll;
78 | const isScrolledDown = temScroll < docScroll && homeScroll <= docScroll;
79 | const isScrolledUp = temScroll > docScroll && homeScroll >= docScroll;
80 | const shouldToggleClass = openButton.hasClass('menu-button-scroll');
81 |
82 | // 根据滚动方向和当前状态切换按钮样式
83 | if ((isScrolledDown && !shouldToggleClass) || (isScrolledUp && shouldToggleClass)) {
84 | openButton.toggleClass('menu-button-scroll', isScrolledDown).text(isScrolledDown ? '' : 'MENU');
85 | }
86 | });
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/bannerImages/bannerImages.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:19
6 | * ----------------------------------------------
7 | * @describe: banner背景切换处理
8 | */
9 |
10 | await $.__tools.dynamicLoadingJs($.__config.default.gsap).catch((e) => console.error('gsap.js', e));
11 | export default function main(id, images, cols, time, sort, current) {
12 | const bgMain = document.getElementById(id);
13 | const parts = [];
14 | let playing = false;
15 | const animOptions = { duration: 2.3, ease: Power4.easeInOut };
16 |
17 | images.forEach((src) => {
18 | const img = new Image();
19 | img.src = src;
20 | });
21 |
22 | for (let col = 0; col < cols; col++) {
23 | const part = document.createElement('div');
24 | part.className = 'part';
25 | const el = document.createElement('div');
26 | el.className = 'section';
27 | const img = document.createElement('img');
28 | img.src = images[current];
29 | el.appendChild(img);
30 | part.style.setProperty('--x', `${(-100 / cols) * col}vw`);
31 | part.appendChild(el);
32 | bgMain.appendChild(part);
33 | parts.push(part);
34 | }
35 |
36 | function go(dir) {
37 | if (playing) return;
38 | playing = true;
39 | current = (current + dir + images.length) % images.length;
40 | const bgMainHeight = bgMain.offsetHeight;
41 | parts.forEach((part, p) => {
42 | const next = document.createElement('div');
43 | next.className = 'section';
44 | const img = document.createElement('img');
45 | img.src = images[current];
46 | next.appendChild(img);
47 |
48 | if ((p - Math.max(0, dir)) % 2 === 0) up(part, next, bgMainHeight);
49 | else down(part, next, bgMainHeight);
50 | });
51 | }
52 |
53 | function up(part, next, height) {
54 | part.appendChild(next);
55 | gsap.to(part, { ...animOptions, y: -height }).then(() => {
56 | part.children[0].remove();
57 | gsap.to(part, { duration: 0, y: 0 });
58 | playing = false;
59 | });
60 | }
61 |
62 | function down(part, next, height) {
63 | part.prepend(next);
64 | gsap.to(part, { duration: 0, y: -height });
65 | gsap.to(part, { ...animOptions, y: 0 }).then(() => {
66 | part.children[1].remove();
67 | playing = false;
68 | });
69 | }
70 |
71 | setInterval(() => go(sort), time);
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/blogIcon/blogIcon.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:19
6 | * ----------------------------------------------
7 | * @describe: 网站图标处理
8 | */
9 | export default function main() {
10 | const shortcutIcon = $('#favicon');
11 | if ($.__config.info.blogIcon && shortcutIcon.length) shortcutIcon.attr('href', $.__config.info.blogIcon);
12 | if ($.__config.info.blogIcon && !shortcutIcon.length) {
13 | let linkObject = document.createElement('link');
14 | linkObject.rel = 'shortcut icon';
15 | linkObject.href = $.__config.info.blogIcon;
16 | document.getElementsByTagName('head')[0].appendChild(linkObject);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/comment/comment.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:20
6 | * ----------------------------------------------
7 | * @describe: 评论处理
8 | */
9 |
10 | export default function main() {
11 | // 评论框打字特效
12 | if ($.__config.articleContent.commentTyping.enable) {
13 | const POWERMODE = require('./commentTyping/commentTyping');
14 | POWERMODE.colorful = $.__config.articleContent.commentTyping.options.colorful;
15 | POWERMODE.shake = $.__config.articleContent.commentTyping.options.shake;
16 | document.body.addEventListener('input', POWERMODE);
17 | }
18 |
19 | let setComment = () => {
20 | let feedbackItem = $('.feedbackItem');
21 | if (feedbackItem.length) {
22 | $.each(feedbackItem, (i) => {
23 | let obj = $(feedbackItem[i]),
24 | feedbackCon = obj.find('.feedbackCon'),
25 | feedbackListSubtitle = obj.find('.feedbackListSubtitle'),
26 | commentBody = feedbackCon.length ? feedbackCon.find('.blog_comment_body') : [],
27 | avatarHtml = '',
28 | idInfo = commentBody.length ? commentBody.attr('id').split('_') : undefined;
29 |
30 | if (idInfo && idInfo.length) {
31 | let id = idInfo[idInfo.length - 1],
32 | idTmp = id.toString().match(/\d/g);
33 |
34 | if ($.isArray(idTmp)) id = idTmp.join('');
35 | let op = $(`#comment_${id}_avatar`),
36 | patch = op.length ? op.text().trim() : $.__config.default.avatar;
37 |
38 | let ac = $(`#a_comment_author_${id}`),
39 | ah = ac.length ? ac.attr('href') : 'javascropt:void(0);';
40 |
41 | avatarHtml = `

`;
42 | obj.prepend(avatarHtml);
43 | }
44 |
45 | feedbackListSubtitle.length && feedbackListSubtitle.find('.louzhu').length && feedbackListSubtitle.addClass('feedbackListSubtitle-louzhu');
46 | });
47 | $(feedbackItem[0]).css('padding-top', '0');
48 | $(feedbackItem[feedbackItem.length - 1]).css('padding-bottom', '0');
49 | }
50 | };
51 |
52 | let addComment = () => {
53 | let userBlogAddress = $('.comment_my_posted a').attr('href'),
54 | userName = $('.comment_my_posted a').text(),
55 | commentInfo = $('.bq_post_comment').text();
56 |
57 | let comment = `
58 |
63 |
64 | ${window.isBlogOwner && `[楼主]`}
65 |
66 |
67 |
68 |
73 |
`;
74 |
75 | $('#blog-comments-placeholder').append(comment);
76 | $('.comment_my_posted').remove();
77 | };
78 |
79 | $.__timeIds.commentTId = window.setInterval(() => {
80 | if ($('.feedbackItem').length) {
81 | setComment();
82 | $.__tools.clearIntervalTimeId($.__timeIds.commentTId);
83 | }
84 | }, 1000);
85 |
86 | $(document).ajaxSuccess(function (event, xhr, settings) {
87 | // 评论重新排序
88 | if (settings.url.includes('comments-block')) {
89 | $.__tools.clearIntervalTimeId($.__timeIds.commentTId);
90 | setComment();
91 | }
92 |
93 | // 新增评论
94 | if (settings.url.includes('PostComment/Add.aspx')) addComment();
95 |
96 | // 删除评论
97 | if (settings.url.includes('comment/DeleteComment.aspx')) {
98 | let commentId = JSON.parse(settings?.data)?.commentId;
99 | $(`#comment_body_${commentId}`).parent().parent().remove();
100 | $('.feedbackItem:last').css('padding-bottom', '0');
101 | }
102 | });
103 | }
104 |
--------------------------------------------------------------------------------
/src/components/comment/commentTyping/commentTyping.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: https://github.com/disjukr/activate-power-mode
5 | * @Date 2023-02-19 12:54
6 | * ----------------------------------------------
7 | * @describe: POWERMODE
8 | */
9 |
10 | let canvas = document.createElement('canvas');
11 | canvas.width = window.innerWidth;
12 | canvas.height = window.innerHeight;
13 | canvas.style.cssText = 'position:fixed;top:0;left:0;pointer-events:none;z-index:999999';
14 | window.addEventListener('resize', function () {
15 | canvas.width = window.innerWidth;
16 | canvas.height = window.innerHeight;
17 | });
18 | document.body.appendChild(canvas);
19 | let context = canvas.getContext('2d');
20 | let particles = [];
21 | let particlePointer = 0;
22 | let rendering = false;
23 |
24 | POWERMODE.shake = true;
25 |
26 | function getRandom(min, max) {
27 | return Math.random() * (max - min) + min;
28 | }
29 |
30 | function getColor(el) {
31 | if (POWERMODE.colorful) {
32 | let u = getRandom(0, 360);
33 | return 'hsla(' + getRandom(u - 10, u + 10) + ', 100%, ' + getRandom(50, 80) + '%, ' + 1 + ')';
34 | } else {
35 | return window.getComputedStyle(el).color;
36 | }
37 | }
38 |
39 | function getCaret() {
40 | let el = document.activeElement;
41 | let bcr;
42 | if (el.tagName === 'TEXTAREA' ||
43 | (el.tagName === 'INPUT' && el.getAttribute('type') === 'text')) {
44 | let offset = require('./textareaCaretPosition')(el, el.selectionEnd);
45 | bcr = el.getBoundingClientRect();
46 | return {
47 | x: offset.left + bcr.left,
48 | y: offset.top + bcr.top,
49 | color: getColor(el)
50 | };
51 | }
52 | let selection = window.getSelection();
53 | if (selection.rangeCount) {
54 | let range = selection.getRangeAt(0);
55 | let startNode = range.startContainer;
56 | if (startNode.nodeType === document.TEXT_NODE) {
57 | startNode = startNode.parentNode;
58 | }
59 | bcr = range.getBoundingClientRect();
60 | return {
61 | x: bcr.left,
62 | y: bcr.top,
63 | color: getColor(startNode)
64 | };
65 | }
66 | return { x: 0, y: 0, color: 'transparent' };
67 | }
68 |
69 | function createParticle(x, y, color) {
70 | return {
71 | x: x,
72 | y: y,
73 | alpha: 1,
74 | color: color,
75 | velocity: {
76 | x: -1 + Math.random() * 2,
77 | y: -3.5 + Math.random() * 2
78 | }
79 | };
80 | }
81 |
82 | function POWERMODE() {
83 | { // spawn particles
84 | let caret = getCaret();
85 | let numParticles = 5 + Math.round(Math.random() * 10);
86 | while (numParticles--) {
87 | particles[particlePointer] = createParticle(caret.x, caret.y, caret.color);
88 | particlePointer = (particlePointer + 1) % 500;
89 | }
90 | }
91 | { // shake screen
92 | if (POWERMODE.shake) {
93 | let intensity = 1 + 2 * Math.random();
94 | let x = intensity * (Math.random() > 0.5 ? -1 : 1);
95 | let y = intensity * (Math.random() > 0.5 ? -1 : 1);
96 | document.body.style.marginLeft = x + 'px';
97 | document.body.style.marginTop = y + 'px';
98 | setTimeout(function() {
99 | document.body.style.marginLeft = '';
100 | document.body.style.marginTop = '';
101 | }, 75);
102 | }
103 | }
104 | if(!rendering){
105 | requestAnimationFrame(loop);
106 | }
107 | };
108 | POWERMODE.colorful = false;
109 |
110 | function loop() {
111 | rendering = true;
112 | context.clearRect(0, 0, canvas.width, canvas.height);
113 | let rendered = false;
114 | let rect = canvas.getBoundingClientRect();
115 | for (let i = 0; i < particles.length; ++i) {
116 | let particle = particles[i];
117 | if (particle.alpha <= 0.1) continue;
118 | particle.velocity.y += 0.075;
119 | particle.x += particle.velocity.x;
120 | particle.y += particle.velocity.y;
121 | particle.alpha *= 0.96;
122 | context.globalAlpha = particle.alpha;
123 | context.fillStyle = particle.color;
124 | context.fillRect(
125 | Math.round(particle.x - 1.5) - rect.left,
126 | Math.round(particle.y - 1.5) - rect.top,
127 | 3, 3
128 | );
129 | rendered = true;
130 | }
131 | if(rendered){
132 | requestAnimationFrame(loop);
133 | }else{
134 | rendering = false;
135 | }
136 | }
137 |
138 | module.exports = POWERMODE;
139 |
--------------------------------------------------------------------------------
/src/components/comment/commentTyping/textareaCaretPosition.js:
--------------------------------------------------------------------------------
1 | /* jshint browser: true */
2 |
3 | (function () {
4 |
5 | // The properties that we copy into a mirrored div.
6 | // Note that some browsers, such as Firefox,
7 | // do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
8 | // so we have to do every single property specifically.
9 | var properties = [
10 | 'direction', // RTL support
11 | 'boxSizing',
12 | 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
13 | 'height',
14 | 'overflowX',
15 | 'overflowY', // copy the scrollbar for IE
16 |
17 | 'borderTopWidth',
18 | 'borderRightWidth',
19 | 'borderBottomWidth',
20 | 'borderLeftWidth',
21 | 'borderStyle',
22 |
23 | 'paddingTop',
24 | 'paddingRight',
25 | 'paddingBottom',
26 | 'paddingLeft',
27 |
28 | // https://developer.mozilla.org/en-US/docs/Web/CSS/font
29 | 'fontStyle',
30 | 'fontVariant',
31 | 'fontWeight',
32 | 'fontStretch',
33 | 'fontSize',
34 | 'fontSizeAdjust',
35 | 'lineHeight',
36 | 'fontFamily',
37 |
38 | 'textAlign',
39 | 'textTransform',
40 | 'textIndent',
41 | 'textDecoration', // might not make a difference, but better be safe
42 |
43 | 'letterSpacing',
44 | 'wordSpacing',
45 |
46 | 'tabSize',
47 | 'MozTabSize'
48 |
49 | ];
50 |
51 | var isFirefox = window.mozInnerScreenX != null;
52 |
53 | function getCaretCoordinates(element, position, options) {
54 |
55 | var debug = options && options.debug || false;
56 | if (debug) {
57 | var el = document.querySelector('#input-textarea-caret-position-mirror-div');
58 | if ( el ) { el.parentNode.removeChild(el); }
59 | }
60 |
61 | // mirrored div
62 | var div = document.createElement('div');
63 | div.id = 'input-textarea-caret-position-mirror-div';
64 | document.body.appendChild(div);
65 |
66 | var style = div.style;
67 | var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
68 |
69 | // default textarea styles
70 | style.whiteSpace = 'pre-wrap';
71 | if (element.nodeName !== 'INPUT')
72 | style.wordWrap = 'break-word'; // only for textarea-s
73 |
74 | // position off-screen
75 | style.position = 'absolute'; // required to return coordinates properly
76 | if (!debug)
77 | style.visibility = 'hidden'; // not 'display: none' because we want rendering
78 |
79 | // transfer the element's properties to the div
80 | properties.forEach(function (prop) {
81 | style[prop] = computed[prop];
82 | });
83 |
84 | if (isFirefox) {
85 | // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
86 | if (element.scrollHeight > parseInt(computed.height))
87 | style.overflowY = 'scroll';
88 | } else {
89 | style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
90 | }
91 |
92 | div.textContent = element.value.substring(0, position);
93 | // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
94 | if (element.nodeName === 'INPUT')
95 | div.textContent = div.textContent.replace(/\s/g, "\u00a0");
96 |
97 | var span = document.createElement('span');
98 | // Wrapping must be replicated *exactly*, including when a long word gets
99 | // onto the next line, with whitespace at the end of the line before (#7).
100 | // The *only* reliable way to do that is to copy the *entire* rest of the
101 | // textarea's content into the
created at the caret position.
102 | // for inputs, just '.' would be enough, but why bother?
103 | span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
104 | div.appendChild(span);
105 |
106 | var coordinates = {
107 | top: span.offsetTop + parseInt(computed['borderTopWidth']),
108 | left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
109 | };
110 |
111 | if (debug) {
112 | span.style.backgroundColor = '#aaa';
113 | } else {
114 | document.body.removeChild(div);
115 | }
116 |
117 | return coordinates;
118 | }
119 |
120 | if (typeof module != "undefined" && typeof module.exports != "undefined") {
121 | module.exports = getCaretCoordinates;
122 | } else {
123 | window.getCaretCoordinates = getCaretCoordinates;
124 | }
125 |
126 | }());
127 |
--------------------------------------------------------------------------------
/src/components/common/comAfter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:20
6 | * ----------------------------------------------
7 | * @describe: 后置公共处理
8 | */
9 | import progress from '../progress/progress';
10 | import title from '../title/title';
11 | import footer from '../footer/footer';
12 | import rtMenu from '../rtMenu/rtMenu';
13 | import blogIcon from '../blogIcon/blogIcon';
14 | import dayNight from '../dayNight/dayNight';
15 | import console from '../console/console';
16 |
17 | export default function main() {
18 | // 页脚
19 | footer();
20 |
21 | // 右下角菜单
22 | rtMenu();
23 |
24 | // 日/夜模式
25 | dayNight();
26 |
27 | // 进度条
28 | progress();
29 |
30 | // 背景动效
31 | (async () => {
32 | for (const [key, config] of Object.entries($.__config.animate.background)) {
33 | if (config.enable) {
34 | const module = await import(/* webpackChunkName: "background-[request]" */ `../background/${key}`);
35 | const backgroundEffect = module.default;
36 | backgroundEffect(config.options || {});
37 | }
38 | }
39 | })();
40 |
41 | // 鼠标动效
42 | (async () => {
43 | for (const [key, config] of Object.entries($.__config.animate.mouse)) {
44 | if (config.enable) {
45 | const module = await import(/* webpackChunkName: "mouse-[request]" */ `../mouse/${key}`);
46 | const mouse = module.default;
47 | mouse(config.options);
48 | }
49 | }
50 | })();
51 |
52 | // 网站图标
53 | blogIcon();
54 |
55 | // 页面title
56 | title();
57 |
58 | // 控制台输出
59 | console();
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/common/comBefore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:20
6 | * ----------------------------------------------
7 | * @describe: 前置公共处理
8 | */
9 | import sidebar from '../sidebar/sidebar';
10 | import banner from '../banner/banner';
11 | import event from '../event/event';
12 | await $.__tools.dynamicLoadingJs($.__config.default.jqueryrotate).catch((e) => console.error('jqueryrotate.js', e));
13 |
14 | export default function main() {
15 | // 默认字体图标库
16 | import(/* webpackChunkName: "iconfont" */ /* webpackPreload: true */ '../../style/iconfont.css');
17 |
18 | // 谷歌字体
19 | import(/* webpackChunkName: "google-fonts" */ /* webpackPreload: true */ '../../style/google-fonts.css');
20 |
21 | // 国家公祭日和自定义重要的缅怀的日子
22 | const today = $.__tools.getTodayDate();
23 | if (today == '12-13' || $.__config.memorialDays.includes(today)) $('html').css('filter', 'grayscale(100%)');
24 |
25 | // 定时清除全部计时器
26 | setTimeout(() => {
27 | Object.values($.__timeIds).forEach((id) => id && clearInterval(id));
28 | }, 30000);
29 |
30 | // 事件绑定
31 | event.init();
32 |
33 | // 侧边栏
34 | sidebar();
35 |
36 | // 头图
37 | banner();
38 |
39 | // 添加扩展字体图标库;
40 | if ($.__config.fontIconExtend !== '') $.__tools.dynamicLoadingCss($.__config.fontIconExtend, 1);
41 |
42 | $.__loading.stop();
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/console/console.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:21
6 | * ----------------------------------------------
7 | * @describe: 控制台输出处理
8 | */
9 |
10 | export default function main() {
11 | // 输出默认版权信息
12 | let github = [
13 | '\n %c %c %c CnblogsTheme-GitHub %c %c https://github.com/wangyang0210/cnblogs-theme %c \n\n',
14 | 'background: #fadfa3; padding:5px 0;',
15 | 'background: #fadfa3; padding:5px 0;',
16 | 'color: #fadfa3; background: #030307; padding:5px 0;',
17 | 'background: #fadfa3; padding:5px 0;',
18 | 'background: #FCEDC9; color:#030307; padding:5px 0;',
19 | 'background: #fadfa3; padding:5px 0;',
20 | ];
21 | window.console.log.apply(console, github);
22 | const consoleListData = $.__config.consoleList;
23 | if (consoleListData.length) {
24 | consoleListData.forEach(({text, link}) => {
25 | console.log(`\n %c ${text} %c ${link}\n`, 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; color:#000;padding:5px 0;');
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/dayNight/dayNight.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:21
6 | * ----------------------------------------------
7 | * @describe: 日夜间模式处理
8 | */
9 | import dayNightTemp from '../../template/dayNight.html';
10 |
11 | export default function main() {
12 | if (!$.__config.switchDayNight.enable) return;
13 |
14 | let h = parseInt(new Date().getHours()),
15 | cookieKey = 'cnblogs_config_isNight',
16 | exp = 4 * 3600,
17 | daySwitch;
18 | $.__status.dayNightCssHref = '';
19 | const { auto, nightMode } = $.__config.switchDayNight;
20 |
21 | // 切换评论框背景
22 | const switchCommentBackground = (status) => {
23 | $.__config.articleContent.commentBackground.enable && $.__tools.setCommentBackground(status);
24 | };
25 |
26 | const switchHighlighterTheme = (lighterCodeTheme) => {
27 | window.enableCodeThemeTypeFollowSystem && window.highlighter.setTheme(lighterCodeTheme);
28 | };
29 |
30 | const getAutoSwitch = () => {
31 | if (auto.enable) return h >= auto.nightHour ? '' : h >= auto.dayHour ? 'daySwitch' : '';
32 | return 'daySwitch';
33 | };
34 | const cookieValue = $.__tools.getCookie(cookieKey);
35 | daySwitch = nightMode ? '' : cookieValue === 'day' ? 'daySwitch' : cookieValue === 'night' ? '' : getAutoSwitch();
36 |
37 | $('body').prepend($.__tools.tempReplacement(dayNightTemp, 'daySwitch', daySwitch));
38 |
39 | if (daySwitch) {
40 | switchCommentBackground('day');
41 | switchHighlighterTheme(window.codeHighlightTheme);
42 | } else {
43 | loadDarkCss();
44 | switchCommentBackground('night');
45 | switchHighlighterTheme(window.darkModeCodeHighlightTheme);
46 | }
47 |
48 | /**
49 | * 模式切换事件
50 | */
51 | $('#dayNightSwitch .onOff').click(function () {
52 | if ($(this).hasClass('daySwitch')) {
53 | // 夜间
54 | $.__tools.setCookie(cookieKey, 'night', exp);
55 | $(this).removeClass('daySwitch');
56 | loadDarkCss();
57 | switchCommentBackground('night');
58 | switchHighlighterTheme(window.darkModeCodeHighlightTheme);
59 | } else {
60 | // 日间
61 | $.__tools.setCookie(cookieKey, 'day', exp);
62 | $(this).addClass('daySwitch');
63 | $('head link#baseDarkCss').remove();
64 | switchCommentBackground('day');
65 | switchHighlighterTheme(window.codeHighlightTheme);
66 | }
67 | });
68 |
69 | /**
70 | * 加载夜间模式样式文件
71 | * 第一次初始化使用 import 加载并记录路径
72 | * 第二次及以后使用标签构建文件加载
73 | */
74 | function loadDarkCss() {
75 | if ($.__status.dayNightCssHref) {
76 | $('head').append(
77 | ``
78 | );
79 | } else {
80 | import(/* webpackChunkName: "day-night" */ /* webpackPrefetch: true */ '../../style/base.dark.css');
81 |
82 | setTimeout(function () {
83 | let links = $('head link');
84 | for (let i = links.length - 1; i > 0; i--) {
85 | let obj = $(links[i]);
86 | let href = obj.attr('href');
87 | if (/^.*\/day-night\.[a-z0-9]{8}\.css$/.test(href)) {
88 | $.__status.dayNightCssHref = href;
89 | obj.attr('id', 'baseDarkCss');
90 | break;
91 | }
92 | }
93 | }, 500);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/components/event/event.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:21
6 | * ----------------------------------------------
7 | * @describe: 事件监听
8 | */
9 |
10 | export default {
11 | init() {
12 | $.__event.scroll = {
13 | handle: [],
14 | temScroll: 0,
15 | docScroll: $(document).scrollTop(),
16 | homeScroll: $('#home').offset().top - 40,
17 | };
18 |
19 | $(window).scroll(() => {
20 | const { scroll } = $.__event;
21 | scroll.docScroll = $(document).scrollTop();
22 | scroll.homeScroll = $('#home').offset().top - 40;
23 | this.handle.scroll();
24 | scroll.temScroll = scroll.docScroll;
25 | });
26 | $.__event.resize = { handle: [] };
27 | $(window).resize(() => this.handle.resize());
28 | },
29 | handle: {
30 | scroll() {
31 | $.__event.scroll.handle.forEach((fn) => fn());
32 | },
33 | resize() {
34 | $.__event.resize.handle.forEach((fn) => fn());
35 | $.__tools.setDomHomePosition();
36 | },
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/src/components/footer/footer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:22
6 | * ----------------------------------------------
7 | * @describe: footer底部信息
8 | */
9 | import footerTemp from '../../template/footer.html';
10 | import { request } from '../../utils/request';
11 |
12 | export default function main() {
13 | const footer = $('#footer');
14 | const footerText = footer.text();
15 |
16 | let footerHtml = footerTemp;
17 | let config = $.__config.footer;
18 |
19 | footerHtml = $.__tools.tempReplacement(footerHtml, 'footerText', footerText);
20 |
21 | // 设置音乐播放器
22 | if (config.aplayer.enable) {
23 | Promise.all([$.__tools.dynamicLoadingJs($.__config.default.aplayer), $.__tools.dynamicLoadingJs($.__config.default.meting)])
24 | .then((r) => {
25 | $.__tools.dynamicLoadingCss($.__config.default.aplayercss);
26 | $('#footer').append(`
27 |
47 | `);
48 | })
49 | .catch((e) => console.error('aplayer|meting', e));
50 | }
51 |
52 | // 设置标语
53 | const re = [
54 | ['textLeft', config.text.left],
55 | ['iconFont', config.text.iconFont.icon],
56 | ['iconColor', config.text.iconFont.color],
57 | ['iconSize', config.text.iconFont.fontSize],
58 | ['textRight', config.text.right],
59 | ];
60 |
61 | if (config.text.left || config.text.right) {
62 | re.push(['textShow', 'block']);
63 | } else {
64 | re.push(['textShow', 'none']);
65 | }
66 |
67 | footerHtml = $.__tools.batchTempReplacement(footerHtml, re);
68 |
69 | // 设置友情链接
70 | const footerLinksData = $.__config.links.footer;
71 | if (footerLinksData.length) {
72 | const linksHtml = footerLinksData
73 | .map(({ text, link }, index) => {
74 | return `${text}${index < footerLinksData.length - 1 ? '/' : ''}`;
75 | })
76 | .join('');
77 |
78 | footerHtml = $.__tools.batchTempReplacement(footerHtml, [
79 | ['linksHtml', `友情链接:${linksHtml}`],
80 | ['linkShow', 'block'],
81 | ]);
82 | } else {
83 | footerHtml = $.__tools.tempReplacement(footerHtml, 'linkShow', 'none');
84 | }
85 |
86 | // 添加页脚
87 | footer.html(footerHtml);
88 |
89 | // 页脚样式
90 | switch (parseInt(config.style)) {
91 | case 1:
92 | $('#footer').addClass('footer-t1').find('#footerStyle1').show().css('background', 'url(//images.cnblogs.com/cnblogs_com/wangyang0210/1943283/o_221114131838_footer.webp) no-repeat 50%');
93 | break;
94 | case 2:
95 | default:
96 | $('#footer .footer-text').css({ 'padding-bottom': '0', 'border-bottom': 'none', 'margin-bottom': '0' });
97 | const footerStyle2 = $('#footerStyle2');
98 | footerStyle2.show().find('.clouds').css('background', 'url(//images.cnblogs.com/cnblogs_com/wangyang0210/1943283/o_221114132857_clouds.webp) repeat-x');
99 | footerStyle2.find('.background').css('background', 'url(//images.cnblogs.com/cnblogs_com/wangyang0210/1943283/o_221114134558_background.webp) repeat-x');
100 | footerStyle2.find('.foreground').css('background', 'url(//images.cnblogs.com/cnblogs_com/wangyang0210/1943283/o_221114132230_foreground.webp) repeat-x');
101 | break;
102 | }
103 |
104 | // 设置运行时间
105 | window.setInterval(() => {
106 | let runDate = $.__tools.getRunDate(($.__config.info.startDate ||= '2021-01-01'));
107 | $('#blogRunTimeSpan').text(`This blog has running : ${runDate.daysold} d ${runDate.hrsold} h ${runDate.minsold} m ${runDate.seconds} s`);
108 | }, 500);
109 |
110 | // 定时网站统计
111 | if ($.__config.umami?.url && $.__config.umami?.shareId) {
112 | const baseUrl = $.__config.umami.url;
113 | $.__timeIds.umamiTId = window.setInterval(() => {
114 | request(`${baseUrl}/api/share/${$.__config.umami.shareId}`).then((r) => {
115 | Promise.all([
116 | request(`${baseUrl}/api/websites/${r.id}/stats?start_at=${$.__tools.getTodayStart()}&end_at=${$.__tools.getTodayEnd()}`, 'GET', {}, { 'x-umami-share-token': r.token }),
117 | request(`${baseUrl}/api/websites/${r.id}/stats?start_at=${$.__tools.getYesterdayStart()}&end_at=${$.__tools.getYesterdayEnd()}`, 'GET', {}, { 'x-umami-share-token': r.token }),
118 | request(`${baseUrl}/api/websites/${r.id}/active`, 'GET', {}, { 'x-umami-share-token': r.token }),
119 | ]).then((results) => {
120 | const todayState = results[0];
121 | const yesterdayState = results[1];
122 | const online = results[2];
123 | $('#cnzzInfo')
124 | .text(
125 | `Online: ${online[0].x} | Today: ${todayState.pageviews.value} / ${todayState.uniques.value} / ${todayState.totaltime.value} | Yesterday: ${yesterdayState.pageviews.value} / ${yesterdayState.uniques.value} / ${yesterdayState.totaltime.value}`
126 | )
127 | .show();
128 | });
129 | });
130 | $.__tools.clearIntervalTimeId($.__timeIds.umamiTId);
131 | }, 1000);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/components/greenChannel/greenChannel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:22
6 | * ----------------------------------------------
7 | * @describe: 文章底部信息按钮处理
8 | */
9 | import '../../style/customBtn.css';
10 |
11 | export default function main() {
12 | const buttons = [
13 | {
14 | id: 'green_channel_digg',
15 | text: '推荐该文',
16 | className: 'custom-btn btn-11',
17 | },
18 | {
19 | id: 'green_channel_follow',
20 | text: '关注博主',
21 | className: 'custom-btn btn-8',
22 | },
23 | {
24 | id: 'green_channel_favorite',
25 | text: '收藏本文',
26 | className: 'custom-btn btn-7',
27 | },
28 | {
29 | id: 'green_channel_weibo',
30 | text: '分享微博',
31 | className: 'custom-btn btn-15',
32 | },
33 | {
34 | id: 'green_channel_wechat',
35 | text: '分享微信',
36 | className: 'custom-btn btn-13',
37 | },
38 | ];
39 |
40 | buttons.forEach((button) => {
41 | $.__timeIds[`${button.id}TId`] = window.setInterval(() => {
42 | const element = $(`#${button.id}`);
43 | if (element.length) {
44 | element.after(
45 | ``
48 | );
49 | $.__tools.clearIntervalTimeId($.__timeIds[`${button.id}TId`]);
50 | }
51 | }, 1000);
52 | });
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/imgBox/imgBox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:22
6 | * ----------------------------------------------
7 | * @describe: 图片灯箱处理
8 | */
9 | await $.__tools.dynamicLoadingCss($.__config.default.fancyboxcss);
10 | await $.__tools.dynamicLoadingJs($.__config.default.fancybox).catch((e) => console.error('fancybox.js', e));
11 | export default function main() {
12 | setTimeout(() => {
13 | let imgLength = $('#cnblogs_post_body img').length - 1;
14 | if (!imgLength) return;
15 |
16 | let cpb = $('#cnblogs_post_body'),
17 | imgList = $(`#cnblogs_post_body img:lt(${imgLength})`),
18 | comImgList = $('.feedbackCon img'),
19 | data = [];
20 |
21 | $.each(imgList, (i) => data.push(imgList[i]));
22 |
23 | $.each(comImgList, (i) => data.push(comImgList[i]));
24 |
25 | if (cpb.length && data.length) {
26 | $.each(data, (i) => {
27 | let tem = $(data[i]);
28 | if (!tem.hasClass('code_img_closed') && !tem.hasClass('code_img_opened')) {
29 | let width = tem.attr('width');
30 | let height = tem.attr('height');
31 | let alt = tem.attr('alt') ?? '';
32 | let style = tem.attr('style') ?? '';
33 | tem.after(
34 | `
35 |
`
38 | );
39 | tem.remove();
40 | }
41 | });
42 | }
43 | }, 800);
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/loading/loading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:22
6 | * ----------------------------------------------
7 | * @describe: loading 处理
8 | */
9 | import { rebound, Spinner } from 'exports-loader?exports=rebound,Spinner!../../vendor/loading/loading';
10 |
11 | export default function main() {
12 | let loading = function () {
13 | let that = this;
14 | this.config = $?.__config?.loading || {
15 | rebound: {
16 | tension: 16,
17 | friction: 5,
18 | },
19 | spinner: {
20 | id: 'spinner',
21 | radius: 90,
22 | sides: 3,
23 | depth: 4,
24 | colors: {
25 | background: '#f0f0f0',
26 | stroke: '#272633',
27 | base: null,
28 | child: '#272633',
29 | },
30 | alwaysForward: true,
31 | restAt: 0.5,
32 | renderBase: false,
33 | },
34 | };
35 | this.spring = null;
36 | this.spinner = null;
37 | this.initRebound = () => {
38 | let settings = that.config.rebound;
39 |
40 | let springSystem = new rebound.SpringSystem();
41 |
42 | that.spring = springSystem.createSpring(settings.tension, settings.friction);
43 | };
44 | this.initSpinner = () => {
45 | let settings = that.config.spinner;
46 |
47 | that.spinner = new Spinner(settings);
48 | };
49 | this.start = () => {
50 | $('#blog-news').prepend('');
51 | that.initRebound();
52 | that.initSpinner();
53 | that.spinner.init(that.spring, true);
54 | };
55 | this.stop = () => {
56 | $('body').css('overflow', 'auto');
57 | that.spinner.setComplete();
58 | $('div#loading').hide();
59 | $('a[name="top"]').hide();
60 | };
61 | };
62 | return new loading();
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/mouse/bubble.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-09-02 23:05
6 | * ----------------------------------------------
7 | * @describe: 鼠标移动汽包粒子效果
8 | */
9 |
10 | export default function main(options) {
11 | let canvasElement = document.createElement('canvas');
12 | canvasElement.id = 'bubble';
13 | $('#home').after(canvasElement);
14 | let canvas = document.getElementById('bubble');
15 | let ctx = canvas.getContext('2d');
16 | canvas.width = window.innerWidth;
17 | canvas.height = window.innerHeight;
18 | canvas.style.position = 'fixed';
19 | canvas.style.left = '0';
20 | canvas.style.bottom = '0';
21 | canvas.style.zIndex = '999999999999999999999999999999999999999999';
22 | canvas.style.pointerEvents = 'none';
23 | let points = [];
24 | let live = options.live;
25 | let colors = options.colors;
26 |
27 | window.addEventListener('mousemove', function (evt) {
28 | for (let i = 0; i < options.quantity; i++) {
29 | points.push({
30 | sx: evt.x,
31 | sy: evt.y,
32 | vx: 0.5 - Math.random(),
33 | vy: 0.5 - Math.random(),
34 | life: live, //存活周期
35 | color: colors[parseInt(Math.random() * colors.length)],
36 | size: Math.random() * options.size,
37 | });
38 | }
39 | });
40 |
41 | function drawpoints() {
42 | ctx.clearRect(0, 0, canvas.width, canvas.height);
43 | for (let i = 0; i < points.length; i++) {
44 | let point = points[i];
45 | ctx.beginPath();
46 | ctx.arc(point.sx, point.sy, point.size, Math.PI * 2, false);
47 | ctx.fillStyle = 'rgba(' + point.color + ',' + point.life / live + ')';
48 | ctx.fill();
49 | point.life--;
50 | if (point.life <= 0) {
51 | points.splice(i, 1);
52 | }
53 | point.sx += point.vx * 3;
54 | point.sy += point.vy * 3;
55 | }
56 | }
57 |
58 | setInterval(drawpoints, 20);
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/mouse/click.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-27 20:29
6 | * ----------------------------------------------
7 | * @describe: 鼠标粒子点击特效
8 | */
9 |
10 | export default function main() {
11 | class Circle {
12 | constructor({ origin, speed, color, angle, context }) {
13 | this.origin = origin;
14 | this.position = {
15 | ...this.origin,
16 | };
17 | this.color = color;
18 | this.speed = speed;
19 | this.angle = angle;
20 | this.context = context;
21 | this.renderCount = 0;
22 | }
23 |
24 | draw() {
25 | this.context.fillStyle = this.color;
26 | this.context.beginPath();
27 | this.context.arc(this.position.x, this.position.y, 2, 0, Math.PI * 2);
28 | this.context.fill();
29 | }
30 |
31 | move() {
32 | this.position.x = Math.sin(this.angle) * this.speed + this.position.x;
33 | this.position.y = Math.cos(this.angle) * this.speed + this.position.y + this.renderCount * 0.3;
34 | this.renderCount++;
35 | }
36 | }
37 |
38 | class Boom {
39 | constructor({ origin, context, circleCount = 10, area }) {
40 | this.origin = origin;
41 | this.context = context;
42 | this.circleCount = circleCount;
43 | this.area = area;
44 | this.stop = false;
45 | this.circles = [];
46 | }
47 |
48 | randomArray(range) {
49 | const length = range.length;
50 | const randomIndex = Math.floor(length * Math.random());
51 | return range[randomIndex];
52 | }
53 |
54 | randomColor() {
55 | const range = ['8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
56 | return (
57 | '#' +
58 | this.randomArray(range) +
59 | this.randomArray(range) +
60 | this.randomArray(range) +
61 | this.randomArray(range) +
62 | this.randomArray(range) +
63 | this.randomArray(range)
64 | );
65 | }
66 |
67 | randomRange(start, end) {
68 | return (end - start) * Math.random() + start;
69 | }
70 |
71 | init() {
72 | for (let i = 0; i < this.circleCount; i++) {
73 | const circle = new Circle({
74 | context: this.context,
75 | origin: this.origin,
76 | color: this.randomColor(),
77 | angle: this.randomRange(Math.PI - 1, Math.PI + 1),
78 | speed: this.randomRange(1, 6),
79 | });
80 | this.circles.push(circle);
81 | }
82 | }
83 |
84 | move() {
85 | this.circles.forEach((circle, index) => {
86 | if (circle.position.x > this.area.width || circle.position.y > this.area.height) {
87 | return this.circles.splice(index, 1);
88 | }
89 | circle.move();
90 | });
91 | if (this.circles.length == 0) this.stop = true;
92 | }
93 |
94 | draw() {
95 | this.circles.forEach((circle) => circle.draw());
96 | }
97 | }
98 |
99 | class CursorSpecialEffects {
100 | constructor() {
101 | this.computerCanvas = document.createElement('canvas');
102 | this.renderCanvas = document.createElement('canvas');
103 |
104 | this.computerContext = this.computerCanvas.getContext('2d');
105 | this.renderContext = this.renderCanvas.getContext('2d');
106 |
107 | this.globalWidth = window.innerWidth;
108 | this.globalHeight = window.innerHeight;
109 |
110 | this.booms = [];
111 | this.running = false;
112 | }
113 |
114 | handleMouseDown(e) {
115 | const boom = new Boom({
116 | origin: {
117 | x: e.clientX,
118 | y: e.clientY,
119 | },
120 | context: this.computerContext,
121 | area: {
122 | width: this.globalWidth,
123 | height: this.globalHeight,
124 | },
125 | });
126 | boom.init();
127 | this.booms.push(boom);
128 | this.running || this.run();
129 | }
130 |
131 | handlePageHide() {
132 | this.booms = [];
133 | this.running = false;
134 | }
135 |
136 | init() {
137 | const style = this.renderCanvas.style;
138 | style.position = 'fixed';
139 | style.top = style.left = 0;
140 | style.zIndex = '999999999999999999999999999999999999999999';
141 | style.pointerEvents = 'none';
142 |
143 | style.width = this.renderCanvas.width = this.computerCanvas.width = this.globalWidth;
144 | style.height = this.renderCanvas.height = this.computerCanvas.height = this.globalHeight;
145 |
146 | document.body.append(this.renderCanvas);
147 |
148 | window.addEventListener('mousedown', this.handleMouseDown.bind(this));
149 | window.addEventListener('pagehide', this.handlePageHide.bind(this));
150 | }
151 |
152 | run() {
153 | this.running = true;
154 | if (this.booms.length == 0) {
155 | this.running = false;
156 | return;
157 | }
158 |
159 | requestAnimationFrame(this.run.bind(this));
160 |
161 | this.computerContext.clearRect(0, 0, this.globalWidth, this.globalHeight);
162 | this.renderContext.clearRect(0, 0, this.globalWidth, this.globalHeight);
163 |
164 | this.booms.forEach((boom, index) => {
165 | if (boom.stop) {
166 | return this.booms.splice(index, 1);
167 | }
168 | boom.move();
169 | boom.draw();
170 | });
171 | this.renderContext.drawImage(this.computerCanvas, 0, 0, this.globalWidth, this.globalHeight);
172 | }
173 | }
174 |
175 | const cursorSpecialEffects = new CursorSpecialEffects();
176 | cursorSpecialEffects.init();
177 | }
178 |
--------------------------------------------------------------------------------
/src/components/mouse/cursor.js:
--------------------------------------------------------------------------------
1 | export default function main(options) {
2 | document.body.style.cursor = `url(${options.url || 'https://files.cnblogs.com/files/wangyang0210/normal.gif?t=1681261712'}), default`;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/mouse/mo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-09-04 19:36
6 | * ----------------------------------------------
7 | * @describe: 使用mo.js实现各种效果
8 | */
9 |
10 | export default function main(options) {
11 | $.__tools
12 | .dynamicLoadingJs($.__config.default.mojs)
13 | .then(() => {
14 | const burst = new mojs.Burst({
15 | left: 0,
16 | top: 0,
17 | ...options,
18 | });
19 | burst.el.style.zIndex = 999999;
20 | document.addEventListener('click', function (e) {
21 | burst.tune({ x: e.pageX, y: e.pageY }).setSpeed(3).replay();
22 | });
23 | })
24 | .catch((e) => console.error('mo.js: ', e));
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/mouse/mouse.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:23
6 | * ----------------------------------------------
7 | * @describe: 鼠标移动/点击效果
8 | */
9 | import '../../style/mouse.css';
10 | await $.__tools.dynamicLoadingJs($.__config.default.gsap).catch((e) => console.error('gsap.js', e));
11 | export default function main(options) {
12 | const cursor = document.createElement('div');
13 | cursor.className = 'cursor';
14 |
15 | const cursorF = document.createElement('div');
16 | cursorF.className = 'cursor-f';
17 |
18 | let cursorX = 0;
19 | let cursorY = 0;
20 | let pageX = 0;
21 | let pageY = 0;
22 | let size = options.size;
23 | let sizeF = options.sizeF;
24 | let followSpeed = 0.16;
25 |
26 | document.body.appendChild(cursor);
27 | document.body.appendChild(cursorF);
28 |
29 | if ('ontouchstart' in window) {
30 | cursor.style.display = 'none';
31 | cursorF.style.display = 'none';
32 | }
33 |
34 | cursor.style.setProperty('--size', size + 'px');
35 | cursorF.style.setProperty('--size', sizeF + 'px');
36 |
37 | window.addEventListener('mousemove', function (e) {
38 | pageX = e.pageX;
39 | pageY = e.pageY;
40 | cursor.style.top = pageY - size / 2 + 'px';
41 | let cursorLeft = pageX - size / 2;
42 | let offsetWidth = document.body.offsetWidth;
43 | cursorLeft = cursorLeft < 0 ? 0 : offsetWidth - size < cursorLeft ? offsetWidth - size : cursorLeft;
44 | cursor.style.left = cursorLeft + 'px';
45 | });
46 |
47 | function lerp(start, end, amount) {
48 | return (1 - amount) * start + amount * end;
49 | }
50 |
51 | function loop() {
52 | cursorX = lerp(cursorX, pageX, followSpeed);
53 | cursorY = lerp(cursorY, pageY, followSpeed);
54 | cursorF.style.top = cursorY - sizeF / 2 + 'px';
55 | let cursorFLeft = cursorX - sizeF / 2;
56 | let offsetWidth = document.body.offsetWidth;
57 | cursorFLeft = cursorFLeft < 0 ? 0 : offsetWidth - sizeF < cursorFLeft ? offsetWidth - sizeF : cursorFLeft;
58 | cursorF.style.left = cursorFLeft + 'px';
59 |
60 | requestAnimationFrame(loop);
61 | }
62 |
63 | loop();
64 |
65 | let startY;
66 | let endY;
67 | let clicked = false;
68 |
69 | function mousedown(e) {
70 | gsap.to(cursor, { scale: 4.5 });
71 | gsap.to(cursorF, { scale: 0.4 });
72 |
73 | clicked = true;
74 | startY = e.clientY || e.touches[0].clientY || e.targetTouches[0].clientY;
75 | }
76 |
77 | function mouseup(e) {
78 | gsap.to(cursor, { scale: 1 });
79 | gsap.to(cursorF, { scale: 1 });
80 |
81 | endY = e.clientY || endY;
82 | if (clicked && startY && Math.abs(startY - endY) >= 40) {
83 | clicked = false;
84 | startY = null;
85 | endY = null;
86 | }
87 | }
88 |
89 | window.addEventListener('mousedown', mousedown, false);
90 | window.addEventListener('touchstart', mousedown, false);
91 | window.addEventListener(
92 | 'touchmove',
93 | function (e) {
94 | if (clicked) endY = e.touches[0].clientY || e.targetTouches[0].clientY;
95 | },
96 | false
97 | );
98 | window.addEventListener('touchend', mouseup, false);
99 | window.addEventListener('mouseup', mouseup, false);
100 | }
101 |
--------------------------------------------------------------------------------
/src/components/progress/progress.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:23
6 | * ----------------------------------------------
7 | * @describe: 头部进度条处理
8 | */
9 |
10 | export default function main() {
11 | $.__tools
12 | .dynamicLoadingJs($.__config.default.toprogress)
13 | .then((r) => {
14 | $('#blog-news').prepend('');
15 | let progressBar = ToProgress && new window.ToProgress($.__config.progressBar, '#progressBar');
16 | // 添加事件监听
17 | $.__event.scroll.handle.push(() => progressBar.setProgress($.__tools.getScrollPercent()));
18 | })
19 | .catch((e) => console.log('toprogress.js', e));
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/sidebar/lib/classie.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:24
6 | * ----------------------------------------------
7 | * @describe: 侧边栏处理
8 | */
9 | export default function main() {
10 | function classReg(className) {
11 | return new RegExp(`(^|\\s+)${className}(\\s+|$)`);
12 | }
13 |
14 | const hasClass = (elem, c) => elem.classList ? elem.classList.contains(c) : classReg(c).test(elem.className);
15 | const addClass = (elem, c) => elem.classList ? elem.classList.add(c) : !hasClass(elem, c) && (elem.className += ` ${c}`);
16 | const removeClass = (elem, c) => elem.classList ? elem.classList.remove(c) : elem.className = elem.className.replace(classReg(c), ' ');
17 |
18 | const toggleClass = (elem, c) => (hasClass(elem, c) ? removeClass : addClass)(elem, c);
19 |
20 | return {
21 | hasClass,
22 | addClass,
23 | removeClass,
24 | toggleClass,
25 | has: hasClass,
26 | add: addClass,
27 | remove: removeClass,
28 | toggle: toggleClass,
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/sidebar/lib/main4.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:24
6 | * ----------------------------------------------
7 | * @describe: 侧边栏处理
8 | */
9 | await $.__tools.dynamicLoadingJs($.__config.default.snapsvg).catch((e) => console.error('snapsvg-cjs.js', e));
10 | await $.__tools.dynamicLoadingJs($.__config.default.optiscroll).catch((e) => console.log('optiscroll.js', e));
11 | await $.__tools.dynamicLoadingCss($.__config.default.optiscrollcss);
12 | export default function main() {
13 | const bodyEl = document.body;
14 | const content = document.querySelector('.content-wrap');
15 | const openbtn = document.getElementById('open-button');
16 | const closebtn = document.getElementById('close-button');
17 | const morphEl = document.getElementById('morph-shape');
18 | const s = Snap(morphEl.querySelector('svg'));
19 | const path = s.select('path');
20 | const initialPath = path.attr('d');
21 | let isOpen = false;
22 | let isAnimating = false;
23 | let myOptiscrollInstance;
24 |
25 | function init() {
26 | initEvents();
27 | myOptiscrollInstance = new Optiscroll(document.querySelector('#menuWrap'), {
28 | preventParentScroll: true,
29 | forceScrollbars: true,
30 | });
31 | }
32 |
33 | function initEvents() {
34 | openbtn.addEventListener('click', toggleMenu);
35 | if (closebtn) closebtn.addEventListener('click', toggleMenu);
36 | content.addEventListener('click', (ev) => {
37 | if (isOpen && ev.target !== openbtn) toggleMenu();
38 | });
39 | }
40 |
41 | function toggleMenu() {
42 | $('.menu-wrap').show();
43 | if (isOpen) {
44 | $(bodyEl).removeClass('show-menu');
45 | $('#content-wrap').fadeOut(300);
46 | $(bodyEl).css('overflow', 'auto');
47 | $('#mainContent').off('touchmove');
48 | path.attr('d', initialPath);
49 | isAnimating = false;
50 | } else {
51 | $(bodyEl).addClass('show-menu');
52 | $('#content-wrap').show();
53 | $('body').css('overflow', 'hidden');
54 | myOptiscrollInstance.scrollTo(false, 'top');
55 | }
56 | isOpen = !isOpen;
57 | }
58 |
59 | init();
60 |
61 | return {
62 | myOptiscrollInstance,
63 | };
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/status/status.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:24
6 | * ----------------------------------------------
7 | * @describe: 博客基础信息抓取处理
8 | */
9 |
10 | let status = {
11 | url: window.location.href,
12 | user: '',
13 | pageType: '',
14 | articleId: '',
15 | };
16 | // 提取url信息
17 | let tmp = status.url.split('/');
18 | status.user = tmp[3];
19 | status.homeUrl = tmp.slice(0, 4).join('/');
20 | let topics = $('#topics').length;
21 | status.pageType = !topics
22 | ? 'home'
23 | : $('#bookListFlg').length
24 | ? 'books'
25 | : $('#linkListFlg').length
26 | ? 'links'
27 | : 'article';
28 | if (topics) status.articleId = tmp[tmp.length - 1].split('.')[0];
29 |
30 | export default status;
31 |
--------------------------------------------------------------------------------
/src/components/title/title.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:25
6 | * ----------------------------------------------
7 | * @describe: 页面title处理
8 | */
9 |
10 | export default function main() {
11 |
12 | const RelTitle = document.title;
13 | const config = $.__config.title;
14 | let hidden, visibilityChange, timer;
15 |
16 |
17 | if (typeof document.hidden !== 'undefined') {
18 | hidden = 'hidden';
19 | visibilityChange = 'visibilitychange';
20 | }
21 | if (typeof document.mozHidden !== 'undefined') {
22 | // Firefox up to v17
23 | hidden = 'mozHidden';
24 | visibilityChange = 'mozvisibilitychange';
25 | }
26 |
27 | if (typeof document.webkitHidden !== 'undefined') {
28 | // Chrome up to v32, Android up to v4.4, Blackberry up to v10
29 | hidden = 'webkitHidden';
30 | visibilityChange = 'webkitvisibilitychange';
31 | }
32 |
33 | let handleVisibilityChange = () => {
34 | if (timer) clearTimeout(timer);
35 |
36 | if (document[hidden] && config.onblurTime >= 0) {
37 | timer = setTimeout(() => {
38 | document.title = `${config.onblur} - ${RelTitle.split(' - ')[0]}`;
39 | }, config.onblurTime);
40 | }
41 |
42 | if (!document[hidden] && config.focusTime >= 0) {
43 | document.title = config.focus;
44 | timer = setTimeout(() => {
45 | document.title = RelTitle;
46 | }, config.focusTime);
47 | }
48 |
49 | if (!document[hidden] && config.focusTime < 0) {
50 | document.title = RelTitle;
51 | }
52 | };
53 | if (typeof document.addEventListener !== 'undefined' || typeof document[hidden] !== 'undefined') {
54 | document.addEventListener(visibilityChange, handleVisibilityChange, false);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:26
6 | * ----------------------------------------------
7 | * @describe: 主程序文件
8 | */
9 | import loading from './components/loading/loading';
10 | import defaultConfig from './components/config/config.json5';
11 | import status from './components/status/status';
12 | import tools from './utils/tools';
13 | import event from './components/event/event';
14 | $.__loading = loading();
15 | $.__loading.start();
16 | $(document).ready(function () {
17 | // 初始化
18 | $.__config = $.extend(true, defaultConfig, window?.cnblogsConfig || {}); // 配置信息
19 | $.__status = status; // 页面状态信息
20 | $.__tools = tools; // 公共处理工具
21 | $.__timeIds = {}; // 定时器
22 | $.__event = {}; // 事件
23 | $.__config.info.name ||= $.__status.user;
24 | $.__tools
25 | .dynamicLoadingJs($.__config.default.moment)
26 | .then((r) => {
27 | import(
28 | /* webpackChunkName: "page-[request]" */ /* webpackPrefetch: true */ `./page/${$.__status.pageType}`
29 | ).then((module) => {
30 | const page = module.default;
31 | // 前置公共处理
32 | import(
33 | /* webpackChunkName: "com-before" */ /* webpackPrefetch: true */ './components/common/comBefore'
34 | ).then((beforeModule) => {
35 | const comBefore = beforeModule.default;
36 | comBefore();
37 | // 页面逻辑处理
38 | page();
39 |
40 | // 后置公共处理
41 | import(
42 | /* webpackChunkName: "com-after" */ /* webpackPrefetch: true */ './components/common/comAfter'
43 | ).then((afterModule) => {
44 | const comAfter = afterModule.default;
45 | comAfter();
46 | (() => {
47 | $.__tools.setDomHomePosition(); // 文章主体位置修正
48 | event.handle.scroll(); // 触发滚动处理
49 | event.handle.resize(); // 触发窗口大小变化处理
50 | })();
51 | });
52 | });
53 | });
54 | })
55 | .catch((e) => console.error('moment.js', e));
56 | });
57 |
--------------------------------------------------------------------------------
/src/page/article.js:
--------------------------------------------------------------------------------
1 | import comArticle from './common/com-article';
2 | import imgBox from '../components/imgBox/imgBox';
3 |
4 | export default function main() {
5 | // 文章页公共处理
6 | comArticle();
7 |
8 | // 图片灯箱处理
9 | imgBox();
10 | }
11 |
--------------------------------------------------------------------------------
/src/page/books.js:
--------------------------------------------------------------------------------
1 | import '../style/books.css';
2 | import booksTemp from '../template/books.html';
3 | import articleDirectory from '../components/articleDirectory/articleDirectory';
4 | import comArticle from './common/com-article';
5 |
6 | export default function main() {
7 | // 文章页公共处理
8 | comArticle();
9 |
10 | // 书单页处理
11 | if ($.__config.bookList.length) {
12 | import(/* webpackChunkName: "gf-blink" */ '../style/gf-blink.css');
13 |
14 | let postBody = $('#cnblogs_post_body'),
15 | html = '';
16 | const infoObj = {
17 | formerName: '原 名:',
18 | author: '作 者:',
19 | translator: '译 者:',
20 | press: '出版社:',
21 | year: '出版年:',
22 | direct: '导 演: ',
23 | scenarist: '编 剧: ',
24 | star: '主 演: ',
25 | type: '类 型: ',
26 | productionCountry: '制片国家/地区: ',
27 | language: '语 言: ',
28 | releaseDate: '上映日期: ',
29 | filmLength: '片 长: ',
30 | alias: '别 名: ',
31 | };
32 |
33 | $.__config.bookList.forEach((list) => {
34 | if (list.title) html += `${list.title}
`;
35 | html += '';
36 | list.books.forEach((book) => {
37 | let cardHtml = booksTemp,
38 | scoreHtml = '',
39 | infoHtml = '';
40 |
41 | if (book?.score > 0) {
42 | const fullStars = Math.floor(book.score);
43 | const halfStar = book.score > fullStars ? '' : '';
44 | const emptyStars = ``.repeat(5 - fullStars);
45 | scoreHtml = ``.repeat(fullStars) + halfStar + emptyStars;
46 | } else {
47 | scoreHtml = ``.repeat(5);
48 | }
49 |
50 | Object.entries(infoObj).forEach(([key, value]) => {
51 | if (book?.[key]) infoHtml += `${value} ${book?.[key]}
`;
52 | });
53 |
54 | cardHtml = $.__tools.batchTempReplacement(cardHtml, [
55 | ['cover', book.cover || ''],
56 | ['name', book.name || ''],
57 | ['readDate', book?.readDate || ''],
58 | ['readDateStyle', book?.readDate ? 'initial;' : 'none'],
59 | ['readPercentage', book?.readPercentage || ''],
60 | ['readPercentageStyle', book?.readPercentage ? 'initial;' : 'none'],
61 | ['scoreHtml', scoreHtml],
62 | ['infoHtml', infoHtml],
63 | ]);
64 | html += cardHtml;
65 | });
66 | html += '
';
67 | });
68 | let articleSuffixFlg = $('.articleSuffix-flg');
69 | articleSuffixFlg.length ? articleSuffixFlg.before(html) : postBody.append(html);
70 | }
71 |
72 | // 设置文章目录
73 | articleDirectory();
74 | }
75 |
--------------------------------------------------------------------------------
/src/page/common/com-article.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:26
6 | * ----------------------------------------------
7 | * @describe: 文章页公共处理部分
8 | * 由于书单页、友链页等部分页面基础是文章页,所以共通部分提取至此来处理
9 | */
10 | import articleInfo from '../../components/articleInfo/articleInfo';
11 | import comment from '../../components/comment/comment';
12 | import articleSuffix from '../../components/articleSuffix/articleSuffix';
13 | import articleDirectory from '../../components/articleDirectory/articleDirectory';
14 | import greenChannel from '../../components/greenChannel/greenChannel';
15 |
16 | export default function main() {
17 | /**
18 | * 设置文章banner动效
19 | */
20 |
21 | $.__config.animate.articleBanner.enable &&
22 | import(/* webpackChunkName: "nh-banner-animation" */ '../../style/nhBannerAnimation.css');
23 |
24 | /**
25 | * 清除文章页冲突样式
26 | */
27 | const postMain = $('#main');
28 | const elementsToModify = postMain.find('.cnblogs-markdown, .cnblogs-post-body');
29 | elementsToModify.removeClass('cnblogs-markdown cnblogs-post-body');
30 | // 延迟删除类名
31 | [...Array(11).keys()].forEach((i) => {
32 | setTimeout(() => {
33 | elementsToModify.removeClass('cnblogs-markdown cnblogs-post-body');
34 | }, i * 500);
35 | });
36 |
37 | // 设置文章信息
38 | articleInfo();
39 |
40 | // 设置文章目录
41 | articleDirectory();
42 |
43 | // 设置文章底部信息按钮
44 | greenChannel();
45 |
46 | // 设置文章后缀
47 | articleSuffix();
48 |
49 | // 设置评论框
50 | comment();
51 | }
52 |
--------------------------------------------------------------------------------
/src/page/home.js:
--------------------------------------------------------------------------------
1 | import { request } from '../utils/request';
2 |
3 | export default function main() {
4 | // 设置主页标语
5 | $('#homeTopTitle span').text($.__config.info.name);
6 |
7 | // 博客名字动效
8 | if ($.__config.animate.homeBannerTitle.enable) {
9 | const titleSpan = $('#homeTopTitle span');
10 | titleSpan.hover(
11 | () => titleSpan.addClass('pageTitleText'),
12 | () => titleSpan.removeClass('pageTitleText')
13 | );
14 | }
15 |
16 | // 主页banner动效
17 | if ($.__config.animate.homeBanner.enable) {
18 | import(/* webpackChunkName: "circle-magic" */ '../vendor/circleMagic/circleMagic').then((module) => {
19 | $('.main-header').circleMagic($.__config.animate.homeBanner.options);
20 | });
21 | }
22 |
23 | // 头图点击滚动到内容位置
24 | $('.scroll-down').click(function () {
25 | const endScroll = $('#home').offset().top + 10;
26 | $.__tools.actScroll(endScroll, 500);
27 | });
28 |
29 | // 设置主页文章信息样式
30 | $('#main .c_b_p_desc_readmore').text('阅读全文 »');
31 | const allTitles = $('#main .postTitle, #main .entrylistPosttitle');
32 | $.each(allTitles, (i, titleElement) => {
33 | let title = $(titleElement),
34 | titleText = title.text(),
35 | postDescText = title.nextAll('.postDesc:eq(0), .entrylistItemPostDesc:eq(0)').text();
36 | title.after(postMetaHtml(postDescText));
37 | if (title.hasClass('postTitle') && /\[置顶\]/.test(titleText)) {
38 | title.append('置顶');
39 | title.find('a').text(titleText.replace(/\[置顶\]/, ''));
40 | }
41 | });
42 | function postMetaHtml(postDescText) {
43 | const { date, vnum, cnum, tnum } = $.__tools.handlePostDesc(postDescText);
44 | return `
45 | 发表于 ${date}
46 | 阅读:${vnum}
47 | 评论:${cnum}
48 | 推荐:${tnum}
49 | `;
50 | }
51 |
52 | // 设置摘要文章
53 | const descElements = $('.c_b_p_desc');
54 | descElements.each((i, element) => {
55 | const $obj = $(element);
56 | const $img = $obj.find('img.desc_img');
57 | if ($img.length) {
58 | const src = $img.attr('src');
59 | $img.hide();
60 | $obj.addClass('desc-width-60');
61 | $obj.parent('div').addClass('desc-parent-minheight-150');
62 | const $newDiv = $('');
63 | $newDiv.find('div').css({
64 | background: `url('${src}') center center / contain no-repeat`,
65 | });
66 | $obj.after($newDiv);
67 | }
68 | });
69 |
70 | // 主页的随机一言
71 | let hitokoto = $('#hitokoto');
72 | let configTitle = $.__config.banner.home.title;
73 | const topTitleList = ['当你凝视深渊时,深渊也在凝视着你。', '有的人25岁就死了,只是到75岁才埋葬'];
74 |
75 | const updateHitokotoDisplay = (content) => {
76 | hitokoto.html(content).css('display', '-webkit-box');
77 | $.__tools.setDomHomePosition();
78 | };
79 |
80 | function fetchAndSetTitle(url) {
81 | request(url)
82 | .then(topTitleContent)
83 | .catch(() => {
84 | const listIndex = $.__tools.randomNum(0, topTitleList.length - 1);
85 | updateHitokotoDisplay(topTitleList[listIndex]);
86 | });
87 | }
88 |
89 | function topTitleContent(r) {
90 | if (r.status === 'success') {
91 | const { note, content, data } = r;
92 | const poetry = `《${data?.origin?.title}》 - ${data?.origin?.dynasty} - ${data?.origin?.author}`;
93 | updateHitokotoDisplay(note || data.content);
94 | $('#hitokotoAuthor')
95 | .text(content || poetry)
96 | .show();
97 | }
98 | }
99 |
100 | const titleSources = {
101 | one: 'https://one.oyo.cool/',
102 | jinrishici: 'https://v2.jinrishici.com/one.json',
103 | };
104 |
105 |
106 | if (Array.isArray(configTitle)&&configTitle.length) {
107 | updateHitokotoDisplay(configTitle[$.__tools.randomNum(0, configTitle.length - 1)]);
108 | return
109 | }
110 |
111 | if (typeof configTitle === 'string') {
112 | updateHitokotoDisplay(configTitle);
113 | return
114 | }
115 |
116 | const titleSource = titleSources[$.__config.banner.home.titleSource];
117 | if (titleSource) {
118 | fetchAndSetTitle(titleSource);
119 | } else {
120 | const listIndex = $.__tools.randomNum(0, topTitleList.length - 1);
121 | updateHitokotoDisplay(topTitleList[listIndex]);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/page/links.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:27
6 | * ----------------------------------------------
7 | * @describe: 友链页处理
8 | */
9 | import comArticle from './common/com-article';
10 | import '../style/links.css';
11 | import linksTemp from '../template/links.html';
12 | import articleDirectory from '../components/articleDirectory/articleDirectory';
13 |
14 | export default function main() {
15 | // 文章页公共处理
16 | comArticle();
17 |
18 | // 添加友链
19 | if ($.__config.links.page.length) {
20 | import(/* webpackChunkName: "gf-blink" */ /* webpackPrefetch: true */ '../style/gf-blink.css');
21 |
22 | const postBody = $('#cnblogs_post_body');
23 | const articleSuffixFlg = $('.articleSuffix-flg');
24 |
25 | // 生成友链的html
26 | const generateLinkHtml = (link, index) => {
27 | const { avatar = '', name = '', introduction = '', url = '' } = link;
28 | const icons = ['icon-zhifeiji', 'icon-like-fill', 'icon-flashlight-fill'];
29 | const icon = icons[index % icons.length];
30 | return $.__tools.batchTempReplacement(linksTemp, [
31 | ['avatar', avatar],
32 | ['name', name],
33 | ['introduction', introduction],
34 | ['url', url],
35 | ['icon', icon],
36 | ]);
37 | };
38 |
39 | // 生成完整的友链分类的html
40 | const generateSectionHtml = (data) => {
41 | const { title, icon, style, links } = data;
42 | const sectionTitle = title ? `${title}
` : '';
43 | const linksHtml = links.map(generateLinkHtml).join('');
44 | return `${sectionTitle}${linksHtml}
`;
45 | };
46 |
47 | const linksHtml = $.__config.links.page.map(generateSectionHtml).join('');
48 |
49 | // 插入模版
50 | articleSuffixFlg.length ? articleSuffixFlg.before(linksHtml) : postBody.append(linksHtml);
51 | }
52 |
53 | articleDirectory();
54 | }
55 |
--------------------------------------------------------------------------------
/src/style/articleDirectory.css:
--------------------------------------------------------------------------------
1 | #articleDirectory {
2 | position: absolute;
3 | top: calc(40vh + 5px);
4 | right: 0;
5 | width: 260px;
6 | z-index: 1;
7 | max-height: 55vh;
8 | overflow: auto;
9 | box-shadow: 0 4px 11px -2px rgb(37 44 97 / 15%), 0 1px 3px 0 rgb(93 100 148 / 20%);
10 | background: rgba(255, 255, 255, .9);
11 | }
12 |
13 | #articleDirectory.articleDirectoryFixed {
14 | position: fixed;
15 | max-height: 95vh;
16 | top: 5px !important;
17 | }
18 |
19 | #articleDirectory ul {
20 | margin: 0;
21 | padding: 10px 10px 10px 5px;
22 | }
23 |
24 | #articleDirectory ul li {
25 | list-style: none;
26 | text-align: left;
27 | text-overflow: ellipsis;
28 | white-space: nowrap;
29 | margin: 0;
30 | padding: 2px 0;
31 | height: 24px;
32 | cursor: pointer;
33 | }
34 |
35 | .articleDirectory-overflow {
36 | overflow: hidden;
37 | }
38 |
39 | #articleDirectory ul li a {
40 | color: #000;
41 | padding: 0 0 0 10px;
42 | display: inline-block;
43 | width: 100%;
44 | height: 100%;
45 | position: relative;
46 | text-overflow: ellipsis;
47 | white-space: nowrap;
48 | }
49 |
50 | #articleDirectory ul li a:hover, #articleDirectory ul li a.active {
51 | background: rgba(80, 80, 80, .04);
52 | color: #807dd4;
53 | }
54 |
55 | #articleDirectory ul li a:hover::after, #articleDirectory ul li a.active:after {
56 | content: "";
57 | z-index: 1;
58 | top: 0;
59 | right: 0;
60 | width: 100%;
61 | height: 100%;
62 | position: absolute;
63 | left: 0;
64 | bottom: 0;
65 | display: inline-block;
66 | border-left: 3px solid #807dd4;
67 | }
68 |
--------------------------------------------------------------------------------
/src/style/articleSuffix.css:
--------------------------------------------------------------------------------
1 | #articleSuffix {
2 | background-image: linear-gradient(180deg,#fff,#f5f5fa);
3 | box-shadow: 0 4px 11px 0 rgb(37 44 97 / 10%), 0 1px 3px 0 rgb(93 100 148 / 13%);
4 | position: relative;
5 | overflow: hidden;
6 | cursor: pointer;
7 | margin: 0 5px;
8 | color: #3a416f;
9 | border-radius: 4px;
10 | }
11 |
12 | .essaySuffix-eof {
13 | font-weight: 700;
14 | font-size: 16px;
15 | text-align: center;
16 | color: #ddd;
17 | text-indent: 0
18 | }
19 |
20 | #articleSuffix .articleSuffix-bg {
21 | position: absolute;
22 | right: -22px;
23 | top: 0;
24 | bottom: 0;
25 | height: 240px;
26 | margin-top: -23px;
27 | width: 210px;
28 | opacity: .5;
29 | }
30 |
31 | #articleSuffix .articleSuffix-left {
32 | max-width: 140px;
33 | float: left;
34 | padding: 12px;
35 | }
36 |
37 | #articleSuffix .articleSuffix-left img {
38 | width: 128px;
39 | height: 128px;
40 | border: 1px solid #ddd;
41 | padding: 6px;
42 | margin: 0;
43 | display: block;
44 | border-radius: 4px;
45 | }
46 |
47 | #articleSuffix .articleSuffix-right {
48 | height: 160px;
49 | width: calc(100% - 170px);
50 | float: right;
51 | }
52 |
53 | #articleSuffix .articleSuffix-right item {
54 | display:inline-block;
55 | position:absolute;
56 | top: 50%;
57 | transform: translate(0, -50%);
58 | padding-top: 8px;
59 | }
60 |
61 | #articleSuffix .articleSuffix-right li {
62 | list-style: none;
63 | line-height: 1.35em;
64 | }
65 |
66 | #articleSuffix .articleSuffix-right li b {
67 | font-weight: bold;
68 | }
--------------------------------------------------------------------------------
/src/style/books.css:
--------------------------------------------------------------------------------
1 | .book-cards {
2 | display: grid;
3 | grid-template-columns: repeat(2, 49%);
4 | grid-column-gap: 20px;
5 | grid-row-gap: 15px;
6 | margin: 5px;
7 | position: relative;
8 | }
9 |
10 | @media only screen and (max-width: 960px) {
11 | .book-cards {
12 | grid-template-columns: repeat(1, 100%);
13 | grid-column-gap: 15px;
14 | grid-row-gap: 10px;
15 | }
16 | }
17 |
18 | .book-card {
19 | margin: 10px 0;
20 | background-color: #fff;
21 | box-shadow: 0 4px 11px -2px rgb(37 44 97 / 10%), 0 1px 3px 0 rgb(93 100 148 / 15%);
22 | border-radius: 4px;
23 | display: flex;
24 | flex-direction: column;
25 | cursor: pointer;
26 | padding: 0 15px 15px 15px;
27 | color: #8b939c;
28 | position: relative;
29 | }
30 |
31 | .book-cards .book-rate i {
32 | margin-right: 1px;
33 | font-size: 13px;
34 | }
35 |
36 | .book-cards .book-card-img {
37 | width: 160px;
38 | margin-top: -35px;
39 | border-radius: 2px;
40 | box-shadow: 0px 1px 7px 2px #c7c9d3;
41 | border-bottom: 1px solid #dcddde;
42 | object-fit: cover;
43 | margin-bottom: 20px;
44 | transition: 0.3s ease;
45 | }
46 | .book-cards .book-card-img:hover {
47 | transform: scale(1.04);
48 | }
49 |
50 | .card-content {
51 | color: #3d4954;
52 | padding: 20px 30px;
53 | overflow: hidden;
54 | position: relative;
55 | }
56 |
57 | .book-cards .book-name {
58 | font-weight: 500;
59 | text-overflow: ellipsis;
60 | overflow: hidden;
61 | white-space: nowrap;
62 | font-family: 'ZCOOL XiaoWei',serif;
63 | font-size: 16px;
64 | }
65 |
66 | .book-cards .book-by {
67 | font-size: 13px;
68 | color: #bbb;
69 | margin-top: 4px;
70 | position: absolute;
71 | bottom: 6px;
72 | right: 10px;
73 | }
74 |
75 | .book-cards .book-by i {
76 | font-size: 14px;
77 | margin: 0 4px;
78 | display: none;
79 | }
80 |
81 | .book-cards .book-rate > label {
82 | color: #cccccc;
83 | }
84 |
85 | .book-cards .rate {
86 | display: inline-block;
87 | white-space: nowrap;
88 | overflow: hidden;
89 | text-overflow: ellipsis;
90 | width: 100%;
91 | }
92 |
93 | .book-rate > input:checked ~ label,
94 | .book-rate:not(:checked) > label:hover,
95 | .book-rate:not(:checked) > label:hover ~ label {
96 | color: #ff9700;
97 | }
98 |
99 | .book-rate > input:checked + label:hover,
100 | .book-rate > input:checked ~ label:hover,
101 | .book-rate > label:hover ~ input:checked ~ label,
102 | .book-rate > input:checked ~ label:hover ~ label {
103 | color: #ff9700;
104 | }
105 |
106 | .book-cards .card-vote {
107 | color: #8b939c;
108 | font-size: 13px;
109 | }
110 |
111 | .book-cards .card-sum {
112 | color: #8b939c;
113 | font-size: 13px;
114 | line-height: 1.6em;
115 | -webkit-line-clamp: 4;
116 | margin-top: 15px;
117 | }
118 |
119 | .book-cards .content-wrapper {
120 | display: flex;
121 | position: relative;
122 | }
123 |
124 | .book-cards .book-rate i {
125 | color: #ff9700;
126 | font-size: 14px;
127 | font-weight: bold;
128 | }
--------------------------------------------------------------------------------
/src/style/links.css:
--------------------------------------------------------------------------------
1 | #links-box {
2 | display: grid;
3 | grid-template-columns: repeat(3,30%);
4 | grid-column-gap: 30px;
5 | grid-row-gap: 10px;
6 | margin: 5px;
7 | position: relative;
8 | }
9 |
10 | @media only screen and (max-width: 960px) {
11 | #links-box {
12 | grid-template-columns: repeat(2, 50%);
13 | }
14 | }
15 |
16 | @media only screen and (max-width: 720px) {
17 | #links-box {
18 | grid-template-columns: repeat(1, 100%);
19 | }
20 | }
21 |
22 | #links-box .links-item {
23 | width: 250px;
24 | position: relative;
25 | padding: 0 6px;
26 | max-width: 100%;
27 | height: 70px;
28 | align-items: center;
29 | display: flex;
30 | border-radius: 6px;
31 | background-color: #fff;
32 | margin: 10px 5px;
33 | color: rgba(107,114,128,1);
34 | cursor:pointer;
35 | justify-self: center;
36 | box-shadow: 0 1px 7px 2px #c7c9d3;
37 | }
38 |
39 | #links-box .links-item:hover {
40 | transform: scale(1.04);
41 | }
42 |
43 | #links-box .links-item > img {
44 | width: 60px;
45 | height: 60px;
46 | border-radius: 6px;
47 | display: block;
48 | vertical-align: middle;
49 | }
50 |
51 | #links-box .links-item .links-info {
52 | margin-left: 10px;
53 | width: 180px;
54 | }
55 |
56 | #links-box .links-item .links-info .links-info-name {
57 | overflow: hidden;
58 | white-space: nowrap;
59 | text-overflow: ellipsis;
60 | font-weight: bold;
61 | color: #777aaf;
62 | font-size: 1.5rem;
63 | position: relative;
64 | top: -2px;
65 | }
66 |
67 | #links-box .links-item .links-info .links-info-text {
68 | font-style: oblique;
69 | font-size: 14px;
70 | font-family: ZCOOL XiaoWei,serif;
71 | text-overflow: ellipsis;
72 | overflow: hidden;
73 | white-space: nowrap;
74 | width: 100%;
75 | }
76 |
77 | #links-box .links-item .links-info span {
78 | display: block;
79 | }
80 |
81 | #links-box .links-item .links-icon {
82 | width: 22px;
83 | height: 22px;
84 | position: absolute;
85 | top: 0;
86 | right: 0;
87 | margin-right: -8px;
88 | margin-top: -7px;
89 | align-items: center;
90 | display: flex;
91 | border-radius: 6px;
92 | -webkit-transform: rotate(360deg);
93 | animation: rotation 3s linear infinite;
94 | -moz-animation: rotation 3s linear infinite;
95 | -webkit-animation: rotation 3s linear infinite;
96 | -o-animation: rotation 3s linear infinite;
97 | box-shadow: 0 1px 7px 2px #c7c9d3;
98 | transition: .3s ease;
99 | }
100 |
101 | #links-box .links-item:nth-child(3n+0) .links-icon {
102 | background-color: rgba(52,211,153,1);
103 | }
104 |
105 | #links-box .links-item:nth-child(3n+1) .links-icon {
106 | background-color: rgba(167,139,250,1);
107 | }
108 |
109 | #links-box .links-item:nth-child(3n+2) .links-icon {
110 | background-color: #f87171;
111 | }
112 |
113 | #links-box .links-item .links-icon a {
114 | color: #fff;
115 | --transform-rotate: -45deg;
116 | transform: rotate(-45deg);
117 | }
118 |
119 | #links-box .links-item .links-icon a i.icon-zhifeiji {
120 | position: relative;
121 | top: 3px;
122 | left: 2px;
123 | font-size: 14px;
124 | }
125 |
126 | #links-box .links-item .links-icon a i.icon-like-fill,
127 | #links-box .links-item .links-icon a i.icon-flashlight-fill {
128 | position: relative;
129 | top: 4px;
130 | left: 3px;
131 | font-size: 14px;
132 | }
133 |
134 | @-webkit-keyframes rotation {
135 | from {-webkit-transform: rotate(0deg);}
136 | to {-webkit-transform: rotate(360deg);}
137 | }
138 |
--------------------------------------------------------------------------------
/src/style/mouse.css:
--------------------------------------------------------------------------------
1 | /*html {*/
2 | /* cursor: none;*/
3 | /*}*/
4 |
5 | .cursor {
6 | border-radius: 50%;
7 | background: #333;
8 | }
9 |
10 | .cursor-f {
11 | top: 0;
12 | left: 0;
13 | background-image: url("data:image/svg+xml,%3Csvg width='47' height='47' viewBox='0 0 47 47' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M42.4202 42.4202C38.8403 46 33.3594 46 23.5 46C13.6406 46 8.15966 46 4.57983 42.4202C1 38.8403 1 33.3594 1 23.5C1 13.6406 1 8.15966 4.57983 4.57983C8.15966 1 13.6406 1 23.5 1C33.3594 1 38.8403 1 42.4202 4.57983C46 8.15966 46 13.6406 46 23.5C46 33.3594 46 38.8403 42.4202 42.4202Z' stroke='black'/%3E%3C/svg%3E%0A");
14 | background-size: cover;
15 | opacity: 0.7;
16 | }
17 |
18 | .cursor, .cursor-f {
19 | width: var(--size);
20 | height: var(--size);
21 | position: absolute;
22 | z-index: 999;
23 | pointer-events: none;
24 | }
--------------------------------------------------------------------------------
/src/style/nhBannerAnimation.css:
--------------------------------------------------------------------------------
1 | #nhBannerAnimation {
2 | width: 100%;
3 | height: 100%;
4 | position: absolute;
5 | z-index: 1;
6 | }
7 |
8 | #nhBannerAnimation .circles {
9 | position: absolute;
10 | top: 0;
11 | left: 0;
12 | width: 100%;
13 | height: 100%;
14 | overflow: hidden;
15 | }
16 |
17 | #nhBannerAnimation .circles li {
18 | position: absolute;
19 | display: block;
20 | list-style: none;
21 | width: 20px;
22 | height: 20px;
23 | background: rgba(255, 255, 255, 0.2);
24 | animation: nhBannerAnimation 25s linear infinite;
25 | bottom: -150px;
26 |
27 | }
28 |
29 | #nhBannerAnimation .circles li:nth-child(1) {
30 | left: 25%;
31 | width: 80px;
32 | height: 80px;
33 | animation-delay: 0s;
34 | }
35 |
36 |
37 | #nhBannerAnimation .circles li:nth-child(2) {
38 | left: 10%;
39 | width: 20px;
40 | height: 20px;
41 | animation-delay: 2s;
42 | animation-duration: 12s;
43 | }
44 |
45 | #nhBannerAnimation .circles li:nth-child(3) {
46 | left: 70%;
47 | width: 20px;
48 | height: 20px;
49 | animation-delay: 4s;
50 | }
51 |
52 | #nhBannerAnimation .circles li:nth-child(4) {
53 | left: 40%;
54 | width: 60px;
55 | height: 60px;
56 | animation-delay: 0s;
57 | animation-duration: 18s;
58 | }
59 |
60 | #nhBannerAnimation .circles li:nth-child(5) {
61 | left: 65%;
62 | width: 20px;
63 | height: 20px;
64 | animation-delay: 0s;
65 | }
66 |
67 | #nhBannerAnimation .circles li:nth-child(6) {
68 | left: 75%;
69 | width: 110px;
70 | height: 110px;
71 | animation-delay: 3s;
72 | }
73 |
74 | #nhBannerAnimation .circles li:nth-child(7) {
75 | left: 35%;
76 | width: 150px;
77 | height: 150px;
78 | animation-delay: 7s;
79 | }
80 |
81 | #nhBannerAnimation .circles li:nth-child(8) {
82 | left: 50%;
83 | width: 25px;
84 | height: 25px;
85 | animation-delay: 15s;
86 | animation-duration: 45s;
87 | }
88 |
89 | #nhBannerAnimation .circles li:nth-child(9) {
90 | left: 20%;
91 | width: 15px;
92 | height: 15px;
93 | animation-delay: 2s;
94 | animation-duration: 35s;
95 | }
96 |
97 | #nhBannerAnimation .circles li:nth-child(10) {
98 | left: 85%;
99 | width: 150px;
100 | height: 150px;
101 | animation-delay: 0s;
102 | animation-duration: 11s;
103 | }
104 |
105 |
106 | @keyframes nhBannerAnimation {
107 |
108 | 0% {
109 | transform: translateY(0) rotate(0deg);
110 | opacity: 1;
111 | border-radius: 0;
112 | }
113 |
114 | 100% {
115 | transform: translateY(-1000px) rotate(720deg);
116 | opacity: 0;
117 | border-radius: 50%;
118 | }
119 |
120 | }
--------------------------------------------------------------------------------
/src/style/particles.css:
--------------------------------------------------------------------------------
1 | #particles {
2 | position: fixed;
3 | left: 0;
4 | top: 0;
5 | width: 100vw;
6 | height: 100vh;
7 | overflow: hidden;
8 | opacity: .6;
9 | z-index: -2;
10 | pointer-events: none
11 | }
12 |
13 | #particles .particles-layer {
14 | position: absolute;
15 | top: -5%;
16 | left: -5%;
17 | width: 110%;
18 | height: 110%;
19 | background-repeat: repeat;
20 | background-position: center center;
21 | opacity: 0;
22 | display: block
23 | }
24 |
25 | #particles .particles-layer--1 {
26 | background-image: url('https://cdn.jsdelivr.net/gh/wangyang0210/pic/imgs/project/cnblogs/bg-pattern-1.svg')
27 | }
28 |
29 | #particles .particles-layer--2 {
30 | background-image: url('https://cdn.jsdelivr.net/gh/wangyang0210/pic/imgs/project/cnblogs/bg-pattern-2.svg')
31 | }
32 |
33 | #particles .particles-layer--3 {
34 | background-image: url('https://cdn.jsdelivr.net/gh/wangyang0210/pic/imgs/project/cnblogs/bg-pattern-3.svg')
35 | }
36 |
37 | @media (max-width: 767px) {
38 | #particles {
39 | display: none;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/template/articleDirectory.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/template/articleSuffix.html:
--------------------------------------------------------------------------------
1 |
2 |
__EOF__
3 |
4 |
9 |
10 |

11 |
12 |
13 |
-
14 |
15 | ##origin##文作者:
16 | ##author##
17 |
18 |
19 | ##origin##文链接:
20 | ##source##
21 |
22 |
23 | 关于博主:
24 | ##aboutHtml##
25 |
26 |
27 | 版权声明:
28 | ##copyrightHtml##
29 |
30 |
31 | 声援博主:
32 | ##supportHtml##
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/template/banner.html:
--------------------------------------------------------------------------------
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 | Scroll Down
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/template/books.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
##name##
6 |
7 |
10 |
11 | ##infoHtml##
12 |
13 |
14 |
15 |
16 |
17 | ##readDate##
18 | ##readPercentage##
19 |
20 |
--------------------------------------------------------------------------------
/src/template/dayNight.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/template/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
35 |
36 |
--------------------------------------------------------------------------------
/src/template/links.html:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 | ##name##
5 | ##introduction##
6 |
7 |
10 |
--------------------------------------------------------------------------------
/src/template/particles.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/template/rtMenu.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/template/sidebarNav.html:
--------------------------------------------------------------------------------
1 |
6 |
11 |
12 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/wangyang0210
3 | * https://www.cnblogs.com/wangyang0210/
4 | * @author: WangYang, i@oyo.cool
5 | * @Date 2022-08-25 15:28
6 | * ----------------------------------------------
7 | * @describe: fetch封装
8 | */
9 |
10 | export async function request(url = '', method = 'GET', data = {}, headers = {}) {
11 | let options = {
12 | method: method,
13 | mode: 'cors',
14 | redirect: 'follow',
15 | referrerPolicy: 'no-referrer',
16 | };
17 | if (Object.keys(headers).length) {
18 | options.headers = headers;
19 | }
20 | if (Object.keys(data).length) {
21 | options.body = JSON.stringify(data);
22 | }
23 | const response = await fetch(url, options);
24 | return response.json();
25 | }
26 |
--------------------------------------------------------------------------------
/src/vendor/circleMagic/circleMagic.js:
--------------------------------------------------------------------------------
1 | ;(function ($) {
2 | $.fn.circleMagic = function (options) {
3 |
4 | let width, height, canvas, ctx, animateHeader = true;
5 | let circles = [];
6 |
7 | let settings = $.extend({
8 | color: 'rgba(255,255,255,.5)',
9 | radius: 10,
10 | density: 0.3,
11 | clearOffset: 0.2
12 | }, options);
13 |
14 | let container = this['0'];
15 | initContainer();
16 | addListeners();
17 |
18 | function initContainer() {
19 | width = container.offsetWidth;
20 | height = container.offsetHeight;
21 |
22 | initCanvas();
23 | canvas = document.getElementById('homeTopCanvas');
24 | canvas.width = width;
25 | canvas.height = height;
26 | canvas.style.position = 'absolute';
27 | canvas.style.left = '0';
28 | canvas.style.bottom = '0';
29 | canvas.style.zIndex = '1';
30 | ctx = canvas.getContext('2d');
31 |
32 | for (let x = 0; x < width * settings.density; x++) {
33 | let c = new Circle();
34 | circles.push(c);
35 | }
36 | animate();
37 | }
38 |
39 | function initCanvas() {
40 | let canvasElement = document.createElement('canvas');
41 | canvasElement.id = 'homeTopCanvas';
42 | container.appendChild(canvasElement);
43 | canvasElement.parentElement.style.overflow = 'hidden';
44 |
45 | }
46 |
47 | function addListeners() {
48 | window.addEventListener('scroll', scrollCheck, false);
49 | window.addEventListener('resize', resize, false);
50 | }
51 |
52 | function scrollCheck() {
53 | document.body.scrollTop > height ? animateHeader = false : animateHeader = true;
54 | }
55 |
56 | function resize() {
57 | width = container.clientWidth;
58 | height = container.clientHeight;
59 | container.height = height + 'px';
60 | canvas.width = width;
61 | canvas.height = height;
62 | }
63 |
64 | function animate() {
65 | if (animateHeader) {
66 | ctx.clearRect(0, 0, width, height);
67 | for (let i in circles) {
68 | circles[i].draw();
69 | }
70 | }
71 | requestAnimationFrame(animate);
72 | }
73 |
74 | function randomColor() {
75 | let r = Math.floor(Math.random() * 255);
76 | let g = Math.floor(Math.random() * 255);
77 | let b = Math.floor(Math.random() * 255);
78 | let alpha = Math.random().toPrecision(2);
79 | return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
80 | }
81 |
82 | function Circle() {
83 | let that = this;
84 | (function () {
85 | that.pos = {};
86 | init();
87 | })();
88 |
89 | function init() {
90 | that.pos.x = Math.random() * width;
91 | that.pos.y = height + Math.random() * 100;
92 | that.alpha = 0.1 + Math.random() * settings.clearOffset;
93 | that.scale = 0.1 + Math.random() * 0.3;
94 | that.speed = Math.random();
95 | settings.color === 'random' ? that.color = randomColor() : that.color = settings.color;
96 | }
97 |
98 | this.draw = function () {
99 | if (that.alpha <= 0) init();
100 | that.pos.y -= that.speed;
101 | that.alpha -= 0.0005;
102 | ctx.beginPath();
103 | ctx.arc(that.pos.x, that.pos.y, that.scale * settings.radius, 0, 2 * Math.PI, false);
104 | ctx.fillStyle = that.color;
105 | ctx.fill();
106 | ctx.closePath();
107 | };
108 | }
109 | }
110 | })(jQuery);
111 |
--------------------------------------------------------------------------------
/src/vendor/consoleText/consoleText.js:
--------------------------------------------------------------------------------
1 | /**
2 | * UPDATES AND DOCS AT: https://github.com/BNDong
3 | * https://www.cnblogs.com/bndong/
4 | * @author: BNDong, dbnuo@foxmail.com
5 | * @param words [] 循环文字数组
6 | * @param id string 文字domId
7 | * @param conId string 符合domId
8 | * @param colors [] 颜色
9 | * @param isCycle boolean 是否循环
10 | * @param callback fun 每个文字设置后回调
11 | */
12 | export default function main(words, id, containerId, isCycle) {
13 | let textIndex = 0;
14 | let visible = true;
15 | let containerElement = document.getElementById(containerId);
16 | let targetElement = document.getElementById(id);
17 | containerElement.innerHTML = '_';
18 |
19 | const deleteText = () => {
20 | targetElement.innerHTML = targetElement.innerHTML.slice(0, -1)
21 | if (targetElement.innerHTML.length > 0) {
22 | setTimeout(deleteText, 200)
23 | } else {
24 | textIndex = 0
25 | setTimeout(typeWriter, 200)
26 | }
27 | }
28 | const typeWriter = () => {
29 | targetElement.innerHTML += words[textIndex++]
30 | if (textIndex < words.length) {
31 | setTimeout(typeWriter, 200)
32 | } else if (isCycle) {
33 | setTimeout(() => { deleteText() }, 1000)
34 | }
35 | }
36 |
37 | window.setInterval(() => {
38 | if (visible) {
39 | containerElement.style.visibility = 'hidden';
40 | visible = false;
41 | } else {
42 | containerElement.style.visibility = 'visible';
43 | visible = true;
44 | }
45 | }, 400);
46 | setTimeout(typeWriter, 200);
47 | }
48 |
--------------------------------------------------------------------------------
${commentInfo}
71 |