|
1 | | -import {useId} from 'react'; |
2 | 1 | import {useTheme} from '@emotion/react'; |
3 | 2 |
|
4 | 3 | interface AnimatedSentryLogoProps { |
5 | 4 | /** |
6 | 5 | * Draw progress from 0 (no stroke) to 1 (fully drawn). |
7 | | - * Uses stroke-dashoffset on the Sentry glyph outline. |
| 6 | + * Uses the stroke-dashoffset technique from the app loading screen. |
8 | 7 | */ |
9 | 8 | progress: number; |
10 | 9 | className?: string; |
11 | 10 | size?: number; |
12 | 11 | } |
13 | 12 |
|
14 | | -// Single-path Sentry glyph (64×59 viewBox) |
| 13 | +const DASH_TOTAL = 1150; |
| 14 | + |
| 15 | +// Filled glyph (same path as index.ejs base + mask) |
15 | 16 | const SENTRY_GLYPH = |
16 | | - 'M31.9948 0C33.0411 0 34.0698 0.28309 34.9701 0.816074C35.8698 1.34902 36.6108 2.11326 37.1138 3.03002L63.1813 49.4916C63.7108 50.4167 63.9896 51.4634 63.9896 52.5294C63.9896 53.3288 63.8344 54.1179 63.5327 54.8527L63.2243 55.6492C62.7224 56.5678 61.9805 57.3325 61.0806 57.8671C60.1803 58.4016 59.1523 58.6861 58.1053 58.687H51.9867V53.611H58.1053C58.275 53.6124 58.4421 53.5664 58.5894 53.4821C58.7365 53.398 58.8599 53.2774 58.9448 53.1307C59.0363 52.9806 59.0851 52.8068 59.0853 52.6309C59.0853 52.4551 59.0361 52.2814 58.9448 52.1311L32.8733 5.63052C32.7904 5.48053 32.6696 5.35444 32.5219 5.26739C32.3743 5.18042 32.2052 5.13463 32.0338 5.13463C31.8633 5.1349 31.6967 5.18091 31.5497 5.26739C31.402 5.35444 31.2773 5.48053 31.1943 5.63052L25.1968 16.3059C31.3083 20.4099 36.3755 25.887 39.9915 32.2994C44.0222 39.5769 46.1174 47.7675 46.0711 56.0865V58.4879H30.3158V55.9694C30.3478 50.489 28.9706 45.0915 26.3174 40.2961C24.0788 36.3061 20.9951 32.8521 17.282 30.1791L14.3223 35.4582C17.1667 37.6052 19.5265 40.331 21.2414 43.455C23.3822 47.3214 24.5081 51.667 24.5174 56.0865V58.609H5.92727C4.88044 58.6081 3.85214 58.3233 2.95192 57.789C2.05148 57.2543 1.31035 56.4862 0.808265 55.5672C0.278995 54.6422 0 53.5952 0 52.5294C1.59391e-05 51.4636 0.27896 50.4166 0.808265 49.4916L4.56845 42.7717C6.13984 43.3348 7.58937 44.1931 8.84406 45.2941L5.08777 52.0101C4.99593 52.1606 4.94721 52.3335 4.94721 52.5099C4.94727 52.6861 4.996 52.8592 5.08777 53.0097C5.17546 53.1534 5.29701 53.2735 5.4431 53.3572C5.58995 53.4411 5.75816 53.4875 5.92727 53.4899H19.4413C19.0534 50.4366 17.9811 47.5091 16.3059 44.927C14.6307 42.345 12.3957 40.1727 9.76556 38.5741L7.64533 37.2973L15.521 23.303L17.6413 24.5408C23.0555 27.7797 27.524 32.3846 30.5969 37.8947C33.2819 42.6981 34.8667 48.0386 35.2357 53.529H41.1512C40.7642 46.9702 38.8906 40.5854 35.673 34.8569C32.1062 28.4358 26.9071 23.071 20.601 19.3046L18.4417 18.0239L26.8758 3.03002C27.3791 2.11272 28.123 1.34906 29.0233 0.816074C29.9234 0.283345 30.9489 0.000109961 31.9948 0Z'; |
| 17 | + 'M223.687 85.56a28.02 28.02 0 0 0-24-13.559 28.02 28.02 0 0 0-24 13.559l-39.48 67.62a193.26 193.26 0 0 1 106.5 159.96h-27.72a166.07 166.07 0 0 0-92.76-136.32L85.687 240a95.52 95.52 0 0 1 55.38 73.02h-63.66a4.55 4.55 0 0 1-3.659-2.325 4.56 4.56 0 0 1-.06-4.335l17.64-30a64.4 64.4 0 0 0-20.16-11.4l-17.46 30a27.24 27.24 0 0 0 2.023 30.435 27.2 27.2 0 0 0 8.116 7.005 27.96 27.96 0 0 0 13.56 3.6h87.18a116.41 116.41 0 0 0-48-103.86l13.86-24a143.22 143.22 0 0 1 61.8 127.86h73.86a215.28 215.28 0 0 0-98.46-190.8l28.02-48a4.62 4.62 0 0 1 6.3-1.62c3.18 1.74 121.74 208.62 123.96 211.02a4.562 4.562 0 0 1-4.08 6.78h-28.56q.54 11.46 0 22.86h28.68a27.54 27.54 0 0 0 27.72-27.66 26.94 26.94 0 0 0-3.72-13.68z'; |
| 18 | + |
| 19 | +// Stroke paths that trace the logo outline (one group from index.ejs) |
| 20 | +const STROKE_PATHS = [ |
| 21 | + 'm82.8 253.5-12.7 22-12.5 21.2-.2.3v.1l-.2.2v.3a24 24 0 00.3 22.4l.3.6A24 24 0 0076.4 332h87l-3.2-21.8A115 115 0 00112 233l17.6-30.4a147 147 0 0166.8 112l1.3 17.5h65.6l-1.3-20.2a212 212 0 00-99.3-165.8l29.6-50.8a9 9 0 013.3-3.1 9 9 0 0112.1 3.1l122.2 209.2h.1a8 8 0 011 4v.2a8.5 8.5 0 01-8.6 8.5h-38.9', |
| 22 | + 'M92.5 268.7 88 276.6 71.4 305v.1a8 8 0 000 7.2l.2.4a8 8 0 006 3.4h67.3l-.6-3.4A99 99 0 0097.2 242.5l-6.8-4 33.4-57.8 6.9 4a163 163 0 0181.6 128.8l.2 2.7h33.7L246 313A196 196 0 00147.8 155.7l-7-4L178.6 87v-.1a25 25 0 0133-9.1l.8.3a25 25 0 019 8.9h.1v.2L343.7 296.4a24 24 0 013.3 12v.4a24.5 24.5 0 01-24.6 24.4H291.5', |
| 23 | + 'M85.7 264.5l-8.7 15-12.5 21.3-.1.2-.1.3A15.6 15.6 0 0076.9 324h77.2l-1.8-12.6a107 107 0 00-51-75.8l25.5-44.1A155 155 0 01204.3 314l.8 10.1h49.7l-.8-11.7A204 204 0 00151.8 148.9l33.6-57.7a17 17 0 0129.2 0L336.8 300.4a16 16 0 012.2 8.2 16.5 16.5 0 01-16.6 16.6H291.5', |
| 24 | +]; |
17 | 25 |
|
18 | 26 | /** |
19 | | - * Sentry brand mark with stroke-draw progress. The outline draws |
20 | | - * itself via stroke-dashoffset, driven by a `progress` prop (0-1). |
| 27 | + * Sentry brand mark with stroke-draw progress, adapted from the app |
| 28 | + * loading animation in static/index.ejs. Single color, progress-driven |
| 29 | + * instead of the looping multicolor original. |
21 | 30 | */ |
22 | 31 | export function AnimatedSentryLogo({ |
23 | 32 | progress, |
24 | 33 | size = 72, |
25 | 34 | className, |
26 | 35 | }: AnimatedSentryLogoProps) { |
27 | 36 | const theme = useTheme(); |
28 | | - const clipId = useId(); |
| 37 | + const gooId = 'goo-layer'; |
| 38 | + const maskId = 'mask-layer'; |
| 39 | + |
| 40 | + const dashOffset = DASH_TOTAL * (1 - progress); |
29 | 41 |
|
30 | 42 | return ( |
31 | 43 | <svg |
32 | 44 | width={size} |
33 | 45 | height={size} |
34 | | - viewBox="0 0 64 59" |
| 46 | + viewBox="0 0 400 400" |
35 | 47 | fill="none" |
36 | 48 | xmlns="http://www.w3.org/2000/svg" |
37 | 49 | className={className} |
38 | 50 | role="img" |
39 | 51 | aria-label="Sentry" |
40 | 52 | > |
41 | 53 | <defs> |
42 | | - <clipPath id={clipId}> |
43 | | - <path d={SENTRY_GLYPH} /> |
44 | | - </clipPath> |
| 54 | + <filter id={gooId}> |
| 55 | + <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" /> |
| 56 | + <feColorMatrix |
| 57 | + in="blur" |
| 58 | + mode="matrix" |
| 59 | + values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 36 -4" |
| 60 | + result="goo" |
| 61 | + /> |
| 62 | + <feComposite in="SourceGraphic" in2="goo" operator="atop" /> |
| 63 | + </filter> |
45 | 64 | </defs> |
46 | 65 |
|
| 66 | + <mask |
| 67 | + id={maskId} |
| 68 | + width="300" |
| 69 | + height="265" |
| 70 | + x="50" |
| 71 | + y="72" |
| 72 | + maskUnits="userSpaceOnUse" |
| 73 | + style={{maskType: 'alpha'} as React.CSSProperties} |
| 74 | + > |
| 75 | + <path fill={theme.tokens.graphics.accent.vibrant} d={SENTRY_GLYPH} /> |
| 76 | + </mask> |
| 77 | + |
47 | 78 | {/* Muted base shape */} |
48 | | - <path fill={theme.tokens.background.secondary} d={SENTRY_GLYPH} /> |
| 79 | + <path fill={theme.tokens.graphics.neutral.muted} d={SENTRY_GLYPH} /> |
49 | 80 |
|
50 | | - {/* Stroke-draw overlay, clipped to the glyph shape */} |
51 | | - <path |
52 | | - d={SENTRY_GLYPH} |
53 | | - clipPath={`url(#${clipId})`} |
54 | | - pathLength={1} |
| 81 | + {/* Stroke-draw overlay, masked to the logo shape */} |
| 82 | + <g |
| 83 | + mask={`url(#${maskId})`} |
| 84 | + filter={`url(#${gooId})`} |
55 | 85 | stroke={theme.tokens.content.accent} |
56 | | - strokeWidth="4" |
| 86 | + strokeWidth="5" |
57 | 87 | strokeLinecap="round" |
58 | 88 | strokeLinejoin="round" |
59 | | - strokeDasharray={1} |
60 | | - strokeDashoffset={1 - progress} |
61 | | - style={{transition: 'stroke-dashoffset 600ms ease-out'}} |
62 | | - /> |
| 89 | + > |
| 90 | + {STROKE_PATHS.map((d, i) => ( |
| 91 | + <path |
| 92 | + key={i} |
| 93 | + d={d} |
| 94 | + strokeDasharray={DASH_TOTAL} |
| 95 | + strokeDashoffset={dashOffset} |
| 96 | + style={{transition: 'stroke-dashoffset 600ms ease-out'}} |
| 97 | + /> |
| 98 | + ))} |
| 99 | + </g> |
63 | 100 | </svg> |
64 | 101 | ); |
65 | 102 | } |
0 commit comments