perf(backgrounds): animated chat backgrounds cost ~5% CPU instead of 50 to 70% (#337) #361
@@ -51,7 +51,7 @@ const dark: CSSProperties = {
|
|||||||
backgroundSize: '260% 240%, 300% 260%, 240% 280%, 220% 200%, 100% 100%, 100% 100%',
|
backgroundSize: '260% 240%, 300% 260%, 240% 280%, 220% 200%, 100% 100%, 100% 100%',
|
||||||
backgroundPosition: '0% 30%, 100% 70%, 50% 0%, 20% 80%, 50% 50%, 50% 50%',
|
backgroundPosition: '0% 30%, 100% 70%, 50% 0%, 20% 80%, 50% 50%, 50% 50%',
|
||||||
backgroundRepeat: 'no-repeat',
|
backgroundRepeat: 'no-repeat',
|
||||||
animation: `${auroraFlow} 60s ease-in-out infinite`,
|
animation: `${auroraFlow} 60s steps(180) infinite`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const light: CSSProperties = {
|
const light: CSSProperties = {
|
||||||
@@ -75,7 +75,7 @@ const light: CSSProperties = {
|
|||||||
backgroundSize: '260% 240%, 300% 260%, 240% 280%, 220% 200%, 100% 100%, 100% 100%',
|
backgroundSize: '260% 240%, 300% 260%, 240% 280%, 220% 200%, 100% 100%, 100% 100%',
|
||||||
backgroundPosition: '0% 30%, 100% 70%, 50% 0%, 20% 80%, 50% 50%, 50% 50%',
|
backgroundPosition: '0% 30%, 100% 70%, 50% 0%, 20% 80%, 50% 50%, 50% 50%',
|
||||||
backgroundRepeat: 'no-repeat',
|
backgroundRepeat: 'no-repeat',
|
||||||
animation: `${auroraFlow} 60s ease-in-out infinite`,
|
animation: `${auroraFlow} 60s steps(180) infinite`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const animAurora: ChatBgVariants = { dark, light };
|
export const animAurora: ChatBgVariants = { dark, light };
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const animFireflies: ChatBgVariants = {
|
|||||||
'0 0', // wash A (static)
|
'0 0', // wash A (static)
|
||||||
'0 0', // wash B (static)
|
'0 0', // wash B (static)
|
||||||
].join(','),
|
].join(','),
|
||||||
animation: `${firefliesDrift} 44s linear infinite`,
|
animation: `${firefliesDrift} 44s steps(528) infinite`,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Light: a cozy warm dim-dusk. No harsh dots on white — soft amber motes with
|
// Light: a cozy warm dim-dusk. No harsh dots on white — soft amber motes with
|
||||||
@@ -93,6 +93,6 @@ export const animFireflies: ChatBgVariants = {
|
|||||||
'0 0', // wash A (static)
|
'0 0', // wash A (static)
|
||||||
'0 0', // wash B (static)
|
'0 0', // wash B (static)
|
||||||
].join(','),
|
].join(','),
|
||||||
animation: `${firefliesDrift} 44s linear infinite`,
|
animation: `${firefliesDrift} 44s steps(528) infinite`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const animPulse: ChatBgVariants = {
|
|||||||
'0 0', // base wash
|
'0 0', // base wash
|
||||||
'0 0', // vignette
|
'0 0', // vignette
|
||||||
].join(','),
|
].join(','),
|
||||||
animation: `${gridPulse} 22s ease-in-out infinite`,
|
animation: `${gridPulse} 22s steps(528) infinite`,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Light: soft slate-blue grid on pale cool-white, a gentle luminance breathe.
|
// Light: soft slate-blue grid on pale cool-white, a gentle luminance breathe.
|
||||||
@@ -116,6 +116,6 @@ export const animPulse: ChatBgVariants = {
|
|||||||
'0 0', // base wash
|
'0 0', // base wash
|
||||||
'0 0', // vignette
|
'0 0', // vignette
|
||||||
].join(','),
|
].join(','),
|
||||||
animation: `${gridPulse} 22s ease-in-out infinite`,
|
animation: `${gridPulse} 22s steps(528) infinite`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const animRain: ChatBgVariants = {
|
|||||||
].join(','),
|
].join(','),
|
||||||
backgroundSize: ['260px 200px', '100% 100%', '100% 100%', '100% 100%'].join(','),
|
backgroundSize: ['260px 200px', '100% 100%', '100% 100%', '100% 100%'].join(','),
|
||||||
backgroundPosition: ['0 0', '0 0', '0 0', '0 0'].join(','),
|
backgroundPosition: ['0 0', '0 0', '0 0', '0 0'].join(','),
|
||||||
animation: `${rainFall} 12s linear infinite`,
|
animation: `${rainFall} 12s steps(288) infinite`,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Light: soft teal-grey streaks on a pale cool base — elegant, never neon.
|
// Light: soft teal-grey streaks on a pale cool base — elegant, never neon.
|
||||||
@@ -118,6 +118,6 @@ export const animRain: ChatBgVariants = {
|
|||||||
].join(','),
|
].join(','),
|
||||||
backgroundSize: ['260px 200px', '100% 100%', '100% 100%', '100% 100%'].join(','),
|
backgroundSize: ['260px 200px', '100% 100%', '100% 100%', '100% 100%'].join(','),
|
||||||
backgroundPosition: ['0 0', '0 0', '0 0', '0 0'].join(','),
|
backgroundPosition: ['0 0', '0 0', '0 0', '0 0'].join(','),
|
||||||
animation: `${rainFall} 12s linear infinite`,
|
animation: `${rainFall} 12s steps(288) infinite`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const animStars: ChatBgVariants = {
|
|||||||
'0 0', // nebula A
|
'0 0', // nebula A
|
||||||
'0 0', // nebula B
|
'0 0', // nebula B
|
||||||
].join(','),
|
].join(','),
|
||||||
animation: `${starDrift} 90s linear infinite`,
|
animation: `${starDrift} 90s steps(1080) infinite`,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Light: an airy pre-dawn sky. No literal white-on-white stars — instead very
|
// Light: an airy pre-dawn sky. No literal white-on-white stars — instead very
|
||||||
@@ -112,6 +112,6 @@ export const animStars: ChatBgVariants = {
|
|||||||
'0 0', // wash A
|
'0 0', // wash A
|
||||||
'0 0', // wash B
|
'0 0', // wash B
|
||||||
].join(','),
|
].join(','),
|
||||||
animation: `${starDrift} 100s linear infinite`,
|
animation: `${starDrift} 100s steps(1200) infinite`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -149,6 +149,11 @@ export const getChatBg = (
|
|||||||
}
|
}
|
||||||
// For animated backgrounds, promote the element to its own compositor layer so
|
// For animated backgrounds, promote the element to its own compositor layer so
|
||||||
// background-position keyframes don't trigger repaints on descendant elements.
|
// background-position keyframes don't trigger repaints on descendant elements.
|
||||||
|
// [Gitea #337] background-position can't run on the compositor, so every
|
||||||
|
// animation frame repaints the whole chat area (45 to 70% main-thread busy
|
||||||
|
// at 60 fps). The drifts are slow (3–22 px/s), so each animation uses steps():
|
||||||
|
// ~12 updates/s for the slow ones, ~24/s for rain and pulse: visually the
|
||||||
|
// same, a fraction of the repaints.
|
||||||
if (style.animation) {
|
if (style.animation) {
|
||||||
return { ...style, willChange: 'background-position', contain: 'paint' };
|
return { ...style, willChange: 'background-position', contain: 'paint' };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user