Files
test-repo/docs/.vitepress/theme/components/appendix/browser-rendering-pipeline/PaintLayerDemo.vue
T
sanbuphy 0eba9e87e9 fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier)
- Relaxed strict Vue/JS rules for demo code compatibility
- Fix syntax errors in ApiPlayground and VoiceCloningDemo
- Fix duplicate else-if condition in ApiPlayground
- Fix Promise executor async pattern in AutoregressiveAudioDemo
- Add TypeScript file support to ESLint config

Warnings reduced from 295 to 251 problems.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 17:38:10 +08:00

349 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="paint-layer-demo">
<div class="demo-header">
<span class="icon">🎨</span>
<span class="title">绘制层优化</span>
<span class="subtitle">浏览器如何通过分层提升性能</span>
</div>
<div class="demo-content">
<div class="layer-visualization">
<div class="layers-container">
<div
v-for="(layer, index) in layers"
:key="layer.id"
class="layer"
:class="{ active: layer.isActive, promoted: layer.isPromoted }"
:style="{ zIndex: index }"
>
<div class="layer-header">
<span class="layer-icon">{{ layer.icon }}</span>
<span class="layer-name">{{ layer.name }}</span>
<span
v-if="layer.isPromoted"
class="promoted-badge"
>GPU层</span>
</div>
<div class="layer-content">
<div
v-if="layer.id === 'background'"
class="background-box"
/>
<div
v-if="layer.id === 'card'"
class="card-box"
>
<div class="card-title">
卡片
</div>
</div>
<div
v-if="layer.id === 'button'"
class="button-box"
>
按钮
</div>
</div>
</div>
</div>
</div>
<div class="properties-panel">
<div class="panel-title">
触发新层的 CSS 属性
</div>
<div class="property-list">
<div
v-for="prop in promotedProperties"
:key="prop.name"
class="property-item"
@mouseenter="highlightLayer(prop.layerId)"
@mouseleave="clearHighlight"
>
<code class="property-code">{{ prop.code }}</code>
<span class="property-desc">{{ prop.desc }}</span>
</div>
</div>
</div>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心要点</strong>浏览器把需要动画的元素提升到独立的 GPU 这样动画时只需要调整位置和透明度不需要重绘但不要滥用每个层都会占用 GPU 内存
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const layers = ref([
{
id: 'background',
name: '背景层',
icon: '🖼️',
isActive: false,
isPromoted: false
},
{
id: 'card',
name: '内容层',
icon: '📄',
isActive: false,
isPromoted: false
},
{
id: 'button',
name: '动画层',
icon: '✨',
isActive: false,
isPromoted: true
}
])
const promotedProperties = [
{
name: '3D变换',
code: 'transform: translate3d(0,0,0)',
desc: '任何3D变换都会创建新层',
layerId: 'button'
},
{
name: '透明度动画',
code: 'opacity',
desc: '配合transition使用时',
layerId: 'button'
},
{
name: '固定定位',
code: 'position: fixed',
desc: '固定定位元素需要独立层',
layerId: 'button'
},
{
name: 'Will-change',
code: 'will-change: transform',
desc: '显式提示浏览器创建层',
layerId: 'button'
}
]
function highlightLayer(layerId) {
layers.value.forEach(layer => {
layer.isActive = layer.id === layerId
})
}
function clearHighlight() {
layers.value.forEach(layer => {
layer.isActive = false
})
}
</script>
<style scoped>
.paint-layer-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
.demo-header .icon { font-size: 1.25rem; }
.demo-header .title { font-weight: bold; font-size: 1rem; }
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.85rem; margin-left: 0.5rem; }
.demo-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 0.75rem;
}
.layer-visualization {
margin-bottom: 1rem;
}
.layers-container {
position: relative;
height: 200px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 0.75rem;
overflow: hidden;
}
.layer {
position: absolute;
width: calc(100% - 2rem);
height: calc(100% - 2rem);
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 6px;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.layer:nth-child(1) {
top: 10px;
left: 10px;
transform: translate(0, 0);
}
.layer:nth-child(2) {
top: 20px;
left: 20px;
transform: translate(10px, 10px);
}
.layer:nth-child(3) {
top: 30px;
left: 30px;
transform: translate(20px, 20px);
}
.layer.active {
border-color: var(--vp-c-brand);
box-shadow: 0 0 0 4px rgba(64, 158, 255, 0.2);
z-index: 100;
}
.layer.promoted {
border-color: var(--vp-c-success);
}
.layer-header {
position: absolute;
top: 0.5rem;
left: 0.5rem;
display: flex;
align-items: center;
gap: 0.4rem;
}
.layer-icon {
font-size: 1rem;
}
.layer-name {
font-size: 0.8rem;
font-weight: 500;
color: var(--vp-c-text-2);
}
.promoted-badge {
font-size: 0.7rem;
padding: 0.1rem 0.3rem;
background: var(--vp-c-success);
color: white;
border-radius: 3px;
}
.layer-content {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.background-box {
width: 80%;
height: 60%;
background: var(--vp-c-bg-soft);
border-radius: 4px;
}
.card-box {
width: 120px;
height: 80px;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 0.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-title {
font-size: 0.8rem;
color: var(--vp-c-text-2);
text-align: center;
}
.button-box {
padding: 0.5rem 1rem;
background: var(--vp-c-brand);
color: white;
border-radius: 4px;
font-size: 0.85rem;
}
.properties-panel {
padding-top: 1rem;
border-top: 1px solid var(--vp-c-divider);
}
.panel-title {
font-size: 0.9rem;
font-weight: 500;
color: var(--vp-c-text-1);
margin-bottom: 0.75rem;
}
.property-list {
display: grid;
gap: 0.5rem;
}
.property-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
.property-item:hover {
background: var(--vp-c-bg-alt);
}
.property-code {
font-family: 'Courier New', monospace;
font-size: 0.8rem;
padding: 0.2rem 0.4rem;
background: var(--vp-c-bg-alt);
border-radius: 3px;
color: var(--vp-c-brand-1);
}
.property-desc {
font-size: 0.85rem;
color: var(--vp-c-text-2);
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
margin-top: 1rem;
}
.info-box .icon { margin-right: 0.25rem; }
</style>