main.js 11 KB

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