@riverflowpkg/riverflow 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bar/bar.js +90 -90
- package/bar/theCodes.html +144 -144
- package/editor/editor.js +1 -1
- package/editor/highlight.min.js +1 -1
- package/editor/terminal.js +309 -309
- package/effects/sunshine.js +160 -0
- package/effects/typing.js +105 -105
- package/effects/underwater.js +117 -117
- package/package.json +1 -1
- package/scroll/scroll.css +91 -91
- package/scroll/scroll.js +244 -244
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
function injectStyles() {
|
|
3
|
+
if (document.getElementById('sunshine-styles')) return;
|
|
4
|
+
const s = document.createElement('style');
|
|
5
|
+
s.id = 'sunshine-styles';
|
|
6
|
+
s.textContent = `
|
|
7
|
+
#effect-sunshine {
|
|
8
|
+
position: fixed;
|
|
9
|
+
inset: 0;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
z-index: 9998;
|
|
12
|
+
mix-blend-mode: screen;
|
|
13
|
+
}
|
|
14
|
+
#effect-sunshine-glow {
|
|
15
|
+
position: absolute;
|
|
16
|
+
width: 140vmax;
|
|
17
|
+
height: 140vmax;
|
|
18
|
+
border-radius: 50%;
|
|
19
|
+
will-change: opacity, transform;
|
|
20
|
+
filter: blur(var(--sunshine-blur, 90px));
|
|
21
|
+
}
|
|
22
|
+
#effect-sunshine-rays {
|
|
23
|
+
position: fixed;
|
|
24
|
+
inset: 0;
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
z-index: 9997;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
mix-blend-mode: screen;
|
|
29
|
+
filter: blur(var(--sunshine-blur, 90px));
|
|
30
|
+
}
|
|
31
|
+
#effect-sunshine-rays canvas {
|
|
32
|
+
position: absolute;
|
|
33
|
+
inset: 0;
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 100%;
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
document.head.appendChild(s);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const CORNERS = {
|
|
42
|
+
'top-left': { top: '0%', left: '0%', right: 'auto', bottom: 'auto', ox: 0, oy: 0 },
|
|
43
|
+
'top-right': { top: '0%', right: '0%', left: 'auto', bottom: 'auto', ox: 1, oy: 0 },
|
|
44
|
+
'bottom-left': { bottom: '0%', left: '0%', top: 'auto', right: 'auto', ox: 0, oy: 1 },
|
|
45
|
+
'bottom-right': { bottom: '0%', right: '0%', top: 'auto', left: 'auto', ox: 1, oy: 1 },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function init() {
|
|
49
|
+
const host = document.querySelector('.effect-sunshine');
|
|
50
|
+
if (!host) return;
|
|
51
|
+
injectStyles();
|
|
52
|
+
|
|
53
|
+
const cornerKey = (host.dataset.corner || 'top-right').toLowerCase();
|
|
54
|
+
const corner = CORNERS[cornerKey] || CORNERS['top-right'];
|
|
55
|
+
const color = host.dataset.color || '255,235,180';
|
|
56
|
+
const minOpacity = parseFloat(host.dataset.min) || 0.15;
|
|
57
|
+
const maxOpacity = parseFloat(host.dataset.max) || 0.85;
|
|
58
|
+
const period = parseFloat(host.dataset.period) || 900;
|
|
59
|
+
const rays = host.dataset.rays !== 'false';
|
|
60
|
+
const blur = host.dataset.blur || '90px';
|
|
61
|
+
|
|
62
|
+
const wrap = document.createElement('div');
|
|
63
|
+
wrap.id = 'effect-sunshine';
|
|
64
|
+
wrap.style.setProperty('--sunshine-blur', blur);
|
|
65
|
+
const glow = document.createElement('div');
|
|
66
|
+
glow.id = 'effect-sunshine-glow';
|
|
67
|
+
glow.style.top = corner.top;
|
|
68
|
+
glow.style.left = corner.left;
|
|
69
|
+
glow.style.right = corner.right;
|
|
70
|
+
glow.style.bottom = corner.bottom;
|
|
71
|
+
glow.style.transform = 'translate(' + (corner.ox ? '30%' : '-30%') + ',' + (corner.oy ? '30%' : '-30%') + ')';
|
|
72
|
+
glow.style.background =
|
|
73
|
+
'radial-gradient(circle, rgba(' + color + ',0.65) 0%, ' +
|
|
74
|
+
'rgba(' + color + ',0.4) 18%, ' +
|
|
75
|
+
'rgba(' + color + ',0.2) 35%, ' +
|
|
76
|
+
'rgba(' + color + ',0.07) 55%, ' +
|
|
77
|
+
'rgba(' + color + ',0) 75%)';
|
|
78
|
+
wrap.appendChild(glow);
|
|
79
|
+
document.body.appendChild(wrap);
|
|
80
|
+
|
|
81
|
+
let ctx, canvas, W, H;
|
|
82
|
+
if (rays) {
|
|
83
|
+
const rayWrap = document.createElement('div');
|
|
84
|
+
rayWrap.id = 'effect-sunshine-rays';
|
|
85
|
+
rayWrap.style.setProperty('--sunshine-blur', blur);
|
|
86
|
+
canvas = document.createElement('canvas');
|
|
87
|
+
rayWrap.appendChild(canvas);
|
|
88
|
+
document.body.appendChild(rayWrap);
|
|
89
|
+
ctx = canvas.getContext('2d');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function resize() {
|
|
93
|
+
if (!canvas) return;
|
|
94
|
+
W = canvas.width = window.innerWidth;
|
|
95
|
+
H = canvas.height = window.innerHeight;
|
|
96
|
+
}
|
|
97
|
+
resize();
|
|
98
|
+
window.addEventListener('resize', resize);
|
|
99
|
+
|
|
100
|
+
let scrollY = window.scrollY;
|
|
101
|
+
window.addEventListener('scroll', () => { scrollY = window.scrollY; }, { passive: true });
|
|
102
|
+
|
|
103
|
+
let current = minOpacity;
|
|
104
|
+
let rayAngle = 0;
|
|
105
|
+
|
|
106
|
+
function targetOpacity() {
|
|
107
|
+
const wave = (Math.sin((scrollY / period) * Math.PI * 2) + 1) / 2;
|
|
108
|
+
return minOpacity + wave * (maxOpacity - minOpacity);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function drawRays(opacity) {
|
|
112
|
+
ctx.clearRect(0, 0, W, H);
|
|
113
|
+
const ox = corner.ox * W;
|
|
114
|
+
const oy = corner.oy * H;
|
|
115
|
+
const count = 7;
|
|
116
|
+
const len = Math.max(W, H) * 1.4;
|
|
117
|
+
rayAngle += 0.0006;
|
|
118
|
+
for (let i = 0; i < count; i++) {
|
|
119
|
+
const spread = 0.55;
|
|
120
|
+
const base = Math.atan2(H * 0.5 - oy, W * 0.5 - ox);
|
|
121
|
+
const a = base - spread + (spread * 2) * (i / (count - 1)) + Math.sin(rayAngle + i) * 0.03;
|
|
122
|
+
const width = 0.05 + 0.02 * Math.sin(rayAngle * 3 + i * 1.7);
|
|
123
|
+
const grad = ctx.createLinearGradient(ox, oy, ox + Math.cos(a) * len, oy + Math.sin(a) * len);
|
|
124
|
+
grad.addColorStop(0, 'rgba(' + color + ',' + (0.16 * opacity) + ')');
|
|
125
|
+
grad.addColorStop(0.4, 'rgba(' + color + ',' + (0.06 * opacity) + ')');
|
|
126
|
+
grad.addColorStop(1, 'rgba(' + color + ',0)');
|
|
127
|
+
ctx.save();
|
|
128
|
+
ctx.translate(ox, oy);
|
|
129
|
+
ctx.rotate(a);
|
|
130
|
+
ctx.fillStyle = grad;
|
|
131
|
+
ctx.beginPath();
|
|
132
|
+
ctx.moveTo(0, -width * len * 0.5);
|
|
133
|
+
ctx.lineTo(len, -width * len * 1.4);
|
|
134
|
+
ctx.lineTo(len, width * len * 1.4);
|
|
135
|
+
ctx.lineTo(0, width * len * 0.5);
|
|
136
|
+
ctx.closePath();
|
|
137
|
+
ctx.fill();
|
|
138
|
+
ctx.restore();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function frame() {
|
|
143
|
+
const target = targetOpacity();
|
|
144
|
+
current += (target - current) * 0.06;
|
|
145
|
+
|
|
146
|
+
glow.style.opacity = current.toFixed(3);
|
|
147
|
+
const scale = 0.85 + (current - minOpacity) / (maxOpacity - minOpacity) * 0.3;
|
|
148
|
+
glow.style.transform =
|
|
149
|
+
'translate(' + (corner.ox ? '30%' : '-30%') + ',' + (corner.oy ? '30%' : '-30%') + ') scale(' + scale.toFixed(3) + ')';
|
|
150
|
+
|
|
151
|
+
if (ctx) drawRays(current);
|
|
152
|
+
|
|
153
|
+
requestAnimationFrame(frame);
|
|
154
|
+
}
|
|
155
|
+
requestAnimationFrame(frame);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
159
|
+
else init();
|
|
160
|
+
})();
|
package/effects/typing.js
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
(function () {
|
|
2
|
-
|
|
3
|
-
function injectStyles() {
|
|
4
|
-
if (document.getElementById('typing-styles')) return;
|
|
5
|
-
const s = document.createElement('style');
|
|
6
|
-
s.id = 'typing-styles';
|
|
7
|
-
s.textContent = `
|
|
8
|
-
/* cursor */
|
|
9
|
-
.typing-cursor {
|
|
10
|
-
display: inline-block;
|
|
11
|
-
width: 2px;
|
|
12
|
-
height: 1em;
|
|
13
|
-
background: currentColor;
|
|
14
|
-
border-radius: 1px;
|
|
15
|
-
margin-left: 2px;
|
|
16
|
-
vertical-align: text-bottom;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/* default cursor — hard blink */
|
|
20
|
-
.typing .typing-cursor {
|
|
21
|
-
animation: cur-blink 1s steps(1) infinite;
|
|
22
|
-
}
|
|
23
|
-
@keyframes cur-blink {
|
|
24
|
-
0%, 49% { opacity: 1; }
|
|
25
|
-
50%, 100% { opacity: 0; }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/* smooth cursor — fade blink */
|
|
29
|
-
.typing-smooth .typing-cursor {
|
|
30
|
-
animation: cur-fade 1s ease-in-out infinite;
|
|
31
|
-
}
|
|
32
|
-
@keyframes cur-fade {
|
|
33
|
-
0%, 100% { opacity: 1; }
|
|
34
|
-
50% { opacity: 0; }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/* smooth char fade-in */
|
|
38
|
-
.typing-smooth .typing-char {
|
|
39
|
-
display: inline-block;
|
|
40
|
-
animation: char-fade 0.12s ease both;
|
|
41
|
-
}
|
|
42
|
-
@keyframes char-fade {
|
|
43
|
-
from { opacity: 0; transform: translateY(3px); }
|
|
44
|
-
to { opacity: 1; transform: translateY(0); }
|
|
45
|
-
}
|
|
46
|
-
`;
|
|
47
|
-
document.head.appendChild(s);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function initTyping(el) {
|
|
51
|
-
if (el.dataset.typingInit) return;
|
|
52
|
-
el.dataset.typingInit = '1';
|
|
53
|
-
|
|
54
|
-
const fullText = el.textContent.trim();
|
|
55
|
-
const isSmooth = el.classList.contains('typing-smooth');
|
|
56
|
-
const speed = parseInt(el.dataset.speed) || 50; /* ms per character */
|
|
57
|
-
const delay = parseInt(el.dataset.delay) || 0; /* ms before starting */
|
|
58
|
-
|
|
59
|
-
el.textContent = '';
|
|
60
|
-
|
|
61
|
-
/* cursor element */
|
|
62
|
-
const cursor = document.createElement('span');
|
|
63
|
-
cursor.className = 'typing-cursor';
|
|
64
|
-
el.appendChild(cursor);
|
|
65
|
-
|
|
66
|
-
let i = 0;
|
|
67
|
-
|
|
68
|
-
function typeNext() {
|
|
69
|
-
if (i >= fullText.length) return; /* done — cursor stays */
|
|
70
|
-
|
|
71
|
-
const ch = fullText[i];
|
|
72
|
-
i++;
|
|
73
|
-
|
|
74
|
-
if (isSmooth) {
|
|
75
|
-
const span = document.createElement('span');
|
|
76
|
-
span.className = 'typing-char';
|
|
77
|
-
span.textContent = ch;
|
|
78
|
-
el.insertBefore(span, cursor);
|
|
79
|
-
} else {
|
|
80
|
-
const text = document.createTextNode(ch);
|
|
81
|
-
el.insertBefore(text, cursor);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
setTimeout(typeNext, speed);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
setTimeout(typeNext, delay);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function init() {
|
|
91
|
-
injectStyles();
|
|
92
|
-
document.querySelectorAll('.typing').forEach(initTyping);
|
|
93
|
-
new MutationObserver(muts => muts.forEach(m =>
|
|
94
|
-
m.addedNodes.forEach(n => {
|
|
95
|
-
if (n.nodeType !== 1) return;
|
|
96
|
-
if (n.classList.contains('typing')) initTyping(n);
|
|
97
|
-
n.querySelectorAll?.('.typing').forEach(initTyping);
|
|
98
|
-
})
|
|
99
|
-
)).observe(document.body, { childList: true, subtree: true });
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
103
|
-
else init();
|
|
104
|
-
|
|
105
|
-
})();
|
|
1
|
+
(function () {
|
|
2
|
+
|
|
3
|
+
function injectStyles() {
|
|
4
|
+
if (document.getElementById('typing-styles')) return;
|
|
5
|
+
const s = document.createElement('style');
|
|
6
|
+
s.id = 'typing-styles';
|
|
7
|
+
s.textContent = `
|
|
8
|
+
/* cursor */
|
|
9
|
+
.typing-cursor {
|
|
10
|
+
display: inline-block;
|
|
11
|
+
width: 2px;
|
|
12
|
+
height: 1em;
|
|
13
|
+
background: currentColor;
|
|
14
|
+
border-radius: 1px;
|
|
15
|
+
margin-left: 2px;
|
|
16
|
+
vertical-align: text-bottom;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* default cursor — hard blink */
|
|
20
|
+
.typing .typing-cursor {
|
|
21
|
+
animation: cur-blink 1s steps(1) infinite;
|
|
22
|
+
}
|
|
23
|
+
@keyframes cur-blink {
|
|
24
|
+
0%, 49% { opacity: 1; }
|
|
25
|
+
50%, 100% { opacity: 0; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* smooth cursor — fade blink */
|
|
29
|
+
.typing-smooth .typing-cursor {
|
|
30
|
+
animation: cur-fade 1s ease-in-out infinite;
|
|
31
|
+
}
|
|
32
|
+
@keyframes cur-fade {
|
|
33
|
+
0%, 100% { opacity: 1; }
|
|
34
|
+
50% { opacity: 0; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* smooth char fade-in */
|
|
38
|
+
.typing-smooth .typing-char {
|
|
39
|
+
display: inline-block;
|
|
40
|
+
animation: char-fade 0.12s ease both;
|
|
41
|
+
}
|
|
42
|
+
@keyframes char-fade {
|
|
43
|
+
from { opacity: 0; transform: translateY(3px); }
|
|
44
|
+
to { opacity: 1; transform: translateY(0); }
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
document.head.appendChild(s);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function initTyping(el) {
|
|
51
|
+
if (el.dataset.typingInit) return;
|
|
52
|
+
el.dataset.typingInit = '1';
|
|
53
|
+
|
|
54
|
+
const fullText = el.textContent.trim();
|
|
55
|
+
const isSmooth = el.classList.contains('typing-smooth');
|
|
56
|
+
const speed = parseInt(el.dataset.speed) || 50; /* ms per character */
|
|
57
|
+
const delay = parseInt(el.dataset.delay) || 0; /* ms before starting */
|
|
58
|
+
|
|
59
|
+
el.textContent = '';
|
|
60
|
+
|
|
61
|
+
/* cursor element */
|
|
62
|
+
const cursor = document.createElement('span');
|
|
63
|
+
cursor.className = 'typing-cursor';
|
|
64
|
+
el.appendChild(cursor);
|
|
65
|
+
|
|
66
|
+
let i = 0;
|
|
67
|
+
|
|
68
|
+
function typeNext() {
|
|
69
|
+
if (i >= fullText.length) return; /* done — cursor stays */
|
|
70
|
+
|
|
71
|
+
const ch = fullText[i];
|
|
72
|
+
i++;
|
|
73
|
+
|
|
74
|
+
if (isSmooth) {
|
|
75
|
+
const span = document.createElement('span');
|
|
76
|
+
span.className = 'typing-char';
|
|
77
|
+
span.textContent = ch;
|
|
78
|
+
el.insertBefore(span, cursor);
|
|
79
|
+
} else {
|
|
80
|
+
const text = document.createTextNode(ch);
|
|
81
|
+
el.insertBefore(text, cursor);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setTimeout(typeNext, speed);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setTimeout(typeNext, delay);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function init() {
|
|
91
|
+
injectStyles();
|
|
92
|
+
document.querySelectorAll('.typing').forEach(initTyping);
|
|
93
|
+
new MutationObserver(muts => muts.forEach(m =>
|
|
94
|
+
m.addedNodes.forEach(n => {
|
|
95
|
+
if (n.nodeType !== 1) return;
|
|
96
|
+
if (n.classList.contains('typing')) initTyping(n);
|
|
97
|
+
n.querySelectorAll?.('.typing').forEach(initTyping);
|
|
98
|
+
})
|
|
99
|
+
)).observe(document.body, { childList: true, subtree: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
103
|
+
else init();
|
|
104
|
+
|
|
105
|
+
})();
|
package/effects/underwater.js
CHANGED
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
(function () {
|
|
2
|
-
function injectStyles() {
|
|
3
|
-
if (document.getElementById('underwater-styles')) return;
|
|
4
|
-
const s = document.createElement('style');
|
|
5
|
-
s.id = 'underwater-styles';
|
|
6
|
-
s.textContent = `
|
|
7
|
-
#effect-underwater {
|
|
8
|
-
position: fixed;
|
|
9
|
-
inset: 0;
|
|
10
|
-
pointer-events: none;
|
|
11
|
-
z-index: 9998;
|
|
12
|
-
background: linear-gradient(180deg,
|
|
13
|
-
rgba(0,60,120,0.38) 0%,
|
|
14
|
-
rgba(0,100,160,0.22) 50%,
|
|
15
|
-
rgba(0,40,80,0.42) 100%);
|
|
16
|
-
}
|
|
17
|
-
#effect-underwater-caustics {
|
|
18
|
-
position: fixed;
|
|
19
|
-
inset: 0;
|
|
20
|
-
pointer-events: none;
|
|
21
|
-
z-index: 9997;
|
|
22
|
-
overflow: hidden;
|
|
23
|
-
}
|
|
24
|
-
#effect-underwater-caustics canvas {
|
|
25
|
-
position: absolute;
|
|
26
|
-
inset: 0;
|
|
27
|
-
width: 100%;
|
|
28
|
-
height: 100%;
|
|
29
|
-
opacity: 0.13;
|
|
30
|
-
}
|
|
31
|
-
#effect-underwater-blur {
|
|
32
|
-
position: fixed;
|
|
33
|
-
inset: 0;
|
|
34
|
-
pointer-events: none;
|
|
35
|
-
z-index: 9999;
|
|
36
|
-
backdrop-filter: blur(0.6px) saturate(1.3);
|
|
37
|
-
-webkit-backdrop-filter: blur(0.6px) saturate(1.3);
|
|
38
|
-
}
|
|
39
|
-
`;
|
|
40
|
-
document.head.appendChild(s);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function init() {
|
|
44
|
-
if (!document.querySelector('.effect-underwater')) return;
|
|
45
|
-
injectStyles();
|
|
46
|
-
|
|
47
|
-
const overlay = document.createElement('div');
|
|
48
|
-
overlay.id = 'effect-underwater';
|
|
49
|
-
document.body.appendChild(overlay);
|
|
50
|
-
|
|
51
|
-
const blurLayer = document.createElement('div');
|
|
52
|
-
blurLayer.id = 'effect-underwater-blur';
|
|
53
|
-
document.body.appendChild(blurLayer);
|
|
54
|
-
|
|
55
|
-
const causticWrap = document.createElement('div');
|
|
56
|
-
causticWrap.id = 'effect-underwater-caustics';
|
|
57
|
-
const canvas = document.createElement('canvas');
|
|
58
|
-
causticWrap.appendChild(canvas);
|
|
59
|
-
document.body.appendChild(causticWrap);
|
|
60
|
-
|
|
61
|
-
const ctx = canvas.getContext('2d');
|
|
62
|
-
let W, H;
|
|
63
|
-
function resize() { W = canvas.width = window.innerWidth; H = canvas.height = window.innerHeight; }
|
|
64
|
-
resize();
|
|
65
|
-
window.addEventListener('resize', resize);
|
|
66
|
-
|
|
67
|
-
const nodes = Array.from({ length: 10 }, () => ({
|
|
68
|
-
x: Math.random() * 1.2 - 0.1, y: Math.random() * 1.2 - 0.1,
|
|
69
|
-
vx: (Math.random() - 0.5) * 0.0006, vy: (Math.random() - 0.5) * 0.0006,
|
|
70
|
-
r: 0.12 + Math.random() * 0.2,
|
|
71
|
-
}));
|
|
72
|
-
|
|
73
|
-
let scrollOffset = 0;
|
|
74
|
-
window.addEventListener('scroll', () => { scrollOffset = window.scrollY; }, { passive: true });
|
|
75
|
-
|
|
76
|
-
function draw() {
|
|
77
|
-
ctx.clearRect(0, 0, W, H);
|
|
78
|
-
const shift = scrollOffset * 0.3;
|
|
79
|
-
nodes.forEach(n => {
|
|
80
|
-
n.x += n.vx; n.y += n.vy;
|
|
81
|
-
if (n.x < -0.2 || n.x > 1.2) n.vx *= -1;
|
|
82
|
-
if (n.y < -0.2 || n.y > 1.2) n.vy *= -1;
|
|
83
|
-
const cx = n.x * W, cy = n.y * H - shift;
|
|
84
|
-
const rx = n.r * W, ry = n.r * H * 0.5;
|
|
85
|
-
const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(rx, ry));
|
|
86
|
-
grad.addColorStop(0, 'rgba(100,200,255,0.55)');
|
|
87
|
-
grad.addColorStop(0.4, 'rgba(60,160,220,0.2)');
|
|
88
|
-
grad.addColorStop(1, 'rgba(0,80,160,0)');
|
|
89
|
-
ctx.save();
|
|
90
|
-
ctx.scale(1, ry / rx);
|
|
91
|
-
ctx.beginPath();
|
|
92
|
-
ctx.arc(cx, cy * (rx / ry), rx, 0, Math.PI * 2);
|
|
93
|
-
ctx.fillStyle = grad;
|
|
94
|
-
ctx.fill();
|
|
95
|
-
ctx.restore();
|
|
96
|
-
});
|
|
97
|
-
requestAnimationFrame(draw);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/* SVG wave distortion */
|
|
101
|
-
const svgFilter = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
102
|
-
svgFilter.style.cssText = 'position:fixed;width:0;height:0;';
|
|
103
|
-
svgFilter.innerHTML = `<defs><filter id="underwater-wave">
|
|
104
|
-
<feTurbulence type="fractalNoise" baseFrequency="0.012 0.018" numOctaves="3" seed="5" result="noise">
|
|
105
|
-
<animate attributeName="baseFrequency" values="0.012 0.018;0.014 0.020;0.012 0.018" dur="8s" repeatCount="indefinite"/>
|
|
106
|
-
</feTurbulence>
|
|
107
|
-
<feDisplacementMap in="SourceGraphic" in2="noise" scale="6" xChannelSelector="R" yChannelSelector="G"/>
|
|
108
|
-
</filter></defs>`;
|
|
109
|
-
document.body.appendChild(svgFilter);
|
|
110
|
-
overlay.style.filter = 'url(#underwater-wave)';
|
|
111
|
-
|
|
112
|
-
requestAnimationFrame(draw);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
116
|
-
else init();
|
|
117
|
-
})();
|
|
1
|
+
(function () {
|
|
2
|
+
function injectStyles() {
|
|
3
|
+
if (document.getElementById('underwater-styles')) return;
|
|
4
|
+
const s = document.createElement('style');
|
|
5
|
+
s.id = 'underwater-styles';
|
|
6
|
+
s.textContent = `
|
|
7
|
+
#effect-underwater {
|
|
8
|
+
position: fixed;
|
|
9
|
+
inset: 0;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
z-index: 9998;
|
|
12
|
+
background: linear-gradient(180deg,
|
|
13
|
+
rgba(0,60,120,0.38) 0%,
|
|
14
|
+
rgba(0,100,160,0.22) 50%,
|
|
15
|
+
rgba(0,40,80,0.42) 100%);
|
|
16
|
+
}
|
|
17
|
+
#effect-underwater-caustics {
|
|
18
|
+
position: fixed;
|
|
19
|
+
inset: 0;
|
|
20
|
+
pointer-events: none;
|
|
21
|
+
z-index: 9997;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
}
|
|
24
|
+
#effect-underwater-caustics canvas {
|
|
25
|
+
position: absolute;
|
|
26
|
+
inset: 0;
|
|
27
|
+
width: 100%;
|
|
28
|
+
height: 100%;
|
|
29
|
+
opacity: 0.13;
|
|
30
|
+
}
|
|
31
|
+
#effect-underwater-blur {
|
|
32
|
+
position: fixed;
|
|
33
|
+
inset: 0;
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
z-index: 9999;
|
|
36
|
+
backdrop-filter: blur(0.6px) saturate(1.3);
|
|
37
|
+
-webkit-backdrop-filter: blur(0.6px) saturate(1.3);
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
document.head.appendChild(s);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function init() {
|
|
44
|
+
if (!document.querySelector('.effect-underwater')) return;
|
|
45
|
+
injectStyles();
|
|
46
|
+
|
|
47
|
+
const overlay = document.createElement('div');
|
|
48
|
+
overlay.id = 'effect-underwater';
|
|
49
|
+
document.body.appendChild(overlay);
|
|
50
|
+
|
|
51
|
+
const blurLayer = document.createElement('div');
|
|
52
|
+
blurLayer.id = 'effect-underwater-blur';
|
|
53
|
+
document.body.appendChild(blurLayer);
|
|
54
|
+
|
|
55
|
+
const causticWrap = document.createElement('div');
|
|
56
|
+
causticWrap.id = 'effect-underwater-caustics';
|
|
57
|
+
const canvas = document.createElement('canvas');
|
|
58
|
+
causticWrap.appendChild(canvas);
|
|
59
|
+
document.body.appendChild(causticWrap);
|
|
60
|
+
|
|
61
|
+
const ctx = canvas.getContext('2d');
|
|
62
|
+
let W, H;
|
|
63
|
+
function resize() { W = canvas.width = window.innerWidth; H = canvas.height = window.innerHeight; }
|
|
64
|
+
resize();
|
|
65
|
+
window.addEventListener('resize', resize);
|
|
66
|
+
|
|
67
|
+
const nodes = Array.from({ length: 10 }, () => ({
|
|
68
|
+
x: Math.random() * 1.2 - 0.1, y: Math.random() * 1.2 - 0.1,
|
|
69
|
+
vx: (Math.random() - 0.5) * 0.0006, vy: (Math.random() - 0.5) * 0.0006,
|
|
70
|
+
r: 0.12 + Math.random() * 0.2,
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
let scrollOffset = 0;
|
|
74
|
+
window.addEventListener('scroll', () => { scrollOffset = window.scrollY; }, { passive: true });
|
|
75
|
+
|
|
76
|
+
function draw() {
|
|
77
|
+
ctx.clearRect(0, 0, W, H);
|
|
78
|
+
const shift = scrollOffset * 0.3;
|
|
79
|
+
nodes.forEach(n => {
|
|
80
|
+
n.x += n.vx; n.y += n.vy;
|
|
81
|
+
if (n.x < -0.2 || n.x > 1.2) n.vx *= -1;
|
|
82
|
+
if (n.y < -0.2 || n.y > 1.2) n.vy *= -1;
|
|
83
|
+
const cx = n.x * W, cy = n.y * H - shift;
|
|
84
|
+
const rx = n.r * W, ry = n.r * H * 0.5;
|
|
85
|
+
const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(rx, ry));
|
|
86
|
+
grad.addColorStop(0, 'rgba(100,200,255,0.55)');
|
|
87
|
+
grad.addColorStop(0.4, 'rgba(60,160,220,0.2)');
|
|
88
|
+
grad.addColorStop(1, 'rgba(0,80,160,0)');
|
|
89
|
+
ctx.save();
|
|
90
|
+
ctx.scale(1, ry / rx);
|
|
91
|
+
ctx.beginPath();
|
|
92
|
+
ctx.arc(cx, cy * (rx / ry), rx, 0, Math.PI * 2);
|
|
93
|
+
ctx.fillStyle = grad;
|
|
94
|
+
ctx.fill();
|
|
95
|
+
ctx.restore();
|
|
96
|
+
});
|
|
97
|
+
requestAnimationFrame(draw);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* SVG wave distortion */
|
|
101
|
+
const svgFilter = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
102
|
+
svgFilter.style.cssText = 'position:fixed;width:0;height:0;';
|
|
103
|
+
svgFilter.innerHTML = `<defs><filter id="underwater-wave">
|
|
104
|
+
<feTurbulence type="fractalNoise" baseFrequency="0.012 0.018" numOctaves="3" seed="5" result="noise">
|
|
105
|
+
<animate attributeName="baseFrequency" values="0.012 0.018;0.014 0.020;0.012 0.018" dur="8s" repeatCount="indefinite"/>
|
|
106
|
+
</feTurbulence>
|
|
107
|
+
<feDisplacementMap in="SourceGraphic" in2="noise" scale="6" xChannelSelector="R" yChannelSelector="G"/>
|
|
108
|
+
</filter></defs>`;
|
|
109
|
+
document.body.appendChild(svgFilter);
|
|
110
|
+
overlay.style.filter = 'url(#underwater-wave)';
|
|
111
|
+
|
|
112
|
+
requestAnimationFrame(draw);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
116
|
+
else init();
|
|
117
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riverflowpkg/riverflow",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Drop-in vanilla JS browser widgets: top loading bar, smooth scroll, typing effect, underwater overlay, and a code editor with syntax highlighting.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"loading-bar",
|