Add interactive layer management to Editor

- Click layer to toggle visibility and redraw canvas
- Click ▲/▼ buttons to reorder layers (z-index)
- Active layer highlighted with left red border
- Each layer now actually renders: bg/white, border/frame, pattern/suit symbol, text/corner labels
- Fix fabric.js imports (Canvas, FabricText, Rect)
This commit is contained in:
Poker Design Developer
2026-05-31 22:13:38 +08:00
parent 670ac0a917
commit 888b787d1e
2 changed files with 80 additions and 29 deletions

View File

@@ -1,43 +1,43 @@
{
"hash": "8c4b68ba",
"configHash": "34f6f529",
"hash": "76c4bd08",
"configHash": "3c200fce",
"lockfileHash": "99e89a35",
"browserHash": "6a02d3bf",
"browserHash": "df8b7ff8",
"optimized": {
"axios": {
"src": "../../axios/index.js",
"file": "axios.js",
"fileHash": "9022993e",
"fileHash": "a369977c",
"needsInterop": false
},
"element-plus": {
"src": "../../element-plus/es/index.mjs",
"file": "element-plus.js",
"fileHash": "ed152bc7",
"fileHash": "0a2f9603",
"needsInterop": false
},
"fabric": {
"src": "../../fabric/dist/index.min.mjs",
"file": "fabric.js",
"fileHash": "14f1f947",
"fileHash": "a1369625",
"needsInterop": false
},
"pinia": {
"src": "../../pinia/dist/pinia.mjs",
"file": "pinia.js",
"fileHash": "ecda6472",
"fileHash": "20bc890b",
"needsInterop": false
},
"vue-router": {
"src": "../../vue-router/dist/vue-router.mjs",
"file": "vue-router.js",
"fileHash": "4a9d54f8",
"fileHash": "017f450e",
"needsInterop": false
},
"vue": {
"src": "../../vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "4a46bbb6",
"fileHash": "70ae02ae",
"needsInterop": false
}
},

View File

@@ -23,9 +23,17 @@
</div>
<h3 style="margin: 0 0 15px 0; font-size: 14px; color: #888;">图层管理</h3>
<div v-for="l in layers" :key="l.id" style="padding: 10px; margin-bottom: 5px; background: #16213e; border-radius: 4px; display: flex; align-items: center; gap: 10px; cursor: pointer;">
<span>{{ l.visible ? '👁' : '—' }}</span>
<span style="font-size: 13px;">{{ l.name }}</span>
<div v-for="l in layers" :key="l.id"
@click="toggleLayer(l)"
:style="{ padding: '10px 12px', marginBottom: '5px', background: activeLayer === l.id ? '#16213e' : '#0f3460', borderRadius: '4px', display: 'flex', alignItems: 'center', gap: '10px', cursor: 'pointer', borderLeft: activeLayer === l.id ? '3px solid #e94560' : '3px solid transparent', transition: 'all 0.2s' }">
<span style="width: 20px; text-align: center;">{{ l.visible ? '👁' : '—' }}</span>
<span style="font-size: 13px; flex: 1;">{{ l.name }}</span>
<span style="display: flex; flex-direction: column; gap: 2px;">
<button @click.stop="moveLayer(l, -1)" :disabled="l.zIndex <= 0"
:style="{ border: 'none', background: 'transparent', color: l.zIndex <= 0 ? '#333' : '#888', cursor: l.zIndex <= 0 ? 'default' : 'pointer', fontSize: '10px', padding: '0' }"></button>
<button @click.stop="moveLayer(l, 1)" :disabled="l.zIndex >= layers.length - 1"
:style="{ border: 'none', background: 'transparent', color: l.zIndex >= layers.length - 1 ? '#333' : '#888', cursor: l.zIndex >= layers.length - 1 ? 'default' : 'pointer', fontSize: '10px', padding: '0' }"></button>
</span>
</div>
</aside>
@@ -49,7 +57,7 @@
<script setup>
import { ref, onMounted, computed, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Canvas, FabricText } from 'fabric'
import { Canvas, FabricText, Rect } from 'fabric'
import axios from 'axios'
const route = useRoute()
@@ -60,6 +68,7 @@ const pname = ref('')
const currentSuit = ref('spade')
const currentCard = ref('spade-A')
const fabricCanvas = ref(null)
const activeLayer = ref('bg')
const projectId = computed(() => route.params.projectId)
const suits = ['spade', 'heart', 'club', 'diamond']
@@ -76,12 +85,12 @@ const currentCards = computed(() => {
return cards
})
const layers = [
{ id: 'bg', name: '背景层', visible: true },
{ id: 'border', name: '边框层', visible: true },
{ id: 'pattern', name: '图案层', visible: true },
{ id: 'text', name: '文字层', visible: true }
]
const layers = ref([
{ id: 'bg', name: '背景层', visible: true, zIndex: 0 },
{ id: 'border', name: '边框层', visible: true, zIndex: 1 },
{ id: 'pattern', name: '图案层', visible: true, zIndex: 2 },
{ id: 'text', name: '文字层', visible: true, zIndex: 3 }
])
function getSymbol(suit) {
return { spade: '♠', heart: '♥', club: '♣', diamond: '♦' }[suit] || ''
@@ -91,6 +100,23 @@ function isRed(suit) {
return suit === 'heart' || suit === 'diamond'
}
function toggleLayer(layer) {
activeLayer.value = layer.id
layer.visible = !layer.visible
drawCard()
}
function moveLayer(layer, delta) {
const newZ = layer.zIndex + delta
const swapped = layers.value.find(l => l.zIndex === newZ)
if (swapped) {
swapped.zIndex = layer.zIndex
layer.zIndex = newZ
layers.value.sort((a, b) => a.zIndex - b.zIndex)
drawCard()
}
}
onMounted(async () => {
if (projectId.value) {
try {
@@ -109,7 +135,7 @@ function initCanvas() {
fabricCanvas.value = new Canvas('main-canvas', {
width: 400,
height: 560,
backgroundColor: '#ffffff'
backgroundColor: '#f5f5f5'
})
drawCard()
}
@@ -119,22 +145,47 @@ function drawCard() {
if (!c) return
c.clear()
c.backgroundColor = '#ffffff'
const sortedLayers = [...layers.value].sort((a, b) => a.zIndex - b.zIndex)
const suit = currentSuit.value
const rank = currentCard.value.split('-')[1] || 'A'
const sym = getSymbol(suit)
const color = isRed(suit) ? '#FF0000' : '#000000'
const cardW = 400
const cardH = 560
const topText = new fabric.FabricText(`${rank}${sym}`, {
left: 30, top: 25, fontSize: 36, fill: color, selectable: true
})
c.add(topText)
for (const layer of sortedLayers) {
if (!layer.visible) continue
const center = new fabric.FabricText(sym, {
left: 200, top: 280, fontSize: 80, fill: color, selectable: true, originX: 'center', originY: 'center'
})
c.add(center)
if (layer.id === 'bg') {
const bg = new Rect({ left: 0, top: 0, width: cardW, height: cardH, fill: '#ffffff', selectable: false })
c.add(bg)
}
if (layer.id === 'border') {
const frame = new Rect({ left: 10, top: 10, width: cardW - 20, height: cardH - 20, fill: 'transparent', stroke: '#333', strokeWidth: 2, selectable: false })
c.add(frame)
}
if (layer.id === 'pattern') {
const center = new FabricText(sym, {
left: cardW / 2, top: cardH / 2, fontSize: 80, fill: color, selectable: true, originX: 'center', originY: 'center'
})
c.add(center)
}
if (layer.id === 'text') {
const topLabel = new FabricText(`${rank}${sym}`, {
left: 18, top: 16, fontSize: 32, fill: color, selectable: true
})
c.add(topLabel)
const bottomLabel = new FabricText(`${sym}${rank}`, {
left: cardW - 18, top: cardH - 16, fontSize: 32, fill: color, selectable: true, originX: 'right', originY: 'bottom'
})
c.add(bottomLabel)
}
}
c.renderAll()
}