index.js 7.7 KB

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