const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') const width = canvas.width; const height = canvas.height; let current; let selection = [] const tools = { graffity: { mousemove(e){ //e.buttons 0b00000x11 & 0b00000100 == x (e.buttons & 1) && new Circle(e.clientX, e.clientY, +size.value, color.value) } }, circle: { mousedown(e){ current = new Circle(e.clientX,e.clientY, 1, color.value) }, mousemove(e){ if (!current) return; current.radius = current.distanceTo(e.clientX, e.clientY) Drawable.drawAll() }, mouseup(e){ current = null } }, line: { mousedown(e){ current = new Line(e.clientX, e.clientY, 0, 0, color.value, +size.value) }, mousemove(e){ if (!current) return; current.width = e.clientX - current.x current.height = e.clientY - current.y Drawable.drawAll() }, mouseup(e){ current = null } }, rectangle: { mousedown(e){ current = new Rectangle(e.clientX, e.clientY, 0, 0, color.value) }, mousemove(e){ if (!current) return; current.width = e.clientX - current.x current.height = e.clientY - current.y Drawable.drawAll() }, mouseup(e){ current = null } }, ellipse: { mousedown(e){ current = new Ellipse(e.clientX, e.clientY, 0, 0, color.value) }, mousemove(e){ if (!current) return; current.width = e.clientX - current.x current.height = e.clientY - current.y Drawable.drawAll() }, mouseup(e){ current = null } }, select: { click(e){ console.log(e) let found = Drawable.instances.filter(c => c.in && c.in(e.clientX, e.clientY)) if (found.length){ if (e.ctrlKey){ selection.push(found.pop()) } else { selection = [found.pop()] } } else { if (!e.ctrlKey) selection = [] } Drawable.drawAll(selection) }, mousedown(e){ // }, mousemove(e){ }, mouseup(e){ //x,y, w, h прямоугольника //selection - только те элеменеты Drawable.instances которые в границах прямоугольника. }, } } function superHandler(evt){ let t = tools[tool.value] if (typeof t[evt.type] === 'function') t[evt.type].call(this, evt) } canvas.onmousemove = superHandler canvas.onmouseup = superHandler canvas.onmousedown = superHandler canvas.onclick = superHandler //// function Drawable(){ Drawable.addInstance(this); } const distance = (x1,y1, x2, y2) => ((x1-x2)**2 + (y1-y2)**2)**0.5 Drawable.prototype.draw = function(){}; Drawable.prototype.distanceTo = function(x,y){ if (typeof this.x !== 'number' || typeof this.y !== 'number'){ return NaN } return distance(this.x, this.y, x, y) }; Drawable.instances = []; Drawable.addInstance = function(item){ Drawable.instances.push(item); } Drawable.drawAll = function(selection=[]){ ctx.clearRect(0,0,width,height); Drawable.forAll(item => item.draw()) selection.forEach(item => item.draw(true)) } Drawable.forAll = function(callback){ for(var i = 0; i= x && this.x <= x + w && this.y >= y && this.y <= y + h } } class Line extends Drawable { constructor(x,y, width, height, color, lineWidth){ super() this.x = x; this.y = y; this.width = width; this.height = height; this.color = color; this.lineWidth = lineWidth; this.draw(); } get length() { return this.distanceTo(this.x + this.width, this.y + this.height) } draw(selected){ ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.lineTo(this.x + this.width, this.y + this.height); ctx.closePath(); ctx.strokeStyle = this.color; ctx.lineWidth = this.lineWidth ctx.stroke(); } in(x,y) { let rotationAngleAlph = Math.atan2(this.height, this.width) //console.log('raznici', y - this.y, x - this.x) let mouseAngleBeta = Math.atan2(y - this.y, x - this.x) let angleDiff = mouseAngleBeta - rotationAngleAlph //x on 0 do dlinna y ot 0 do width //let newX = Math.cos(angleDiff) * this.distanceTo(x,y) let newX = Math.cos(rotationAngleAlph) * this.distanceTo(x,y) //let newY = Math.sin(angleDiff) * this.distanceTo(x,y) let newY = Math.sin(mouseAngleBeta) * this.distanceTo(x,y) console.log('ot nachala do kincza X', newX) console.log('dist . length', this.distanceTo(x,y) / this.length) console.log('ot nachala do kincza Y', newY) console.log(angleDiff, rotationAngleAlph, mouseAngleBeta) console.log(this.lineWidth) console.log('v line x', 0 <= newX && newX <= this.distanceTo(x,y) / this.length ) console.log('v line y', ( -this.lineWidth/2 <= newY && newY <= this.lineWidth/2 )) return ( 0 >= newX && newX <= (this.distanceTo(x,y)/this.length) ) && ( -this.lineWidth/2 >= newY && newY <= this.lineWidth/2 ) } } color.onchange = () => { selection.forEach(c => c.color = color.value) Drawable.drawAll(selection) } document.getElementById('delete').onclick = () =>{ Drawable.instances = Drawable.instances.filter(item => !selection.includes(item)) selection = [] Drawable.drawAll() } class Rectangle extends Drawable { constructor(x, y, width, height, color) { super() this.x = x; this.y = y; this.width = width; this.height = height; this.color = color; this.draw(); } draw(selected){ ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.lineTo(this.x + this.width, this.y + this.height); ctx.closePath(); ctx.strokeStyle = this.color; ctx.fillStyle = this.color; ctx.fillRect(this.x, this.y, this.width, this.height) ctx.stroke() if (selected){ ctx.beginPath(); ctx.rect(this.x, this.y, this.width, this.height) ctx.lineWidth = 2 ctx.strokeStyle = 'black' ctx.closePath(); ctx.stroke() } ctx.fill() } in(x,y){ if((x > this.x && x < this.x + this.width) && (y > this.y && y < this.y + this.height)) { return true } return false } } class Ellipse extends Drawable { constructor(x,y, width, height, color){ super() this.x = x; this.y = y; this.width = width; this.height = height; this.color = color; this.draw(); } get rx() { return this.width / 2 } get ry() { return this.height / 2 } get h() { return this.x + this.rx } get k() { return this.y + this.ry } draw(selected){ ctx.beginPath(); ctx.fillStyle = this.color; ctx.ellipse( this.h, this.k, this.rx > 0? this.rx : -(this.rx), this.ry > 0? this.ry : -(this.ry), 0, 0, Math.PI * 2 ) if (selected){ ctx.beginPath(); ctx.ellipse( this.h, this.k, this.rx > 0? this.rx : -(this.rx), this.ry > 0? this.ry : -(this.ry), 0, 0, Math.PI * 2 ) ctx.lineWidth = 2 ctx.strokeStyle = 'black' ctx.closePath(); ctx.stroke() } ctx.fill() } in(x,y){ return (((x - this.h)**2) / ((this.rx)**2)) + (((y - this.k)**2) / ((this.ry)**2)) <= 1 } }