-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
110 lines (92 loc) · 2.95 KB
/
script.js
File metadata and controls
110 lines (92 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
const canvas = document.getElementById('editorCanvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
let sourcePoint = null;
let isDrawing = false;
let history = [];
// Configuration
const brushSize = document.getElementById('brushSize');
const opacity = document.getElementById('opacity');
const hardness = document.getElementById('hardness');
// Image Upload
document.getElementById('upload').addEventListener('change', (e) => {
const reader = new FileReader();
reader.onload = (event) => {
const img = new Image();
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
saveState();
};
img.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
});
function saveState() {
if (history.length > 10) history.shift();
history.push(canvas.toDataURL());
}
canvas.addEventListener('mousedown', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (e.altKey) {
sourcePoint = { x, y };
return;
}
if (sourcePoint) {
isDrawing = true;
paint(x, y);
}
});
window.addEventListener('mouseup', () => {
if (isDrawing) saveState();
isDrawing = false;
});
canvas.addEventListener('mousemove', (e) => {
if (!isDrawing || !sourcePoint) return;
const rect = canvas.getBoundingClientRect();
paint(e.clientX - rect.left, e.clientY - rect.top);
});
function paint(destX, destY) {
const offsetX = destX - sourcePoint.x;
const offsetY = destY - sourcePoint.y;
const size = parseInt(brushSize.value);
const alpha = opacity.value / 100;
const hard = hardness.value / 100;
ctx.save();
// Create a circular clipping path for the brush
ctx.beginPath();
ctx.arc(destX, destY, size / 2, 0, Math.PI * 2);
ctx.clip();
// Global Alpha for opacity
ctx.globalAlpha = alpha;
// Draw the source area shifted to the destination
// We draw the canvas itself onto itself with an offset
ctx.drawImage(canvas,
destX - offsetX - size/2, destY - offsetY - size/2, size, size,
destX - size/2, destY - size/2, size, size
);
ctx.restore();
// Feathering (Simulated by a soft shadow/blur if hardness < 1)
if (hard < 1) {
ctx.filter = `blur(${(1 - hard) * 5}px)`;
}
}
document.getElementById('undoBtn').onclick = () => {
if (history.length > 1) {
history.pop();
const img = new Image();
img.onload = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
};
img.src = history[history.length - 1];
}
};
document.getElementById('downloadBtn').onclick = () => {
const link = document.createElement('a');
link.download = 'edited-image.png';
link.href = canvas.toDataURL();
link.click();
};