diff --git a/src/components/FloatPanel/index.vue b/src/components/FloatPanel/index.vue index 869a224..9eb6e21 100644 --- a/src/components/FloatPanel/index.vue +++ b/src/components/FloatPanel/index.vue @@ -94,29 +94,50 @@ export default { methods: { show(callback) { this.ensureParentPosition() - this.showPanel = true - this.callback = callback + // 使用 requestAnimationFrame 确保在下一帧渲染时显示,避免闪烁 + requestAnimationFrame(() => { + this.showPanel = true + this.callback = callback + }) }, hide() { this.showPanel = false this.callback = null - this.restoreParentPosition() + // 延迟恢复父容器定位,等待动画完成 + setTimeout(() => { + this.restoreParentPosition() + }, 300) }, ensureParentPosition() { - // 确保父容器有 position: relative - this.$nextTick(() => { - if (!this.parentElement && this.$el) { + // 确保父容器有 position: relative,提前设置避免闪烁 + if (!this.parentElement) { + // 在组件挂载后立即获取父元素 + if (this.$el && this.$el.parentElement) { this.parentElement = this.$el.parentElement + } else { + // 如果组件还未挂载,使用 $nextTick + this.$nextTick(() => { + if (this.$el && this.$el.parentElement) { + this.parentElement = this.$el.parentElement + } + this.setParentPosition() + }) + return } - if (this.parentElement) { - const computedStyle = window.getComputedStyle(this.parentElement) - const position = computedStyle.position - if (position === 'static' || !position) { - this.originalParentPosition = this.parentElement.style.position || '' - this.parentElement.style.position = 'relative' - } + } + this.setParentPosition() + }, + setParentPosition() { + if (this.parentElement) { + const computedStyle = window.getComputedStyle(this.parentElement) + const position = computedStyle.position + if (position === 'static' || !position) { + this.originalParentPosition = this.parentElement.style.position || '' + // 使用 will-change 优化性能 + this.parentElement.style.willChange = 'transform' + this.parentElement.style.position = 'relative' } - }) + } }, restoreParentPosition() { // 恢复父容器的原始定位 @@ -126,6 +147,7 @@ export default { } else { this.parentElement.style.position = '' } + this.parentElement.style.willChange = '' this.originalParentPosition = null } }, @@ -150,12 +172,23 @@ export default { } } }, + mounted() { + // 组件挂载后立即获取父元素并设置定位,避免后续闪烁 + this.$nextTick(() => { + if (this.$el && this.$el.parentElement) { + this.parentElement = this.$el.parentElement + // 预先设置 position,避免显示时闪烁 + this.setParentPosition() + } + }) + }, beforeDestroy() { // 组件销毁时关闭面板并清理 if (this.showPanel) { this.hide() + } else { + this.restoreParentPosition() } - this.restoreParentPosition() } } @@ -170,6 +203,11 @@ export default { z-index: 1000; width: 100%; height: 100%; + /* 使用 will-change 优化性能,避免重排 */ + will-change: transform; + /* 使用 transform 代替 position,避免触发重排 */ + transform: translateZ(0); + backface-visibility: hidden; } .float-panel { @@ -182,6 +220,10 @@ export default { display: flex; flex-direction: column; overflow: hidden; + /* 使用 GPU 加速,避免闪烁 */ + will-change: transform; + transform: translateZ(0); + backface-visibility: hidden; &.float-panel-right { animation: slideInRight 0.3s ease-out; @@ -242,12 +284,16 @@ export default { // 动画效果 .float-panel-enter-active, .float-panel-leave-active { - transition: opacity 0.3s; + transition: opacity 0.3s ease; + /* 使用 transform 代替 opacity,性能更好 */ + will-change: transform, opacity; } .float-panel-enter, .float-panel-leave-to { opacity: 0; + /* 使用 transform 避免重排 */ + transform: translateZ(0); } @keyframes slideInRight {