Skip to content

Commit 2d8bfc3

Browse files
authored
components: Rewrite Spinner for better performance (#2384)
1 parent 83bd581 commit 2d8bfc3

3 files changed

Lines changed: 46 additions & 83 deletions

File tree

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,17 @@
1-
import React, { SVGAttributes } from 'react'
2-
import { keyframes } from '@emotion/react'
3-
1+
import React from 'react'
42
import type { ThemeUICSSObject } from '@theme-ui/css'
53

64
import { Box, BoxOwnProps, BoxProps } from './Box'
75
import type { ForwardRef } from './types'
86
import { __internalProps } from './util'
97

10-
const spin = keyframes({
11-
from: {
12-
transform: 'rotate(0deg)',
13-
},
14-
to: {
15-
transform: 'rotate(360deg)',
16-
},
17-
})
18-
198
export interface SpinnerProps
209
extends Omit<
2110
React.ComponentPropsWithRef<'svg'>,
2211
'opacity' | 'color' | 'css' | 'sx' | 'strokeWidth'
2312
>,
2413
BoxOwnProps {
25-
size?: number | string
14+
size?: number
2615
strokeWidth?: number
2716
title?: string
2817
duration?: number
@@ -34,37 +23,17 @@ export const Spinner: ForwardRef<SVGSVGElement, SpinnerProps> =
3423
size = 48,
3524
strokeWidth = 4,
3625
max = 1,
37-
title = 'Loading...',
38-
duration = 500,
26+
title = 'Loading',
27+
duration = 750,
3928
...props
4029
},
4130
ref
4231
) {
43-
const r = 16 - strokeWidth
44-
const C = 2 * r * Math.PI
45-
const offset = C - (1 / 4) * C
46-
4732
const __css: ThemeUICSSObject = {
4833
color: 'primary',
4934
overflow: 'visible',
5035
}
5136

52-
const circleProps: SVGAttributes<SVGCircleElement> = {
53-
cx: 16,
54-
cy: 16,
55-
r,
56-
strokeDasharray: C,
57-
strokeDashoffset: offset,
58-
}
59-
60-
const __circleCss: ThemeUICSSObject = {
61-
transformOrigin: '50% 50%',
62-
animationName: spin.toString(),
63-
animationTimingFunction: 'linear',
64-
animationDuration: duration + 'ms',
65-
animationIterationCount: 'infinite',
66-
}
67-
6837
const svgProps = {
6938
strokeWidth,
7039

@@ -77,6 +46,14 @@ export const Spinner: ForwardRef<SVGSVGElement, SpinnerProps> =
7746
role: 'img',
7847
}
7948

49+
const circleProps = {
50+
strokeWidth,
51+
r: 16 - strokeWidth,
52+
cx: 16,
53+
cy: 16,
54+
fill: 'none',
55+
}
56+
8057
return (
8158
<Box
8259
ref={ref}
@@ -86,14 +63,18 @@ export const Spinner: ForwardRef<SVGSVGElement, SpinnerProps> =
8663
{...__internalProps({ __css })}
8764
>
8865
<title>{title}</title>
89-
<circle cx={16} cy={16} r={r} opacity={1 / 8} />
90-
<Box
91-
as="circle"
92-
{...(circleProps as {})}
93-
{...__internalProps({
94-
__css: __circleCss,
95-
})}
96-
/>
66+
<circle {...circleProps} opacity={1 / 8} />
67+
<circle {...circleProps} strokeDasharray="20 110">
68+
<animateTransform
69+
attributeName="transform"
70+
attributeType="XML"
71+
type="rotate"
72+
from="0 16 16"
73+
to="360 16 16"
74+
dur={`${duration}ms`}
75+
repeatCount="indefinite"
76+
/>
77+
</circle>
9778
</Box>
9879
)
9980
})

packages/components/test/__snapshots__/index.tsx.snap

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,22 +1329,6 @@ exports[`Slider renders 1`] = `
13291329
`;
13301330

13311331
exports[`Spinner renders 1`] = `
1332-
@keyframes animation-0 {
1333-
from {
1334-
-webkit-transform: rotate(0deg);
1335-
-moz-transform: rotate(0deg);
1336-
-ms-transform: rotate(0deg);
1337-
transform: rotate(0deg);
1338-
}
1339-
1340-
to {
1341-
-webkit-transform: rotate(360deg);
1342-
-moz-transform: rotate(360deg);
1343-
-ms-transform: rotate(360deg);
1344-
transform: rotate(360deg);
1345-
}
1346-
}
1347-
13481332
.emotion-0 {
13491333
box-sizing: border-box;
13501334
margin: 0;
@@ -1353,21 +1337,6 @@ exports[`Spinner renders 1`] = `
13531337
overflow: visible;
13541338
}
13551339
1356-
.emotion-1 {
1357-
box-sizing: border-box;
1358-
margin: 0;
1359-
min-width: 0;
1360-
transform-origin: 50% 50%;
1361-
-webkit-animation-name: animation-0;
1362-
animation-name: animation-0;
1363-
-webkit-animation-timing-function: linear;
1364-
animation-timing-function: linear;
1365-
-webkit-animation-duration: 500ms;
1366-
animation-duration: 500ms;
1367-
-webkit-animation-iteration-count: infinite;
1368-
animation-iteration-count: infinite;
1369-
}
1370-
13711340
<svg
13721341
className="emotion-0"
13731342
fill="none"
@@ -1379,22 +1348,34 @@ exports[`Spinner renders 1`] = `
13791348
width={48}
13801349
>
13811350
<title>
1382-
Loading...
1351+
Loading
13831352
</title>
13841353
<circle
13851354
cx={16}
13861355
cy={16}
1356+
fill="none"
13871357
opacity={0.125}
13881358
r={12}
1359+
strokeWidth={4}
13891360
/>
13901361
<circle
1391-
className="emotion-1"
13921362
cx={16}
13931363
cy={16}
1364+
fill="none"
13941365
r={12}
1395-
strokeDasharray={75.39822368615503}
1396-
strokeDashoffset={56.548667764616276}
1397-
/>
1366+
strokeDasharray="20 110"
1367+
strokeWidth={4}
1368+
>
1369+
<animateTransform
1370+
attributeName="transform"
1371+
attributeType="XML"
1372+
dur="750ms"
1373+
from="0 16 16"
1374+
repeatCount="indefinite"
1375+
to="360 16 16"
1376+
type="rotate"
1377+
/>
1378+
</circle>
13981379
</svg>
13991380
`;
14001381

packages/docs/src/pages/components/spinner.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ import { Spinner } from 'theme-ui'
1919
| Name | Type | Description |
2020
| ------------- | ------ | ------------------------------------------------ |
2121
| `title` | String | (default `'loading'`) Text for SVG `<title>` tag |
22-
| `size` | Number | (default `48`) chart diameter |
22+
| `size` | Number | (default `48`) indicator diameter |
2323
| `strokeWidth` | Number | (default `4`) stroke width |
24+
| `duration` | Number | (default `750`) duration of animation in ms |
2425

25-
A `title` attribute should be provided to the component for accessibility purposes.
26-
The element uses `role='img'` by default.
27-
Pass any overrides or additional attributes for the SVG element as props.
26+
A `title` attribute should be provided to the component for accessibility
27+
purposes. The element uses `role='img'` by default. Pass any overrides or
28+
additional attributes for the SVG element as props.
2829

2930
## Variants
3031

0 commit comments

Comments
 (0)