返回 slidev
DrawingControls.vue
根目录 / packages / client / internals / DrawingControls.vue
1 <script setup lang="ts">
2 import { Dropdown } from 'floating-vue'
3 import { ref } from 'vue'
4 import { useDrawings } from '../composables/useDrawings'
5 import Draggable from './Draggable.vue'
6 import IconButton from './IconButton.vue'
7 import VerticalDivider from './VerticalDivider.vue'
8
9 const {
10 brush,
11 canClear,
12 canRedo,
13 canUndo,
14 clear,
15 drauu,
16 drawingEnabled,
17 drawingMode,
18 drawingPinned,
19 brushColors,
20 } = useDrawings()
21
22 const strokeWidthDropdownShown = ref(false)
23
24 function showStrokeWidthDropdown(event: Event) {
25 event.preventDefault()
26 event.stopPropagation()
27 requestAnimationFrame(() => {
28 requestAnimationFrame(() => {
29 strokeWidthDropdownShown.value = true
30 })
31 })
32 }
33
34 function showStrokeWidthDropdownForPointer(event: PointerEvent) {
35 if (event.pointerType !== 'mouse')
36 showStrokeWidthDropdown(event)
37 }
38
39 function undo() {
40 drauu.undo()
41 }
42 function redo() {
43 drauu.redo()
44 }
45
46 let lastDrawingMode: typeof drawingMode.value = 'stylus'
47 function setDrawingMode(mode: typeof drawingMode.value) {
48 drawingMode.value = mode
49 drawingEnabled.value = true
50 if (mode !== 'eraseLine')
51 lastDrawingMode = mode
52 }
53 function setBrushColor(color: string) {
54 brush.value.color = color
55 drawingEnabled.value = true
56 drawingMode.value = lastDrawingMode
57 }
58 </script>
59
60 <template>
61 <Draggable
62 v-if="drawingEnabled || drawingPinned"
63 class="flex flex-wrap text-xl p-2 gap-1 rounded-md bg-main shadow transition-opacity duration-200 z-nav border border-main"
64 :class="!drawingEnabled && drawingPinned ? 'opacity-40 hover:opacity-90' : ''"
65 storage-key="slidev-drawing-pos"
66 :initial-x="10"
67 :initial-y="10"
68 >
69 <IconButton title="Draw with stylus" :class="{ shallow: drawingMode !== 'stylus' }" @click="setDrawingMode('stylus')">
70 <div class="i-carbon:pen" />
71 </IconButton>
72 <IconButton title="Draw a line" :class="{ shallow: drawingMode !== 'line' }" @click="setDrawingMode('line')">
73 <svg width="1em" height="1em" class="-mt-0.5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
74 <path d="M21.71 3.29a1 1 0 0 0-1.42 0l-18 18a1 1 0 0 0 0 1.42a1 1 0 0 0 1.42 0l18-18a1 1 0 0 0 0-1.42z" fill="currentColor" />
75 </svg>
76 </IconButton>
77 <IconButton title="Draw an arrow" :class="{ shallow: drawingMode !== 'arrow' }" @click="setDrawingMode('arrow')">
78 <div class="i-carbon:arrow-up-right" />
79 </IconButton>
80 <IconButton title="Draw an ellipse" :class="{ shallow: drawingMode !== 'ellipse' }" @click="setDrawingMode('ellipse')">
81 <div class="i-carbon:radio-button" />
82 </IconButton>
83 <IconButton title="Draw a rectangle" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
84 <div class="i-carbon:checkbox" />
85 </IconButton>
86 <IconButton title="Erase" :class="{ shallow: drawingMode !== 'eraseLine' }" @click="setDrawingMode('eraseLine')">
87 <div class="i-carbon:erase" />
88 </IconButton>
89
90 <VerticalDivider />
91
92 <Dropdown v-model:shown="strokeWidthDropdownShown" :triggers="[]">
93 <IconButton title="Adjust stroke width" :class="{ shallow: drawingMode === 'eraseLine' }" @click="showStrokeWidthDropdown" @pointerdown="showStrokeWidthDropdownForPointer" @pointerup="showStrokeWidthDropdownForPointer" @touchend="showStrokeWidthDropdown">
94 <svg viewBox="0 0 32 32" width="1.2em" height="1.2em">
95 <line x1="2" y1="15" x2="22" y2="4" stroke="currentColor" stroke-width="1" stroke-linecap="round" />
96 <line x1="2" y1="24" x2="28" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
97 <line x1="7" y1="31" x2="29" y2="19" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
98 </svg>
99 </IconButton>
100 <template #popper>
101 <div class="flex bg-main p-2">
102 <div class="inline-block w-7 text-center">
103 {{ brush.size }}
104 </div>
105 <div class="pt-.5">
106 <input v-model="brush.size" type="range" min="1" max="15" @change="drawingMode = lastDrawingMode">
107 </div>
108 </div>
109 </template>
110 </Dropdown>
111 <IconButton
112 v-for="color of brushColors"
113 :key="color"
114 title="Set brush color"
115 :class="brush.color === color && drawingMode !== 'eraseLine' ? 'active' : 'shallow'"
116 @click="setBrushColor(color)"
117 >
118 <div
119 class="w-6 h-6 transition-all transform border"
120 :class="brush.color !== color ? 'rounded-1/2 scale-85 border-white' : 'rounded-md border-gray-300/50'"
121 :style="drawingEnabled ? { background: color } : { borderColor: color }"
122 />
123 </IconButton>
124
125 <VerticalDivider />
126
127 <IconButton title="Undo" :class="{ disabled: !canUndo }" @click="undo()">
128 <div class="i-carbon:undo" />
129 </IconButton>
130 <IconButton title="Redo" :class="{ disabled: !canRedo }" @click="redo()">
131 <div class="i-carbon:redo" />
132 </IconButton>
133 <IconButton title="Delete" :class="{ disabled: !canClear }" @click="clear()">
134 <div class="i-carbon:trash-can" />
135 </IconButton>
136
137 <VerticalDivider />
138 <IconButton :title="drawingPinned ? 'Unpin drawing' : 'Pin drawing'" :class="{ shallow: !drawingPinned }" @click="drawingPinned = !drawingPinned">
139 <div v-show="drawingPinned" class="i-carbon:pin-filled transform -rotate-45" />
140 <div v-show="!drawingPinned" class="i-carbon:pin" />
141 </IconButton>
142 <IconButton
143 v-if="drawingEnabled"
144 :title="drawingPinned ? 'Drawing pinned' : 'Drawing unpinned'"
145 :class="{ shallow: !drawingEnabled }"
146 @click="drawingEnabled = !drawingEnabled"
147 >
148 <div v-show="drawingPinned" class="i-carbon:error" />
149 <div v-show="!drawingPinned" class="i-carbon:close-outline" />
150 </IconButton>
151 </Draggable>
152 </template>
153
154 <style>
155 .v-popper--theme-dropdown .v-popper__arrow-inner {
156 --uno: border-main;
157 }
158 </style>
159
159 lines Plain Text