@once-ui-system/core 1.5.0 → 1.5.2
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 +54 -0
- package/dist/README.md +54 -0
- package/dist/components/CountdownFx.d.ts +11 -0
- package/dist/components/CountdownFx.d.ts.map +1 -0
- package/dist/components/CountdownFx.js +49 -0
- package/dist/components/CountdownFx.js.map +1 -0
- package/dist/components/Heading.d.ts.map +1 -1
- package/dist/components/MatrixFx.d.ts +9 -0
- package/dist/components/MatrixFx.d.ts.map +1 -1
- package/dist/components/MatrixFx.js +292 -17
- package/dist/components/MatrixFx.js.map +1 -1
- package/dist/components/WeatherFx.d.ts +16 -0
- package/dist/components/WeatherFx.d.ts.map +1 -0
- package/dist/components/WeatherFx.js +323 -0
- package/dist/components/WeatherFx.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -1
- package/dist/data/emoji-data.json +8 -0
- package/dist/modules/code/CodeBlock.d.ts +2 -2
- package/dist/modules/code/CodeBlock.d.ts.map +1 -1
- package/dist/modules/navigation/MegaMenu.d.ts.map +1 -1
- package/dist/modules/navigation/MegaMenu.js +0 -6
- package/dist/modules/navigation/MegaMenu.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<br/>
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<picture>
|
|
5
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://docs.once-ui.com/trademarks/icon-dark.svg" width="64" height="64">
|
|
6
|
+
<source media="(prefers-color-scheme: light)" srcset="https://docs.once-ui.com/trademarks/icon-light.svg" width="64" height="64">
|
|
7
|
+
<img alt="Once UI Logo" src="https://docs.once-ui.com/trademarks/icon-dark.svg" width="64" height="64">
|
|
8
|
+
</picture><picture>
|
|
9
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://docs.once-ui.com/trademarks/type-dark.svg" width="200" height="64">
|
|
10
|
+
<source media="(prefers-color-scheme: light)" srcset="https://docs.once-ui.com/trademarks/type-light.svg" width="200" height="64">
|
|
11
|
+
<img alt="Once UI Wordmark" src="https://docs.once-ui.com/trademarks/type-dark.svg" width="200" height="64">
|
|
12
|
+
</picture>
|
|
13
|
+
|
|
14
|
+
<br/>
|
|
15
|
+
|
|
16
|
+
The indie design system for Next.js apps
|
|
17
|
+
|
|
18
|
+
[](https://www.npmjs.com/package/@once-ui-system/core)
|
|
19
|
+
[](./LICENSE.md)
|
|
20
|
+
[](https://www.npmjs.com/package/@once-ui-system/core)
|
|
21
|
+
[](https://discord.com/invite/5EyAQ4eNdS)
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
<br/>
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
|
|
30
|
+
Learn how to set up and build with Once UI at [docs.once-ui.com](https://docs.once-ui.com/once-ui/quick-start).
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @once-ui-system/core
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Authors
|
|
39
|
+
|
|
40
|
+
Built and maintained by [**Lorant One**](https://lorant.one).
|
|
41
|
+
|
|
42
|
+
## Community
|
|
43
|
+
|
|
44
|
+
Join the [Design Engineers Club](https://discord.com/invite/5EyAQ4eNdS) for help, support and discussion.
|
|
45
|
+
|
|
46
|
+
Found a bug? Report it [here](https://github.com/once-ui-system/core/issues/new?labels=bug&template=bug_report.md). Got a feature request? Submit it [here](https://github.com/once-ui-system/core/issues/new?labels=feature%20request&template=feature_request.md).
|
|
47
|
+
|
|
48
|
+
## Contributing
|
|
49
|
+
|
|
50
|
+
Please read our [CONTRIBUTING.md](https://github.com/once-ui-system/core/blob/main/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to Once UI.
|
|
51
|
+
|
|
52
|
+
## Sponsors
|
|
53
|
+
|
|
54
|
+
Once UI is an indie project. [Sponsor us](https://github.com/sponsors/once-ui-system) and get featured on our site!
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<br/>
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<picture>
|
|
5
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://docs.once-ui.com/trademarks/icon-dark.svg" width="64" height="64">
|
|
6
|
+
<source media="(prefers-color-scheme: light)" srcset="https://docs.once-ui.com/trademarks/icon-light.svg" width="64" height="64">
|
|
7
|
+
<img alt="Once UI Logo" src="https://docs.once-ui.com/trademarks/icon-dark.svg" width="64" height="64">
|
|
8
|
+
</picture><picture>
|
|
9
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://docs.once-ui.com/trademarks/type-dark.svg" width="200" height="64">
|
|
10
|
+
<source media="(prefers-color-scheme: light)" srcset="https://docs.once-ui.com/trademarks/type-light.svg" width="200" height="64">
|
|
11
|
+
<img alt="Once UI Wordmark" src="https://docs.once-ui.com/trademarks/type-dark.svg" width="200" height="64">
|
|
12
|
+
</picture>
|
|
13
|
+
|
|
14
|
+
<br/>
|
|
15
|
+
|
|
16
|
+
The indie design system for Next.js apps
|
|
17
|
+
|
|
18
|
+
[](https://www.npmjs.com/package/@once-ui-system/core)
|
|
19
|
+
[](./LICENSE.md)
|
|
20
|
+
[](https://www.npmjs.com/package/@once-ui-system/core)
|
|
21
|
+
[](https://discord.com/invite/5EyAQ4eNdS)
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
<br/>
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
|
|
30
|
+
Learn how to set up and build with Once UI at [docs.once-ui.com](https://docs.once-ui.com/once-ui/quick-start).
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @once-ui-system/core
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Authors
|
|
39
|
+
|
|
40
|
+
Built and maintained by [**Lorant One**](https://lorant.one).
|
|
41
|
+
|
|
42
|
+
## Community
|
|
43
|
+
|
|
44
|
+
Join the [Design Engineers Club](https://discord.com/invite/5EyAQ4eNdS) for help, support and discussion.
|
|
45
|
+
|
|
46
|
+
Found a bug? Report it [here](https://github.com/once-ui-system/core/issues/new?labels=bug&template=bug_report.md). Got a feature request? Submit it [here](https://github.com/once-ui-system/core/issues/new?labels=feature%20request&template=feature_request.md).
|
|
47
|
+
|
|
48
|
+
## Contributing
|
|
49
|
+
|
|
50
|
+
Please read our [CONTRIBUTING.md](https://github.com/once-ui-system/core/blob/main/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to Once UI.
|
|
51
|
+
|
|
52
|
+
## Sponsors
|
|
53
|
+
|
|
54
|
+
Once UI is an indie project. [Sponsor us](https://github.com/sponsors/once-ui-system) and get featured on our site!
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CountFxProps } from ".";
|
|
3
|
+
export interface CountdownFxProps extends Omit<CountFxProps, 'value' | 'format' | 'separator' | 'effect'> {
|
|
4
|
+
targetDate: Date | string;
|
|
5
|
+
format?: "HH:MM:SS" | "DD:HH:MM:SS" | "MM:SS";
|
|
6
|
+
effect?: CountFxProps['effect'];
|
|
7
|
+
onComplete?: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare const CountdownFx: React.FC<CountdownFxProps>;
|
|
10
|
+
export { CountdownFx };
|
|
11
|
+
//# sourceMappingURL=CountdownFx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CountdownFx.d.ts","sourceRoot":"","sources":["../../src/components/CountdownFx.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAAsB,YAAY,EAAE,MAAM,GAAG,CAAC;AAErD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;IACvG,UAAU,EAAE,IAAI,GAAG,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC;IAC9C,MAAM,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAkH3C,CAAC;AAGF,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { Row, Text, CountFx } from ".";
|
|
5
|
+
const CountdownFx = ({ targetDate, format = "HH:MM:SS", effect = "wheel", onComplete, ...countFxProps }) => {
|
|
6
|
+
const [timeRemaining, setTimeRemaining] = useState({
|
|
7
|
+
days: 0,
|
|
8
|
+
hours: 0,
|
|
9
|
+
minutes: 0,
|
|
10
|
+
seconds: 0,
|
|
11
|
+
total: 0,
|
|
12
|
+
});
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const calculateTimeRemaining = () => {
|
|
15
|
+
const now = new Date().getTime();
|
|
16
|
+
const target = typeof targetDate === 'string' ? new Date(targetDate).getTime() : targetDate.getTime();
|
|
17
|
+
const difference = target - now;
|
|
18
|
+
if (difference <= 0) {
|
|
19
|
+
setTimeRemaining({ days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 });
|
|
20
|
+
if (onComplete) {
|
|
21
|
+
onComplete();
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
|
|
26
|
+
const hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
27
|
+
const minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
|
|
28
|
+
const seconds = Math.floor((difference % (1000 * 60)) / 1000);
|
|
29
|
+
setTimeRemaining({ days, hours, minutes, seconds, total: difference });
|
|
30
|
+
};
|
|
31
|
+
calculateTimeRemaining();
|
|
32
|
+
const interval = setInterval(calculateTimeRemaining, 1000);
|
|
33
|
+
return () => clearInterval(interval);
|
|
34
|
+
}, [targetDate, onComplete]);
|
|
35
|
+
const padZero = (num) => num.toString().padStart(2, '0');
|
|
36
|
+
const renderTimeUnit = (value, key) => {
|
|
37
|
+
const paddedValue = padZero(value);
|
|
38
|
+
const digits = paddedValue.split('');
|
|
39
|
+
return (_jsx(Row, { gap: "0", inline: true, children: digits.map((digit, index) => (_jsx(CountFx, { value: parseInt(digit), effect: effect, speed: 400, ...countFxProps }, `${key}-${index}`))) }, key));
|
|
40
|
+
};
|
|
41
|
+
const renderSeparator = () => (_jsx(Text, { ...countFxProps, style: { width: '0.5em', textAlign: 'center' }, children: ":" }, `sep-${Math.random()}`));
|
|
42
|
+
if (timeRemaining.total === 0) {
|
|
43
|
+
return (_jsxs(Row, { gap: "0", align: "center", textVariant: countFxProps.variant, style: { fontVariantNumeric: 'tabular-nums' }, children: [format === "DD:HH:MM:SS" && (_jsxs(_Fragment, { children: [renderTimeUnit(0, 'days'), renderSeparator()] })), renderTimeUnit(0, 'hours'), renderSeparator(), renderTimeUnit(0, 'minutes'), format !== "MM:SS" && (_jsxs(_Fragment, { children: [renderSeparator(), renderTimeUnit(0, 'seconds')] }))] }));
|
|
44
|
+
}
|
|
45
|
+
return (_jsxs(Row, { gap: "0", align: "center", style: { fontVariantNumeric: 'tabular-nums' }, children: [format === "DD:HH:MM:SS" && timeRemaining.days > 0 && (_jsxs(_Fragment, { children: [renderTimeUnit(timeRemaining.days, 'days'), renderSeparator()] })), format !== "MM:SS" && (_jsxs(_Fragment, { children: [renderTimeUnit(format === "DD:HH:MM:SS" ? timeRemaining.hours : timeRemaining.days * 24 + timeRemaining.hours, 'hours'), renderSeparator()] })), renderTimeUnit(timeRemaining.minutes, 'minutes'), renderSeparator(), renderTimeUnit(timeRemaining.seconds, 'seconds')] }));
|
|
46
|
+
};
|
|
47
|
+
CountdownFx.displayName = "CountdownFx";
|
|
48
|
+
export { CountdownFx };
|
|
49
|
+
//# sourceMappingURL=CountdownFx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CountdownFx.js","sourceRoot":"","sources":["../../src/components/CountdownFx.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Heading.d.ts","sourceRoot":"","sources":["../../src/components/Heading.tsx"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"Heading.d.ts","sourceRoot":"","sources":["../../src/components/Heading.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,OAAO,CAAC;AAG9D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGrE,KAAK,YAAY,CAAC,CAAC,SAAS,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC,GACrD,WAAW,GACX,YAAY,GACZ,wBAAwB,CAAC,CAAC,CAAC,CAAC;AAE9B,QAAA,MAAM,OAAO;KAAI,CAAC,SAAS,WAAW,2RA4BnC,YAAY,CAAC,CAAC,CAAC;;CAsEjB,CAAC;AAIF,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Flex } from ".";
|
|
3
|
+
interface BulgeConfig {
|
|
4
|
+
type?: "ripple" | "wave";
|
|
5
|
+
duration?: number;
|
|
6
|
+
intensity?: number;
|
|
7
|
+
repeat?: boolean;
|
|
8
|
+
delay?: number;
|
|
9
|
+
}
|
|
3
10
|
interface MatrixFxProps extends React.ComponentProps<typeof Flex> {
|
|
4
11
|
speed?: number;
|
|
5
12
|
colors?: string[];
|
|
@@ -8,8 +15,10 @@ interface MatrixFxProps extends React.ComponentProps<typeof Flex> {
|
|
|
8
15
|
revealFrom?: "center" | "top" | "bottom" | "left" | "right";
|
|
9
16
|
trigger?: "hover" | "instant" | "mount";
|
|
10
17
|
flicker?: boolean;
|
|
18
|
+
bulge?: BulgeConfig;
|
|
11
19
|
children?: React.ReactNode;
|
|
12
20
|
}
|
|
13
21
|
declare const MatrixFx: React.ForwardRefExoticComponent<Omit<MatrixFxProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
14
22
|
export { MatrixFx };
|
|
23
|
+
export type { BulgeConfig };
|
|
15
24
|
//# sourceMappingURL=MatrixFx.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MatrixFx.d.ts","sourceRoot":"","sources":["../../src/components/MatrixFx.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;AAEzB,UAAU,aAAc,SAAQ,KAAK,CAAC,cAAc,CAAC,OAAO,IAAI,CAAC;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5D,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,QAAA,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"MatrixFx.d.ts","sourceRoot":"","sources":["../../src/components/MatrixFx.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;AAEzB,UAAU,WAAW;IACnB,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAc,SAAQ,KAAK,CAAC,cAAc,CAAC,OAAO,IAAI,CAAC;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5D,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,QAAA,MAAM,QAAQ,mGAgrBb,CAAC;AAGF,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useEffect, useRef } from "react";
|
|
4
4
|
import { Flex } from ".";
|
|
5
|
-
const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"], size = 3, spacing = 3, revealFrom = "center", trigger = "instant", flicker = false, children, ...rest }, forwardedRef) => {
|
|
5
|
+
const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"], size = 3, spacing = 3, revealFrom = "center", trigger = "instant", flicker = false, bulge, children, ...rest }, forwardedRef) => {
|
|
6
6
|
const containerRef = useRef(null);
|
|
7
7
|
const canvasRef = useRef(null);
|
|
8
8
|
const animationRef = useRef(undefined);
|
|
@@ -12,6 +12,7 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
12
12
|
const hideStartProgressRef = useRef(0);
|
|
13
13
|
const isHoveredRef = useRef(false);
|
|
14
14
|
const mountAnimationCompleteRef = useRef(false);
|
|
15
|
+
const bulgeStartTimeRef = useRef(Date.now());
|
|
15
16
|
useEffect(() => {
|
|
16
17
|
if (forwardedRef) {
|
|
17
18
|
if ("current" in forwardedRef) {
|
|
@@ -51,15 +52,18 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
51
52
|
const computedColor = getComputedStyle(container).getPropertyValue(`--${color}`);
|
|
52
53
|
return computedColor || color;
|
|
53
54
|
});
|
|
54
|
-
// Create dot grid
|
|
55
|
+
// Create dot grid with padding to prevent edge gaps during displacement
|
|
55
56
|
const totalSize = size + spacing;
|
|
56
|
-
const
|
|
57
|
-
const
|
|
57
|
+
const maxDisplacement = (bulge?.intensity ?? 10) * 2; // Account for max possible displacement
|
|
58
|
+
const paddedWidth = canvasWidth + maxDisplacement * 2;
|
|
59
|
+
const paddedHeight = canvasHeight + maxDisplacement * 2;
|
|
60
|
+
const cols = Math.ceil(paddedWidth / totalSize);
|
|
61
|
+
const rows = Math.ceil(paddedHeight / totalSize);
|
|
58
62
|
const dots = [];
|
|
59
63
|
for (let row = 0; row < rows; row++) {
|
|
60
64
|
for (let col = 0; col < cols; col++) {
|
|
61
|
-
const x = col * totalSize + size / 2;
|
|
62
|
-
const y = row * totalSize + size / 2;
|
|
65
|
+
const x = col * totalSize + size / 2 - maxDisplacement;
|
|
66
|
+
const y = row * totalSize + size / 2 - maxDisplacement;
|
|
63
67
|
// Calculate distance from reveal origin
|
|
64
68
|
let distanceFromOrigin = 0;
|
|
65
69
|
const centerX = canvasWidth / 2;
|
|
@@ -101,9 +105,56 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
101
105
|
const maxDistance = Math.max(...dots.map((d) => d.distanceFromOrigin));
|
|
102
106
|
// Animation loop
|
|
103
107
|
const startTime = Date.now();
|
|
108
|
+
// Bulge configuration
|
|
109
|
+
const bulgeEnabled = !!bulge;
|
|
110
|
+
const bulgeType = bulge?.type ?? "ripple"; // Default: ripple
|
|
111
|
+
const bulgeDuration = bulge?.duration ?? 3; // Default: 3 seconds per wave
|
|
112
|
+
const bulgeIntensity = bulge?.intensity ?? 10; // Default: 10px displacement
|
|
113
|
+
const bulgeRepeat = bulge?.repeat ?? true; // Default: true
|
|
114
|
+
const bulgeDelay = bulge?.delay ?? 0; // Default: 0ms
|
|
115
|
+
// Calculate center point for circular wave
|
|
116
|
+
const centerX = canvasWidth / 2;
|
|
117
|
+
const centerY = canvasHeight / 2;
|
|
118
|
+
const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);
|
|
104
119
|
const animate = () => {
|
|
105
120
|
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
106
121
|
const time = (Date.now() - startTime) / 1000; // Time in seconds
|
|
122
|
+
// Calculate circular wave radius (travels from center to edge)
|
|
123
|
+
let waveProgress = 0;
|
|
124
|
+
let showBulge = false;
|
|
125
|
+
let bulgeFadeOut = 1; // Multiplier for fading out bulge effect (1 = full effect, 0 = no effect)
|
|
126
|
+
if (bulgeEnabled) {
|
|
127
|
+
const bulgeElapsed = (Date.now() - bulgeStartTimeRef.current) / 1000;
|
|
128
|
+
const delaySeconds = bulgeDelay / 1000;
|
|
129
|
+
const totalCycleDuration = bulgeDuration + delaySeconds;
|
|
130
|
+
const adjustedTime = bulgeElapsed - delaySeconds;
|
|
131
|
+
const fadeStartPercent = 0.6; // Start fading at 60% of wave duration
|
|
132
|
+
if (adjustedTime >= 0) {
|
|
133
|
+
showBulge = true;
|
|
134
|
+
if (bulgeRepeat) {
|
|
135
|
+
// Continuous repeating wave
|
|
136
|
+
const cycleTime = adjustedTime % totalCycleDuration;
|
|
137
|
+
waveProgress = cycleTime < bulgeDuration ? cycleTime / bulgeDuration : 0;
|
|
138
|
+
showBulge = cycleTime < bulgeDuration; // Only show during active wave, not delay
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Single wave with opacity fade during last 40%
|
|
142
|
+
if (adjustedTime <= bulgeDuration) {
|
|
143
|
+
waveProgress = adjustedTime / bulgeDuration;
|
|
144
|
+
// Start fading opacity at 60% of duration
|
|
145
|
+
if (waveProgress >= fadeStartPercent) {
|
|
146
|
+
const fadeProgress = (waveProgress - fadeStartPercent) / (1 - fadeStartPercent);
|
|
147
|
+
bulgeFadeOut = 1 - fadeProgress; // Fade from 1 to 0
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
waveProgress = 0;
|
|
152
|
+
showBulge = false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const waveRadius = waveProgress * maxRadius * 1.5; // Travel beyond edges for smooth cycle
|
|
107
158
|
// For instant trigger, show all dots immediately at full opacity
|
|
108
159
|
if (trigger === "instant") {
|
|
109
160
|
dots.forEach((dot) => {
|
|
@@ -115,8 +166,74 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
115
166
|
const flickerMultiplier = 0.6 + (flickerValue * 0.4); // Oscillate between 0.6 and 1.0
|
|
116
167
|
opacity *= flickerMultiplier;
|
|
117
168
|
}
|
|
118
|
-
|
|
119
|
-
|
|
169
|
+
// Calculate bulge displacement
|
|
170
|
+
let offsetX = 0;
|
|
171
|
+
let offsetY = 0;
|
|
172
|
+
let sizeMultiplier = 1;
|
|
173
|
+
let bulgeOpacity = 1;
|
|
174
|
+
if (bulgeEnabled && showBulge) {
|
|
175
|
+
if (bulgeType === "ripple") {
|
|
176
|
+
// Ripple: circular wave from center
|
|
177
|
+
const dx = dot.x - centerX;
|
|
178
|
+
const dy = dot.y - centerY;
|
|
179
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
180
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
181
|
+
const waveWidth = maxRadius * 0.15;
|
|
182
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
183
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
184
|
+
const angle = Math.atan2(dy, dx);
|
|
185
|
+
const displacementAmount = waveFactor * bulgeIntensity * bulgeFadeOut;
|
|
186
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
187
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
188
|
+
sizeMultiplier = 1 + waveFactor * 0.8 * bulgeFadeOut;
|
|
189
|
+
const waveOpacity = 0.3 + waveFactor * 0.7;
|
|
190
|
+
bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
|
|
191
|
+
}
|
|
192
|
+
else if (bulgeType === "wave") {
|
|
193
|
+
// Wave: Organic S-curve from bottom-left to top-right with rotation
|
|
194
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
195
|
+
// Start wave off-screen before bottom-left, end off-screen after top-right
|
|
196
|
+
const wavePosOnDiagonal = (waveProgress * diagonalLength * 1.4) - (diagonalLength * 0.2);
|
|
197
|
+
// Calculate distance along the diagonal (from bottom-left to top-right)
|
|
198
|
+
const normalizedX = dot.x / canvasWidth;
|
|
199
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
200
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
201
|
+
// Distance from wave front
|
|
202
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
203
|
+
const waveWidth = diagonalLength * 0.25;
|
|
204
|
+
const distanceNorm = distanceToWaveFront / waveWidth;
|
|
205
|
+
// Smooth wave factor with wider influence
|
|
206
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 2.5);
|
|
207
|
+
// Create rotating S-curve with multiple frequency components
|
|
208
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
209
|
+
// Primary S-curve that rotates over time
|
|
210
|
+
const rotationPhase = waveProgress * Math.PI * 3; // Rotates 1.5 times during animation
|
|
211
|
+
const primaryFreq = 3; // Number of S-curves along the wave
|
|
212
|
+
const sCurvePrimary = Math.sin(perpendicularOffset * Math.PI * primaryFreq + rotationPhase);
|
|
213
|
+
// Secondary curve for complexity (higher frequency, lower amplitude)
|
|
214
|
+
const secondaryFreq = 7;
|
|
215
|
+
const sCurveSecondary = Math.sin(perpendicularOffset * Math.PI * secondaryFreq - rotationPhase * 1.5) * 0.4;
|
|
216
|
+
// Combine curves for organic motion
|
|
217
|
+
const sCurveFactor = sCurvePrimary + sCurveSecondary;
|
|
218
|
+
// Wave straightens at the edges (distanceNorm affects curve strength)
|
|
219
|
+
const curveStrength = 1 - Math.abs(distanceNorm) * 0.5;
|
|
220
|
+
const modulatedCurve = sCurveFactor * curveStrength;
|
|
221
|
+
// Apply displacement
|
|
222
|
+
const baseDisplacement = waveFactor * bulgeIntensity * bulgeFadeOut;
|
|
223
|
+
const diagonalAngle = Math.PI / 4;
|
|
224
|
+
// Perpendicular displacement creates the S-curve
|
|
225
|
+
const perpAngle = diagonalAngle + Math.PI / 2;
|
|
226
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + Math.cos(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
|
|
227
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement - Math.sin(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
|
|
228
|
+
sizeMultiplier = 1 + waveFactor * 0.5 * bulgeFadeOut;
|
|
229
|
+
const waveOpacity = 0.4 + waveFactor * 0.6;
|
|
230
|
+
bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
234
|
+
const adjustedSize = size * sizeMultiplier;
|
|
235
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
236
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
120
237
|
});
|
|
121
238
|
ctx.globalAlpha = 1;
|
|
122
239
|
animationRef.current = requestAnimationFrame(animate);
|
|
@@ -149,9 +266,53 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
149
266
|
}
|
|
150
267
|
}
|
|
151
268
|
if (opacity > 0) {
|
|
269
|
+
// Calculate bulge displacement
|
|
270
|
+
let offsetX = 0;
|
|
271
|
+
let offsetY = 0;
|
|
272
|
+
let sizeMultiplier = 1;
|
|
273
|
+
let bulgeOpacity = 1;
|
|
274
|
+
if (bulgeEnabled) {
|
|
275
|
+
if (bulgeType === "ripple") {
|
|
276
|
+
const dx = dot.x - centerX;
|
|
277
|
+
const dy = dot.y - centerY;
|
|
278
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
279
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
280
|
+
const waveWidth = maxRadius * 0.15;
|
|
281
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
282
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
283
|
+
const angle = Math.atan2(dy, dx);
|
|
284
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
285
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
286
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
287
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
288
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
289
|
+
}
|
|
290
|
+
else if (bulgeType === "wave") {
|
|
291
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
292
|
+
const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
|
|
293
|
+
const normalizedX = dot.x / canvasWidth;
|
|
294
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
295
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
296
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
297
|
+
const waveWidth = diagonalLength * 0.2;
|
|
298
|
+
const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
|
|
299
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
|
|
300
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
301
|
+
const sCurvePhase = perpendicularOffset * Math.PI * 2;
|
|
302
|
+
const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
|
|
303
|
+
const baseDisplacement = waveFactor * bulgeIntensity;
|
|
304
|
+
const diagonalAngle = Math.PI / 4;
|
|
305
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
306
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
307
|
+
sizeMultiplier = 1 + waveFactor * 0.6;
|
|
308
|
+
bulgeOpacity = 0.4 + waveFactor * 0.6;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
152
311
|
ctx.fillStyle = dot.color;
|
|
153
|
-
ctx.globalAlpha = opacity;
|
|
154
|
-
|
|
312
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
313
|
+
const adjustedSize = size * sizeMultiplier;
|
|
314
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
315
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
155
316
|
}
|
|
156
317
|
});
|
|
157
318
|
}
|
|
@@ -165,8 +326,52 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
165
326
|
const flickerMultiplier = 0.6 + (flickerValue * 0.4);
|
|
166
327
|
opacity *= flickerMultiplier;
|
|
167
328
|
}
|
|
168
|
-
|
|
169
|
-
|
|
329
|
+
// Calculate bulge displacement
|
|
330
|
+
let offsetX = 0;
|
|
331
|
+
let offsetY = 0;
|
|
332
|
+
let sizeMultiplier = 1;
|
|
333
|
+
let bulgeOpacity = 1;
|
|
334
|
+
if (bulgeEnabled) {
|
|
335
|
+
if (bulgeType === "ripple") {
|
|
336
|
+
const dx = dot.x - centerX;
|
|
337
|
+
const dy = dot.y - centerY;
|
|
338
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
339
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
340
|
+
const waveWidth = maxRadius * 0.15;
|
|
341
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
342
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
343
|
+
const angle = Math.atan2(dy, dx);
|
|
344
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
345
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
346
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
347
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
348
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
349
|
+
}
|
|
350
|
+
else if (bulgeType === "wave") {
|
|
351
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
352
|
+
const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
|
|
353
|
+
const normalizedX = dot.x / canvasWidth;
|
|
354
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
355
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
356
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
357
|
+
const waveWidth = diagonalLength * 0.2;
|
|
358
|
+
const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
|
|
359
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
|
|
360
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
361
|
+
const sCurvePhase = perpendicularOffset * Math.PI * 2;
|
|
362
|
+
const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
|
|
363
|
+
const baseDisplacement = waveFactor * bulgeIntensity;
|
|
364
|
+
const diagonalAngle = Math.PI / 4;
|
|
365
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
366
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
367
|
+
sizeMultiplier = 1 + waveFactor * 0.6;
|
|
368
|
+
bulgeOpacity = 0.4 + waveFactor * 0.6;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
372
|
+
const adjustedSize = size * sizeMultiplier;
|
|
373
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
374
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
170
375
|
});
|
|
171
376
|
}
|
|
172
377
|
ctx.globalAlpha = 1;
|
|
@@ -200,9 +405,53 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
200
405
|
}
|
|
201
406
|
}
|
|
202
407
|
if (opacity > 0) {
|
|
408
|
+
// Calculate bulge displacement
|
|
409
|
+
let offsetX = 0;
|
|
410
|
+
let offsetY = 0;
|
|
411
|
+
let sizeMultiplier = 1;
|
|
412
|
+
let bulgeOpacity = 1;
|
|
413
|
+
if (bulgeEnabled) {
|
|
414
|
+
if (bulgeType === "ripple") {
|
|
415
|
+
const dx = dot.x - centerX;
|
|
416
|
+
const dy = dot.y - centerY;
|
|
417
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
418
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
419
|
+
const waveWidth = maxRadius * 0.15;
|
|
420
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
421
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
422
|
+
const angle = Math.atan2(dy, dx);
|
|
423
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
424
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
425
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
426
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
427
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
428
|
+
}
|
|
429
|
+
else if (bulgeType === "wave") {
|
|
430
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
431
|
+
const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
|
|
432
|
+
const normalizedX = dot.x / canvasWidth;
|
|
433
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
434
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
435
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
436
|
+
const waveWidth = diagonalLength * 0.2;
|
|
437
|
+
const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
|
|
438
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
|
|
439
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
440
|
+
const sCurvePhase = perpendicularOffset * Math.PI * 2;
|
|
441
|
+
const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
|
|
442
|
+
const baseDisplacement = waveFactor * bulgeIntensity;
|
|
443
|
+
const diagonalAngle = Math.PI / 4;
|
|
444
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
445
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
446
|
+
sizeMultiplier = 1 + waveFactor * 0.6;
|
|
447
|
+
bulgeOpacity = 0.4 + waveFactor * 0.6;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
203
450
|
ctx.fillStyle = dot.color;
|
|
204
|
-
ctx.globalAlpha = opacity;
|
|
205
|
-
|
|
451
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
452
|
+
const adjustedSize = size * sizeMultiplier;
|
|
453
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
454
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
206
455
|
}
|
|
207
456
|
});
|
|
208
457
|
}
|
|
@@ -232,9 +481,31 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
232
481
|
}
|
|
233
482
|
}
|
|
234
483
|
if (opacity > 0) {
|
|
484
|
+
// Calculate bulge displacement
|
|
485
|
+
let offsetX = 0;
|
|
486
|
+
let offsetY = 0;
|
|
487
|
+
let sizeMultiplier = 1;
|
|
488
|
+
let bulgeOpacity = 1;
|
|
489
|
+
if (bulgeEnabled) {
|
|
490
|
+
const dx = dot.x - centerX;
|
|
491
|
+
const dy = dot.y - centerY;
|
|
492
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
493
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
494
|
+
const waveWidth = maxRadius * 0.15;
|
|
495
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
496
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
497
|
+
const angle = Math.atan2(dy, dx);
|
|
498
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
499
|
+
offsetX = Math.cos(angle) * displacementAmount * 0.3;
|
|
500
|
+
offsetY = Math.sin(angle) * displacementAmount * 0.3 - waveFactor * bulgeIntensity;
|
|
501
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
502
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
503
|
+
}
|
|
235
504
|
ctx.fillStyle = dot.color;
|
|
236
|
-
ctx.globalAlpha = opacity;
|
|
237
|
-
|
|
505
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
506
|
+
const adjustedSize = size * sizeMultiplier;
|
|
507
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
508
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
238
509
|
}
|
|
239
510
|
});
|
|
240
511
|
}
|
|
@@ -254,7 +525,7 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
254
525
|
cancelAnimationFrame(animationRef.current);
|
|
255
526
|
}
|
|
256
527
|
};
|
|
257
|
-
}, [colors, size, spacing, speed, revealFrom, trigger, flicker]);
|
|
528
|
+
}, [colors, size, spacing, speed, revealFrom, trigger, flicker, bulge]);
|
|
258
529
|
const handleMouseEnter = () => {
|
|
259
530
|
if (trigger === "hover" && !isHoveredRef.current) {
|
|
260
531
|
const now = Date.now();
|
|
@@ -275,6 +546,10 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
275
546
|
// Fresh start
|
|
276
547
|
revealStartTimeRef.current = now;
|
|
277
548
|
}
|
|
549
|
+
// Reset bulge animation on each hover when repeat is false
|
|
550
|
+
if (bulge && !bulge.repeat) {
|
|
551
|
+
bulgeStartTimeRef.current = now;
|
|
552
|
+
}
|
|
278
553
|
isHoveredRef.current = true;
|
|
279
554
|
hideStartProgressRef.current = 0; // Clear hide state
|
|
280
555
|
}
|