main.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. const canvas = document.getElementById('canvas')
  2. const ctx = canvas.getContext('2d')
  3. const width = canvas.width;
  4. const height = canvas.height;
  5. let current;
  6. let selection = []
  7. const tools = {
  8. graffity: {
  9. mousemove(e){
  10. (e.buttons & 1) && new Circle(e.clientX, e.clientY, +size.value, color.value)
  11. }
  12. },
  13. circle: {
  14. mousedown(e){
  15. current = new Circle(e.clientX,e.clientY, 1, color.value)
  16. },
  17. mousemove(e){
  18. if (!current) return;
  19. current.radius = current.distanceTo(e.clientX, e.clientY)
  20. Drawable.drawAll()
  21. },
  22. mouseup(e){
  23. current = null
  24. }
  25. },
  26. line: {
  27. mousedown(e){
  28. current = new Line(e.clientX, e.clientY, 0, 0, color.value, +size.value)
  29. },
  30. mousemove(e){
  31. if (!current) return;
  32. current.width = e.clientX - current.x
  33. current.height = e.clientY - current.y
  34. Drawable.drawAll()
  35. },
  36. mouseup(e){
  37. current = null
  38. }
  39. },
  40. rectangle: {
  41. mousedown(e){
  42. current = new Rectangle(e.clientX, e.clientY, 0, 0, color.value)
  43. },
  44. mousemove(e){
  45. if (!current) return;
  46. current.width = e.clientX - current.x
  47. current.height = e.clientY - current.y
  48. Drawable.drawAll()
  49. },
  50. mouseup(e){
  51. current = null
  52. }
  53. },
  54. ellipse: {
  55. mousedown(e) {
  56. current = new Ellipse(e.clientX, e.clientY, 0, 0, color.value)
  57. },
  58. mousemove(e) {
  59. if (!current) return;
  60. resultX = e.clientX - current.x;
  61. resultY = e.clientY - current.y;
  62. current.radiusX = resultX > 0 ? resultX : -resultX;
  63. current.radiusY = resultY > 0 ? resultY : - resultY;
  64. Drawable.drawAll()
  65. },
  66. mouseup(e) {
  67. current = null
  68. }
  69. },
  70. square: {
  71. mousedown(e){
  72. current = new Square(e.clientX, e.clientY, 0, color.value)
  73. },
  74. mousemove(e){
  75. if (!current) return;
  76. current.width = e.clientX - current.x
  77. Drawable.drawAll()
  78. },
  79. mouseup(e){
  80. current = null
  81. }
  82. },
  83. triangle: {
  84. mousedown(e){
  85. current = new Triangle(e.clientX, e.clientY, color.value)
  86. },
  87. mousemove(e){
  88. if (!current) return;
  89. current.oneX = e.clientX;
  90. current.oneY = e.clientY;
  91. current.twoX = current.x - (current.oneX - current.x);
  92. current.twoY = e.clientY;
  93. Drawable.drawAll()
  94. },
  95. mouseup(e){
  96. current = null
  97. }
  98. },
  99. select: {
  100. click(e){
  101. console.log(e)
  102. let found = Drawable.instances.filter(c => c.in && c.in(e.clientX, e.clientY))
  103. if (found.length){
  104. if (e.ctrlKey){
  105. selection.push(found.pop())
  106. }
  107. else {
  108. selection = [found.pop()]
  109. }
  110. }
  111. else {
  112. if (!e.ctrlKey) selection = []
  113. }
  114. Drawable.drawAll(selection)
  115. },
  116. mousedown(e){
  117. current = new Rectangle(e.clientX, e.clientY, 0, 0, color.value)
  118. },
  119. mousemove(e){
  120. if (!current) return;
  121. current.width = e.clientX - current.x
  122. current.height = e.clientY - current.y
  123. Drawable.drawAll()
  124. },
  125. mouseup(e){
  126. current = null // пока не додумала как обратиться внутрь поля выделения
  127. //x,y, w, h прямоугольника
  128. //selection - только те элеменеты Drawable.instances которые в границах прямоугольника.
  129. },
  130. }
  131. }
  132. function superHandler(evt){
  133. let t = tools[tool.value]
  134. if (typeof t[evt.type] === 'function')
  135. t[evt.type].call(this, evt)
  136. }
  137. canvas.onmousemove = superHandler
  138. canvas.onmouseup = superHandler
  139. canvas.onmousedown = superHandler
  140. canvas.onclick = superHandler
  141. function Drawable(){
  142. Drawable.addInstance(this);
  143. }
  144. const distance = (x1,y1, x2, y2) => ((x1-x2)**2 + (y1-y2)**2)**0.5
  145. Drawable.prototype.draw = function(){};
  146. Drawable.prototype.distanceTo = function(x,y){
  147. if (typeof this.x !== 'number' ||
  148. typeof this.y !== 'number'){
  149. return NaN
  150. }
  151. return distance(this.x, this.y, x, y)
  152. }
  153. Drawable.instances = [];
  154. Drawable.addInstance = function(item){
  155. Drawable.instances.push(item);
  156. }
  157. Drawable.drawAll = function(selection=[]){
  158. ctx.clearRect(0,0,width,height);
  159. Drawable.forAll(item => item.draw())
  160. selection.forEach(item => item.draw(true))
  161. }
  162. Drawable.forAll = function(callback){
  163. for(var i = 0; i<Drawable.instances.length;i++){
  164. callback(Drawable.instances[i])
  165. }
  166. }
  167. class Circle extends Drawable {
  168. constructor(x, y, radius, color){
  169. super()
  170. this.x = x;
  171. this.y = y;
  172. this.radius = radius;
  173. this.color = color;
  174. this.draw();
  175. }
  176. draw(){
  177. ctx.beginPath();
  178. ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
  179. ctx.closePath();
  180. ctx.fillStyle = this.color;
  181. ctx.fill(); // рисует закрашенный circle
  182. // ctx.stroke(); // рисует границы circle
  183. }
  184. in(x,y){
  185. return this.distanceTo(x,y) < this.radius
  186. }
  187. inBounds(x,y,w,h){
  188. return this.x >= x && this.x <= x + w &&
  189. this.y >= y && this.y <= y + h
  190. }
  191. }
  192. class Line extends Drawable {
  193. constructor(x,y, width, height, color, lineWidth){
  194. super()
  195. this.x = x;
  196. this.y = y;
  197. this.width = width;
  198. this.height = height;
  199. this.color = color;
  200. this.lineWidth = lineWidth;
  201. this.draw();
  202. }
  203. draw(){
  204. ctx.beginPath();
  205. ctx.moveTo(this.x, this.y);
  206. ctx.lineTo(this.x + this.width, this.y + this.height);
  207. ctx.closePath();
  208. ctx.strokeStyle = this.color;
  209. ctx.lineWidth = this.lineWidth
  210. ctx.stroke();
  211. }
  212. }
  213. class Rectangle extends Drawable {
  214. constructor(x, y, width, height, color){
  215. super()
  216. this.x = x;
  217. this.y = y;
  218. this.width = width;
  219. this.height = height;
  220. this.color = color;
  221. this.draw();
  222. }
  223. draw(){
  224. ctx.beginPath();
  225. ctx.strokeRect(this.x, this.y, this.width, this.height); // рисует границы rectangle
  226. // ctx.fillRect(this.x, this.y, this.width, this.height); // рисует закрашенный rectangle
  227. ctx.fillStyle = this.color;
  228. ctx.fill();
  229. }
  230. }
  231. class Ellipse extends Drawable {
  232. constructor(x, y, radiusX, radiusY, color) {
  233. super()
  234. this.x = x;
  235. this.y = y;
  236. this.radiusX = radiusX;
  237. this.radiusY = radiusY;
  238. this.color = color;
  239. this.draw();
  240. }
  241. draw() {
  242. ctx.beginPath();
  243. ctx.ellipse(this.x, this.y, this.radiusX, this.radiusY, 0, 0, 2 * Math.PI);
  244. ctx.closePath();
  245. ctx.fillStyle = this.color;
  246. // ctx.fill(); // рисует закрашенный ellipse
  247. ctx.stroke(); // рисует границы ellipse
  248. }
  249. }
  250. class Square extends Drawable {
  251. constructor(x, y, width, color){
  252. super()
  253. this.x = x;
  254. this.y = y;
  255. this.width = width;
  256. this.color = color;
  257. this.draw();
  258. }
  259. draw(){
  260. ctx.beginPath();
  261. ctx.strokeRect(this.x, this.y, this.width, this.width); // рисует границы square
  262. // ctx.fillRect(this.x, this.y, this.width, this.width); // рисует закрашенный square
  263. ctx.fillStyle = this.color;
  264. ctx.fill();
  265. }
  266. }
  267. class Triangle extends Drawable {
  268. constructor(x, y, color){
  269. super()
  270. this.x = x;
  271. this.y = y;
  272. this.oneX = x;
  273. this.oneY = y;
  274. this.twoX = x;
  275. this.twoY = y;
  276. this.color = color;
  277. this.draw();
  278. }
  279. draw(){
  280. ctx.beginPath();
  281. ctx.moveTo(this.x, this.y);
  282. ctx.lineTo(this.twoX, this.twoY);
  283. ctx.lineTo(this.oneX, this.oneY);
  284. ctx.closePath();
  285. // ctx.stroke(); // рисует границы triangle
  286. ctx.fill(); // рисует закрашенный triangle
  287. ctx.fillStyle = this.color;
  288. }
  289. }
  290. color.onchange = () => {
  291. selection.forEach(c => c.color = color.value)
  292. Drawable.drawAll(selection)
  293. }
  294. document.getElementById('delete').onclick = () =>{
  295. Drawable.instances = Drawable.instances.filter(item => !selection.includes(item))
  296. selection = []
  297. Drawable.instances = []; // очищение массива полностью
  298. Drawable.drawAll()
  299. }
  300. undo.onclick = function () {
  301. Drawable.instances.pop(); // удаление последнего элемента в массиве
  302. Drawable.drawAll()
  303. }