index2.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <style>
  9. #canvas-container {
  10. width: 500px;
  11. border: 2px solid black;
  12. background-color: white;
  13. }
  14. body {
  15. background-color: bisque;
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <div id="canvas-container">
  21. <canvas id="canvas" width="500" height="400"></canvas>
  22. </div>
  23. <div>
  24. <button id="undo">UNDO</button>
  25. <input type="color" id="color">
  26. <select id="tool">
  27. <option value="graffity">Graffity</option>
  28. <option value="circle">Circle</option>
  29. <option value="rectangle">Rectangle</option>
  30. <option value="line">Line</option>
  31. <option value="ellipse">Ellipse (huinya)</option>
  32. <option value="select">Select</option>
  33. </select>
  34. <input type="number" id="size" value="6">
  35. <button id="delete">Delete...</button>
  36. </div>
  37. <script>
  38. const canvas = document.getElementById('canvas')
  39. const ctx = canvas.getContext('2d')
  40. const width = canvas.width;
  41. const height = canvas.height;
  42. let current;
  43. let selection = []
  44. const tools = {
  45. graffity: {
  46. mousemove(e) { //e.buttons 0b00000x11 & 0b00000100 == x
  47. (e.buttons & 1) && new Circle(e.clientX, e.clientY, +size.value, color.value)
  48. }
  49. },
  50. circle: {
  51. mousedown(e) {
  52. current = new Circle(e.clientX, e.clientY, 1, color.value)
  53. },
  54. mousemove(e) {
  55. if (!current) return;
  56. current.radius = current.distanceTo(e.clientX, e.clientY)
  57. Drawable.drawAll()
  58. },
  59. mouseup(e) {
  60. current = null
  61. }
  62. },
  63. rectangle: {
  64. mousedown(e) {
  65. current = new Rectangle(e.clientX, e.clientY, 0, 0, color.value, +size.value)
  66. },
  67. mousemove(e) {
  68. if (!current) return;
  69. current.width = e.clientX - current.x
  70. current.height = e.clientY - current.y
  71. Drawable.drawAll()
  72. },
  73. mouseup(e) {
  74. current = null
  75. }
  76. },
  77. line: {
  78. mousedown(e) {
  79. current = new Line(e.clientX, e.clientY, 0, 0, color.value, +size.value)
  80. },
  81. mousemove(e) {
  82. if (!current) return;
  83. current.width = e.clientX - current.x
  84. current.height = e.clientY - current.y
  85. Drawable.drawAll()
  86. },
  87. mouseup(e) {
  88. current = null
  89. }
  90. },
  91. ellipse: {
  92. mousedown(e) {
  93. current = new Ellipse(e.clientX, e.clientY, 1, 1, color.value)
  94. },
  95. mousemove(e) {
  96. if (!current) return;
  97. current.radiusY = current.distanceTo(e.clientX, e.clientY)
  98. current.radiusX = current.distanceTo(e.clientY, e.clientX)
  99. Drawable.drawAll()
  100. },
  101. mouseup(e) {
  102. current = null
  103. }
  104. },
  105. select: {
  106. click(e) {
  107. console.log(e)
  108. let found = Drawable.instances.filter(c => c.in && c.in(e.clientX, e.clientY))
  109. if (found.length) {
  110. if (e.ctrlKey) {
  111. selection.push(found.pop())
  112. }
  113. else {
  114. selection = [found.pop()]
  115. }
  116. }
  117. else {
  118. if (!e.ctrlKey) selection = []
  119. }
  120. Drawable.drawAll(selection)
  121. },
  122. mousedown(e) {
  123. },
  124. mousemove(e) {
  125. },
  126. mouseup(e) {
  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. ////
  142. function Drawable() {
  143. Drawable.addInstance(this);
  144. }
  145. const distance = (x1, y1, x2, y2) => ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
  146. Drawable.prototype.draw = function () { };
  147. Drawable.prototype.distanceTo = function (x, y) {
  148. if (typeof this.x !== 'number' ||
  149. typeof this.y !== 'number') {
  150. return NaN
  151. }
  152. return distance(this.x, this.y, x, y)
  153. };
  154. Drawable.instances = [];
  155. Drawable.addInstance = function (item) {
  156. Drawable.instances.push(item);
  157. }
  158. Drawable.drawAll = function (selection = []) {
  159. ctx.clearRect(0, 0, width, height);
  160. Drawable.forAll(item => item.draw())
  161. selection.forEach(item => item.draw(true))
  162. }
  163. Drawable.forAll = function (callback) {
  164. for (var i = 0; i < Drawable.instances.length; i++) {
  165. callback(Drawable.instances[i])
  166. }
  167. }
  168. class Circle extends Drawable {
  169. constructor(x, y, radius, color) {
  170. super()
  171. this.x = x;
  172. this.y = y;
  173. this.radius = radius;
  174. this.color = color;
  175. this.draw();
  176. }
  177. draw(selected) {
  178. //debugger;
  179. ctx.beginPath();
  180. ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
  181. ctx.closePath();
  182. ctx.fillStyle = this.color;
  183. if (selected) {
  184. ctx.lineWidth = 2
  185. ctx.stroke();
  186. }
  187. ctx.fill();
  188. }
  189. in(x, y) {
  190. return this.distanceTo(x, y) < this.radius
  191. }
  192. inBounds(x, y, w, h) { // x = 100, this.x = 102, w = 5
  193. return this.x >= x && this.x <= x + w &&
  194. this.y >= y && this.y <= y + h
  195. }
  196. }
  197. class Rectangle extends Drawable {
  198. constructor(x, y, width, height, color, lineWidth) {
  199. super()
  200. this.x = x;
  201. this.y = y;
  202. this.width = width;
  203. this.height = height;
  204. this.color = color;
  205. this.lineWidth = lineWidth;
  206. this.draw();
  207. }
  208. draw() {
  209. ctx.beginPath();
  210. ctx.rect(this.x, this.y, this.width, this.height)
  211. ctx.fill();
  212. }
  213. }
  214. class Line extends Drawable {
  215. constructor(x, y, width, height, color, lineWidth) {
  216. super()
  217. this.x = x;
  218. this.y = y;
  219. this.width = width;
  220. this.height = height;
  221. this.color = color;
  222. this.lineWidth = lineWidth;
  223. this.draw();
  224. }
  225. draw() {
  226. ctx.beginPath();
  227. //ctx.rect(this.x,this.y, this.width, this.height)
  228. ctx.moveTo(this.x, this.y);
  229. ctx.lineTo(this.x + this.width, this.y + this.height);
  230. ctx.closePath();
  231. ctx.strokeStyle = this.color;
  232. ctx.lineWidth = this.lineWidth
  233. ctx.stroke();
  234. }
  235. }
  236. color.onchange = () => {
  237. selection.forEach(c => c.color = color.value)
  238. Drawable.drawAll(selection)
  239. }
  240. document.getElementById('delete').onclick = () => {
  241. Drawable.instances = Drawable.instances.filter(item => !selection.includes(item))
  242. selection = []
  243. Drawable.drawAll()
  244. }
  245. class Ellipse extends Drawable {
  246. constructor(x, y, radiusX, radiusY, color) {
  247. super()
  248. this.x = x;
  249. this.y = y;
  250. this.radiusX = radiusX;
  251. this.radiusY = radiusY;
  252. this.color = color;
  253. this.draw();
  254. }
  255. // draw() {
  256. // ctx.beginPath();
  257. // ctx.ellipse(this.x, this.y, this.radius, 75, Math.PI / 4, 0, 2 * Math.PI);
  258. // ctx.stroke();
  259. // }
  260. draw(selected) {
  261. //debugger;
  262. ctx.beginPath();
  263. ctx.ellipse(this.x, this.y, this.radiusX, this.radiusY, Math.PI / 4, 0, 2 * Math.PI);
  264. //ctx.strokeStyle = this.color;
  265. ctx.closePath();
  266. if (selected) {
  267. ctx.lineWidth = 2
  268. ctx.stroke();
  269. }
  270. ctx.stroke();
  271. }
  272. in(x, y) {
  273. return this.distanceTo(x, y) < this.radius
  274. }
  275. inBounds(x, y, w, h) { // x = 100, this.x = 102, w = 5
  276. return this.x >= x && this.x <= x + w &&
  277. this.y >= y && this.y <= y + h
  278. }
  279. }
  280. color.onchange = () => {
  281. selection.forEach(c => c.color = color.value)
  282. Drawable.drawAll(selection)
  283. }
  284. document.getElementById('delete').onclick = () => {
  285. Drawable.instances = Drawable.instances.filter(item => !selection.includes(item))
  286. selection = []
  287. Drawable.drawAll()
  288. }
  289. undo.onclick = function () {
  290. Drawable.instances.pop()
  291. //Drawable.instances = []
  292. Drawable.drawAll()
  293. }
  294. </script>
  295. </body>
  296. </html>