Skip to content

Commit 30292c2

Browse files
committed
Atom - PenAndPaper - Add strokeWidth and individual color per path
1 parent 559b1be commit 30292c2

File tree

1 file changed

+65
-18
lines changed

1 file changed

+65
-18
lines changed

src/atoms/PenAndPaper.vue

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const redoStack = ref([]);
3131
const viewBox = ref("0 0 0 0");
3232
3333
const currentColor = ref(props.color)
34+
const strokeWidth = ref(1);
3435
3536
const buttonBorderColor = computed(() => {
3637
return lightenHexColor(props.color, 0.6);
@@ -86,33 +87,51 @@ function startDrawing(event) {
8687
8788
function draw(event) {
8889
if (!isDrawing.value || !svgElement.value) return;
89-
9090
const { x, y } = getRelativePosition(event);
9191
currentPath.value += ` ${x} ${y}`;
9292
}
9393
9494
function smoothPath(path) {
9595
const segments = path.trim().split(/\s+/);
96-
if (segments.length < 4) return path;
97-
const smoothedPath = [segments[0], segments[1], segments[2]]; // Keep M x y incipit
98-
for (let i = 3; i < segments.length - 2; i += 2) {
99-
const x1 = parseFloat(segments[i - 2]);
100-
const y1 = parseFloat(segments[i - 1]);
101-
const x2 = parseFloat(segments[i]);
102-
const y2 = parseFloat(segments[i + 1]);
103-
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
104-
continue
105-
}
96+
if (segments.length < 4) {
97+
return path;
98+
}
99+
const coordinates = segments.slice(1).map(Number);
100+
if (coordinates.length % 2 !== 0) {
101+
return path;
102+
}
103+
const smoothedCoordinates = reduceNoise(coordinates);
104+
const smoothedPath = [`M ${smoothedCoordinates[0]} ${smoothedCoordinates[1]}`]; // Keep M x y incipit
105+
for (let i = 2; i < smoothedCoordinates.length - 2; i += 2) {
106+
const x1 = smoothedCoordinates[i - 2];
107+
const y1 = smoothedCoordinates[i - 1];
108+
const x2 = smoothedCoordinates[i];
109+
const y2 = smoothedCoordinates[i + 1];
106110
const controlX = (x1 + x2) / 2;
107111
const controlY = (y1 + y2) / 2;
108112
smoothedPath.push(`Q ${x1} ${y1} ${controlX} ${controlY}`);
109113
}
110-
const lastX = segments[segments.length - 2];
111-
const lastY = segments[segments.length - 1];
114+
const lastX = smoothedCoordinates[smoothedCoordinates.length - 2];
115+
const lastY = smoothedCoordinates[smoothedCoordinates.length - 1];
112116
smoothedPath.push(`L ${lastX} ${lastY}`);
113117
return smoothedPath.join(" ");
114118
}
115119
120+
function reduceNoise(coordinates, smoothingFactor = 1) {
121+
const smoothed = [...coordinates];
122+
for (let i = 2; i < coordinates.length - 2; i += 2) {
123+
const x = coordinates[i];
124+
const y = coordinates[i + 1];
125+
const prevX = coordinates[i - 2];
126+
const prevY = coordinates[i - 1];
127+
const nextX = coordinates[i + 2];
128+
const nextY = coordinates[i + 3];
129+
smoothed[i] = x + smoothingFactor * ((prevX + nextX) / 2 - x);
130+
smoothed[i + 1] = y + smoothingFactor * ((prevY + nextY) / 2 - y);
131+
}
132+
return smoothed;
133+
}
134+
116135
function optimizeSvgPath(path) {
117136
const commands = path.trim().split(/\s+/);
118137
let optimizedPath = '';
@@ -181,7 +200,11 @@ function optimizeSvgPath(path) {
181200
182201
function stopDrawing() {
183202
if (isDrawing.value) {
184-
stack.value.push(optimizeSvgPath(smoothPath(currentPath.value)));
203+
stack.value.push({
204+
strokeWidth: strokeWidth.value,
205+
path: currentPath.value,
206+
color: currentColor.value
207+
});
185208
redoStack.value = [];
186209
currentPath.value = "";
187210
}
@@ -228,6 +251,9 @@ function reset() {
228251
stack.value = [];
229252
redoStack.value = [];
230253
}
254+
255+
const range = ref(null);
256+
231257
</script>
232258

233259
<template>
@@ -299,6 +325,18 @@ function reset() {
299325
>
300326
<BaseIcon name="trash" :stroke="color"/>
301327
</button>
328+
<input
329+
ref="range"
330+
type="range"
331+
class="vertical-range"
332+
:min="0.5"
333+
:max="12"
334+
:step="0.1"
335+
v-model="strokeWidth"
336+
:style="{
337+
accentColor: color
338+
}"
339+
/>
302340
</div>
303341
<svg
304342
ref="svgElement"
@@ -316,8 +354,11 @@ function reset() {
316354
@touchmove.prevent="draw"
317355
@touchend="stopDrawing"
318356
>
319-
<path class="vue-ui-pen-and-paper-path" v-for="path in stack" :key="path" :d="path" :stroke="currentColor" fill="none" />
320-
<path class="vue-ui-pen-and-paper-path vue-ui-pen-and-paper-path-drawing" v-if="isDrawing" :d="currentPath" :stroke="currentColor" fill="none" />
357+
<template v-for="path in stack" :key="path">
358+
<circle v-if="path.path.replace('M', '').split(' ').length === 2" :cx="path.path.replace('M', '').split(' ')[0]" :cy="path.path.replace('M', '').split(' ')[1]" :r="path.strokeWidth / 2" :fill="path.color"/>
359+
<path v-else class="vue-ui-pen-and-paper-path" :d="path.path" :stroke="path.color" :stroke-width="path.strokeWidth" fill="none" />
360+
</template>
361+
<path class="vue-ui-pen-and-paper-path vue-ui-pen-and-paper-path-drawing" v-if="isDrawing" :d="currentPath" :stroke="currentColor" :stroke-width="strokeWidth * 1.1" fill="none" />
321362
</svg>
322363
</template>
323364

@@ -366,7 +407,13 @@ function reset() {
366407
stroke-linecap: round;
367408
stroke-linejoin: round;
368409
}
369-
.vue-ui-pen-and-paper-path-drawing {
370-
stroke-width: 2;
410+
411+
input[type="range"].vertical-range {
412+
writing-mode: vertical-lr;
413+
position: absolute;
414+
top: 50%;
415+
transform: translateY(-50%) rotate(180deg);
416+
left: 36px;
371417
}
418+
372419
</style>

0 commit comments

Comments
 (0)