fix(VOverlay): promote to GPU compositing layer to prevent flicker on WebKit#22800
fix(VOverlay): promote to GPU compositing layer to prevent flicker on WebKit#22800e1berd wants to merge 4 commits into
Conversation
|
I should be able to reproduce it with this basic example, right? What am I missing? |
indeed, my initial example turned out to be not quite working. It seems <template>
<v-app>
<v-main>
<v-container>
<v-dialog max-width="600">
<template #activator="{ props }">
<v-btn v-bind="props">Settings-like dialog</v-btn>
</template>
<v-card height="540" style="overflow: hidden">
<div style="display: flex; height: 100%">
<div
style="
width: 180px;
flex-shrink: 0;
background: color-mix(in srgb, rgb(var(--v-theme-surface-variant)) 20%, transparent);
"
>
<div class="pa-4 text-subtitle-2 font-weight-bold" style="opacity: 0.6">
Settings
</div>
<v-list density="compact" nav class="px-2">
<v-list-item
v-for="i in 5"
:key="i"
:title="`Section ${i}`"
:prepend-icon="'mdi-cog'"
rounded="lg"
:active="i === 1"
color="primary"
/>
</v-list>
</div>
<v-divider vertical />
<div style="flex: 1; display: flex; flex-direction: column">
<div class="d-flex align-center justify-space-between px-5 pt-4 pb-3">
<span class="text-subtitle-1 font-weight-medium">Section 1</span>
</div>
<v-divider />
<div class="pa-4">
<v-text-field label="Username" variant="outlined" density="compact" class="mb-2" />
<v-textarea label="Bio" variant="outlined" density="compact" rows="3" />
<div style="display: flex; flex-wrap: wrap; gap: 8px" class="mt-2">
<button
v-for="color in colors"
:key="color"
class="swatch"
:style="{ backgroundColor: color }"
/>
</div>
</div>
</div>
</div>
</v-card>
</v-dialog>
<v-dialog max-width="400" class="mt-4">
<template #activator="{ props }">
<v-btn v-bind="props" class="ml-4">Profile-like dialog</v-btn>
</template>
<v-card rounded="xl" style="overflow: hidden">
<div
style="
height: 96px;
background: linear-gradient(
135deg,
rgb(var(--v-theme-primary)),
color-mix(in srgb, rgb(var(--v-theme-primary)) 60%, rgb(var(--v-theme-secondary)))
);
"
/>
<div style="padding-left: 16px; margin-top: -42px">
<v-avatar size="84" color="primary" style="border: 4px solid rgb(var(--v-theme-surface))">
<span class="text-h5 font-weight-medium">U</span>
</v-avatar>
</div>
<v-card-text>
<div class="text-h6 font-weight-bold">Username</div>
<div class="text-body-2 text-on-surface-variant">user@email.com</div>
<v-divider class="my-3" />
<div class="text-body-2">Bio text goes here</div>
</v-card-text>
<v-divider />
<v-card-actions class="pa-3">
<v-btn block variant="tonal" color="primary">Action</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</v-main>
</v-app>
</template>
<script setup lang="ts">
const colors = [
'#5c6bc0', '#1976d2', '#0288d1', '#00897b',
'#388e3c', '#7b1fa2', '#e91e63', '#f57c00',
'#d32f2f', '#546e7a', '#795548', '#607d8b',
]
</script>
<style>
.swatch {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
padding: 0;
transition: transform 0.12s ease;
}
.swatch:hover {
transform: scale(1.15);
}
</style>And likewise, if you add: .v-overlay {
will-change: transform;
}the bug gets fixed and everything appears to be working fine |
|
i wanted add that I'm also experiencing the same flickering issue on these other selectors:
in my case i was able to completely solve the problem by using the following styles: .v-overlay {
will-change: transform;
.v-btn__overlay,
.v-list-item__overlay,
.v-chip__overlay,
.v-field__overlay {
will-change: opacity;
}
}this approach works very well for me and eliminates the flicker on all mentioned elements |
|
Is there a difference if it is running in dev mode? If not, then I suspect specific hardware or host OS can also have an impact here. Regardless, the suggested fix is about adding a simple hint for the rendering engine, so it won't hurt to add. Let's just make sure we cover as much as possible now, so you don't have to post new PR on a weekly basis. |
issue is clearly visible on WebKitGTK (Tauri on Linux) |
Description
When any
v-overlay-based component (v-dialog,v-menu, etc.) is open, rapidly moving the cursor causes the overlay to flicker — for a single frame (~16ms) the content disappears and the page behind the overlay becomes visible.bug_proof.webm
Fixes the issue by adding
transform: translateZ(0)to.v-overlay, which promotes it to a dedicated GPU compositing layer.Root cause:
.v-overlay__scrimand.v-overlay__contentare sibling elements with no GPU layer on their parent. During fast cursor movement, WebKit performs hit-testing on everymousemoveevent, which triggers compositing layer invalidation. Since neither child has its own GPU texture, the browser re-uploads them on each invalidation. During this re-upload, one child can be absent for exactly one frame — producing the visible flicker.With
transform: translateZ(0)on.v-overlay, both children share a single GPU texture. There is no inter-layer synchronization and no re-upload on cursor movement.Why this doesn't break
position: fixedchildren:When a parent has
transform, itsposition: fixedchildren are positioned relative to it instead of the viewport. This is safe here because.v-overlayitself is alreadyposition: fixedcovering the full viewport (top: 0; right: 0; bottom: 0; left: 0), so.v-overlay__scrimstill covers the full screen.Affected environments: Linux (Tauri / WebKitGTK), potentially Safari on macOS/iOS. Chromium-based browsers are unaffected because their compositor promotes layers more aggressively by default.
Markup:
My configuration where the issue is reproducible: