button update

This commit is contained in:
2026-03-31 14:39:04 +07:00
parent 0f7f01310e
commit bd51ac5ddf
8 changed files with 582 additions and 149 deletions
+454
View File
@@ -0,0 +1,454 @@
import * as React from "react";
import {
AnimatePresence,
motion,
SVGMotionProps,
Variants,
} from "framer-motion";
interface VpnButtonProps extends SVGMotionProps<SVGSVGElement> {
theme?: "light" | "dark";
isOn: boolean;
}
const VpnControlButton = ({
theme = "dark",
isOn,
...props
}: VpnButtonProps) => {
const isDark = theme === "dark";
// Константы цветов из твоей последней правки
const glassBase = isDark ? "#000" : "oklch(1 0 0)";
const glassTop = isDark ? "#fff" : "oklch(0.987 0.002 197.1)";
const strokeColor = isDark ? "#000" : "#E9E9E9";
const powerOffColor = isDark ? "#161B1D" : "oklch(0.56 0.021 213.5)";
return (
<motion.svg
xmlns="http://www.w3.org/2000/svg"
width={400}
height={350}
fill="none"
viewBox="0 0 400 350"
style={{ cursor: "pointer", ...props.style }}
whileTap={{ scale: 0.96 }}
{...props}
>
{/* Внешние тени/корпус */}
<path
fill="url(#a)"
fillOpacity={0.4}
d="M330 173c0 71.797-58.203 130-130 130S70 244.797 70 173 128.203 43 200 43s130 58.203 130 130Z"
/>
<path
fill="url(#b)"
fillOpacity={0.15}
d="M330 173c0 71.797-58.203 130-130 130S70 244.797 70 173 128.203 43 200 43s130 58.203 130 130Z"
/>
{/* Основное внешнее кольцо */}
<path
stroke={strokeColor}
strokeOpacity={0.8}
strokeWidth={15}
d="M200 50.5c67.655 0 122.5 54.845 122.5 122.5S267.655 295.5 200 295.5 77.5 240.655 77.5 173 132.345 50.5 200 50.5Z"
/>
<path
stroke="url(#c)"
strokeOpacity={0.3}
strokeWidth={15}
d="M200 50.5c67.655 0 122.5 54.845 122.5 122.5S267.655 295.5 200 295.5 77.5 240.655 77.5 173 132.345 50.5 200 50.5Z"
/>
<g filter="url(#d)">
<circle cx={200} cy={173} r={125} fill="url(#e)" fillOpacity={0.5} />
<circle cx={200} cy={173} r={125} fill="url(#f)" fillOpacity={0.5} />
<circle
cx={200}
cy={173}
r={122.5}
stroke={isDark ? "#333" : "#E6E6E6"}
strokeOpacity={0.5}
strokeWidth={5}
/>
<circle
cx={200}
cy={173}
r={122.5}
stroke="#000"
strokeOpacity={0.7}
strokeWidth={5}
/>
</g>
<g filter="url(#g)">
<circle
cx={200}
cy={173}
r={125}
stroke="#fff"
strokeOpacity={0.8}
strokeWidth={5}
/>
</g>
<g filter="url(#h)">
<circle
cx={200}
cy={173}
r={125}
stroke="url(#i)"
strokeOpacity={0.8}
strokeWidth={5}
/>
</g>
{/* АНИМИРОВАННАЯ ГРУППА J: ВНЕШНИЙ НЕОН */}
<g filter="url(#j)">
<AnimatePresence>
{isOn && (
<>
<motion.path
d="M 200 48 A 125 125 0 0 1 200 298"
stroke="#8200DB"
strokeWidth={8}
strokeLinecap="round"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
exit={{ pathLength: 0 }}
transition={{ duration: 1.2, ease: [0.4, 0, 0.2, 1] }}
/>
<motion.path
d="M 200 298 A 125 125 0 0 1 200 48"
stroke="#8200DB"
strokeWidth={8}
strokeLinecap="round"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
exit={{ pathLength: 0 }}
transition={{
duration: 1.2,
delay: 0.3,
ease: [0.4, 0, 0.2, 1],
}}
/>
</>
)}
</AnimatePresence>
</g>
{/* Внутреннее стекло */}
<g fillOpacity={0.4} filter="url(#k)">
<path
fill="url(#l)"
d="M275 171c0 41.421-33.579 75-75 75s-75-33.579-75-75 33.579-75 75-75 75 33.579 75 75Z"
/>
<path
fill="url(#m)"
d="M275 171c0 41.421-33.579 75-75 75s-75-33.579-75-75 33.579-75 75-75 75 33.579 75 75Z"
/>
</g>
{/* Среднее кольцо */}
<path
stroke={isDark ? "#333" : "#E9E9E9"}
strokeOpacity={0.8}
strokeWidth={5}
d="M200 98.5c40.041 0 72.5 32.459 72.5 72.5s-32.459 72.5-72.5 72.5-72.5-32.459-72.5-72.5 32.459-72.5 72.5-72.5Z"
/>
<g filter="url(#o)">
<ellipse
cx={199.5}
cy={171}
stroke="url(#p)"
strokeOpacity={0.2}
strokeWidth={5}
rx={71.5}
ry={72}
/>
</g>
{/* АНИМИРОВАННАЯ ГРУППА Q: ВНУТРЕННЕЕ КОЛЬЦО */}
<g filter="url(#q)">
<AnimatePresence>
{isOn && (
<>
<motion.path
d="M 200 96 A 75 75 0 0 1 200 246"
stroke="#8200DB"
strokeOpacity={0.5}
strokeWidth={8}
strokeLinecap="round"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
exit={{ pathLength: 0 }}
transition={{
duration: 1.2,
delay: 0.1,
ease: [0.4, 0, 0.2, 1],
}}
/>
<motion.path
d="M 200 246 A 75 75 0 0 1 200 96"
stroke="#8200DB"
strokeOpacity={0.5}
strokeWidth={8}
strokeLinecap="round"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
exit={{ pathLength: 0 }}
transition={{
duration: 1.2,
delay: 0.4,
ease: [0.4, 0, 0.2, 1],
}}
/>
</>
)}
</AnimatePresence>
</g>
{/* Символ Power */}
<path
stroke="url(#r)"
strokeWidth={5}
d="M250 173c0 27.614-22.386 50-50 50s-50-22.386-50-50m50.5-46v71"
/>
<g strokeWidth={5} filter="url(#s)">
<motion.path
strokeOpacity={0.8}
animate={{ stroke: isOn ? "#AD46FF" : powerOffColor }}
transition={{ duration: 0.8 }}
d="M250 173c0 27.614-22.386 50-50 50s-50-22.386-50-50m50.5-46v71"
/>
<path
stroke="url(#t)"
strokeOpacity={0.44}
d="M250 173c0 27.614-22.386 50-50 50s-50-22.386-50-50m50.5-46v71"
/>
</g>
<defs>
<linearGradient
id="a"
x1={200}
x2={200}
y1={43}
y2={299}
gradientUnits="userSpaceOnUse"
>
<stop stopColor={isDark ? "#000" : "#fff"} />
<stop
offset={1}
stopColor={isDark ? "#000" : "#999"}
stopOpacity={0.4}
/>
</linearGradient>
<linearGradient
id="b"
x1={200}
x2={200}
y1={16}
y2={353.5}
gradientUnits="userSpaceOnUse"
>
<stop stopColor={isDark ? "#000" : "#fff"} />
<stop offset={1} stopColor="#8200DB" stopOpacity={0.3} />
</linearGradient>
<linearGradient
id="c"
x1={205}
x2={206.5}
y1={26.5}
y2={328.5}
gradientUnits="userSpaceOnUse"
>
<stop stopColor={isDark ? "#000" : "#fff"} />
<stop offset={0.978} stopColor="#0A0A0A" stopOpacity={0} />
</linearGradient>
<linearGradient
id="e"
x1={197}
x2={197.5}
y1={-10.5}
y2={463}
gradientUnits="userSpaceOnUse"
>
<stop stopColor={glassTop} />
<stop offset={1} stopColor={glassTop} stopOpacity={0} />
</linearGradient>
<linearGradient
id="f"
x1={200}
x2={200}
y1={48}
y2={442.5}
gradientUnits="userSpaceOnUse"
>
<stop stopColor={glassTop} stopOpacity={0.5} />
<stop offset={1} stopColor="#DAB2FF" stopOpacity={0.5} />
</linearGradient>
<linearGradient
id="i"
x1={212}
x2={359}
y1={198}
y2={358}
gradientUnits="userSpaceOnUse"
>
<stop offset={0.322} stopColor="#6E11B0" stopOpacity={0.7} />
<stop offset={0.851} stopColor={glassTop} stopOpacity={0.3} />
</linearGradient>
<linearGradient
id="l"
x1={200}
x2={200}
y1={96}
y2={243.692}
gradientUnits="userSpaceOnUse"
>
<stop stopColor={glassTop} />
<stop
offset={1}
stopColor={isDark ? "#111" : "#999"}
stopOpacity={0.4}
/>
</linearGradient>
<linearGradient
id="m"
x1={203}
x2={195}
y1={-20}
y2={259.5}
gradientUnits="userSpaceOnUse"
>
<stop stopColor={glassTop} />
<stop offset={1} stopColor="#6E11B0" stopOpacity={0.3} />
</linearGradient>
<linearGradient
id="r"
x1={200}
x2={200}
y1={35.5}
y2={301.5}
gradientUnits="userSpaceOnUse"
>
<stop offset={0.75} stopColor={powerOffColor} stopOpacity={0.7} />
<stop offset={1} stopColor="#8200DB" stopOpacity={0.3} />
</linearGradient>
{/* Обновленные фильтры с stdDeviation из твоей Figma */}
<filter
id="d"
width={280}
height={280}
x={60}
y={33}
filterUnits="userSpaceOnUse"
>
<feGaussianBlur stdDeviation={7.5} />
</filter>
<filter
id="g"
width={261}
height={261}
x={69.5}
y={42.5}
filterUnits="userSpaceOnUse"
>
<feGaussianBlur stdDeviation={1.5} />
</filter>
<filter
id="h"
width={269}
height={269}
x={65.5}
y={38.5}
filterUnits="userSpaceOnUse"
>
<feGaussianBlur stdDeviation={3.5} />
</filter>
<filter
id="j"
width={288}
height={288}
x={56}
y={29}
filterUnits="userSpaceOnUse"
>
<feGaussianBlur stdDeviation={7.5} />
</filter>
<filter
id="q"
width={188}
height={188}
x={106}
y={77}
filterUnits="userSpaceOnUse"
>
<feGaussianBlur stdDeviation={7.5} />
</filter>
<filter
id="s"
width={115}
height={108.5}
x={142.5}
y={122}
filterUnits="userSpaceOnUse"
>
<feGaussianBlur stdDeviation={2.5} />
</filter>
<filter
id="k"
width={150}
height={154}
x={125}
y={96}
filterUnits="userSpaceOnUse"
>
<feOffset dy={4} />
<feGaussianBlur stdDeviation={2} />
<feComposite in2="SourceAlpha" operator="arithmetic" k2={-1} k3={1} />
<feBlend in2="SourceGraphic" />
</filter>
<filter
id="o"
width={158}
height={159}
x={120.5}
y={91.5}
filterUnits="userSpaceOnUse"
>
<feGaussianBlur stdDeviation={2.5} />
</filter>
{/* Вспомогательные градиенты для эффектов иконки */}
<linearGradient
id="p"
x1={206.364}
x2={291.084}
y1={185.4}
y2={276.972}
gradientUnits="userSpaceOnUse"
>
<stop offset={0.322} stopColor="#6E11B0" stopOpacity={0.7} />
<stop offset={0.851} stopColor="#fff" stopOpacity={0.3} />
</linearGradient>
<linearGradient
id="t"
x1={200}
x2={203}
y1={127}
y2={261.5}
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#fff" />
<stop offset={1} stopOpacity={0.5} />
</linearGradient>
</defs>
</motion.svg>
);
};
export default VpnControlButton;