Browse Source

ref(crons): Improve performance of timelinePlaceholder (#53906)

Uses a SVG mask with gradient, isntead of rndering a bunch of small
elements with animations
Evan Purkhiser 1 year ago
parent
commit
febbae7a85

+ 40 - 50
static/app/views/monitors/components/overviewTimeline/timelinePlaceholder.tsx

@@ -1,62 +1,52 @@
 import {keyframes} from '@emotion/react';
 import styled from '@emotion/styled';
 
-export function TimelinePlaceholder({count}: {count: number}) {
+import {space} from 'sentry/styles/space';
+
+const SPACING = 15;
+
+export function TimelinePlaceholder() {
   return (
-    <TimelinePlaceholderContainer>
-      {new Array(count).fill(null).map((_, i) => (
-        <PlaceholderTick
-          key={i}
-          style={{
-            left: `${(i * (100 / count)).toFixed(2)}%`,
-            animationDelay: `${(i / count).toFixed(2)}s`,
-          }}
-        />
-      ))}
-    </TimelinePlaceholderContainer>
+    <PlaceholderSvg xmlns="http://www.w3.org/2000/svg" width="100%" height="14px">
+      <defs>
+        <pattern
+          id="tick-pattern"
+          patternUnits="userSpaceOnUse"
+          width={SPACING + 4}
+          height={24}
+        >
+          <rect width="4px" height="14px" rx="2" fill="white" />
+        </pattern>
+        <mask id="pattern-mask">
+          <rect width="100%" height="100%" fill="url(#tick-pattern)" />
+        </mask>
+      </defs>
+      <foreignObject width="100%" height="14" mask="url(#pattern-mask)">
+        <AnimatedGradient />
+      </foreignObject>
+    </PlaceholderSvg>
   );
 }
 
-const TimelinePlaceholderContainer = styled('div')`
-  display: flex;
-  align-items: center;
-  position: relative;
-  height: 100%;
+const PlaceholderSvg = styled('svg')`
+  margin: ${space(0.5)} 0;
 `;
 
-const placeholderTickKeyframes = keyframes`
-  0% {
-    opacity: 0;
-    transform: scale(0.75) translateZ(0);
-    filter: blur(12px);
-  }
-  33.33% {
-    opacity: 1;
-    transform: scale(1) translateZ(0);
-    filter: blur(0px);
-  }
-  66.66% {
-    opacity: 1;
-    transform: scale(1) translateZ(0);
-    filter: blur(0px);
-  }
-  100% {
-    opacity: 0;
-    transform: scale(0.75) translateZ(0);
-    filter: blur(12px);
-  }
+const gradientAnimation = keyframes`
+    0%{ background-position-x: 0%; }
+    100%{ background-position-x: -200%; }
 `;
 
-const PlaceholderTick = styled('div')`
-  position: absolute;
-  margin-top: 1px;
-  background: ${p => p.theme.translucentBorder};
-  width: 4px;
-  height: 14px;
-  border-radius: 2px;
-
-  opacity: 0;
-  transform: scale(0.75) translateZ(0);
-  filter: blur(12px);
-  animation: ${placeholderTickKeyframes} 2s ease-out forwards infinite;
+const AnimatedGradient = styled('div')`
+  width: 100%;
+  height: 100%;
+  background-image: linear-gradient(
+    90deg,
+    ${p => p.theme.border} 0%,
+    rgba(255, 255, 255, 0) 20%,
+    rgba(255, 255, 255, 0) 80%,
+    ${p => p.theme.border} 100%
+  );
+  background-size: 200% 100%;
+  animation: ${gradientAnimation} 2s linear infinite;
 `;

+ 1 - 1
static/app/views/monitors/components/overviewTimeline/timelineTableRow.tsx

@@ -56,7 +56,7 @@ export function TimelineTableRow({monitor, bucketedData, ...timelineProps}: Prop
             <TimelineEnvOuterContainer key={name}>
               {!bucketedData ? (
                 <TimelineEnvContainer key="timeline">
-                  <TimelinePlaceholder count={Math.round(timelineProps.width / 20)} />
+                  <TimelinePlaceholder />
                 </TimelineEnvContainer>
               ) : (
                 <TimelineEnvContainer key="placeholder">