I need your advice, I have a html with canvas, in this html you can add circles and then link these to make a figure, I also want to insert a search where you can find the different figures and focussed on it, so I don't know if it'll be better : to overlay the figures or use different canvas for each figure.
(I'm not implement the search function yet.)
What do you think?
Here is the functions that makes de draws
//this function puts the circles on a <table>, and then the other function takes the img coordinates and link it making a figure.
function position(year, mon)
{
$('#' + year + ' .' + mon).prepend('<img class="black_point" src="./images/circle.png"/>');
}
var table = document.getElementById("tabla");
var images = table.getElementsByTagName("img");
var canvas = document.getElementById("miCanvas");
var ctx = canvas.getContext("2d");
var x,y; // Remember coordinates
canvas.width = table.offsetWidth;
canvas.height = table.offsetHeight;
function connect(image, index) { //this function link the images
var tabBcr = table.getBoundingClientRect();
var imgBcr = image.getBoundingClientRect();
x = imgBcr.left + (imgBcr.width / 2) - tabBcr.left;
y = imgBcr.top + (imgBcr.height / 2) - tabBcr.top;
index === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
}
//this function add a new canvas
function figure(){
/*$('#miCanvas').prepend('<canvas id="myCanvas">Su navegador no soporta Canvas.</canvas>');
//this is the property who overlay the figures
cxt.globalCompositeOperation="source-over";/*
//which it will be better to implement a search function?
}
// new path here
ctx.beginPath();
for(var i=0; i<images.length; i++){
connect( images[i], i); // provide index so we can sep. move/line
}
// then at the end:
ctx.fill();
Use 1 html canvas to hold all your connected circles.
This simplifies the event handling when focusing / blurring your figures.
You can test if the mouse is inside one of your circles like this:
// given a circle defined by centerX, centerY, radius
var dx = mouseX - centerX;
var dy = mouseY - centerY;
var isInside = dx*dx+dy*dy <= radius*radius;
Here's an outline of how to focus on a figure:
Create a javascript object defining each circle. If a set of objects makes up a figure, then add a group property to each circle-object representing which group that circle is a member of.
Put all your circle-objects in an array.
In your mouse event handlers, iterate through the circle-objects-array and find which circle is under the mouse. That circle's group has been focused.
Related
Have a class with methods that draws rectangular shapes with random lengths.
However, is unable to only do rotate() on the shapes without translating ( translate() ), which translate will make the shapes draw off the canvas.
So are there anyways to make it so no translation occurs while rotating?
The code:
class rect {
constructor(range) {
this.boundary = 100;
this.x = random(this.boundary, width - this.boundary);
this.y = random(this.boundary, height - this.boundary);
this.xu = this.x + random(50, 200);
this.yu = this.y + random(50, 200);
this.range = range;
this.limit = random(-range, range);
this.rand_color1 = random(255);
this.rand_color2 = random(255);
this.rand_color3 = random(255);
}
custom_shapes() {
// how to make no translations occur while only perform rotation on shapes?
translate(this.x-this.margin,this.y-this.margin);
rotate(30);
fill(this.rand_color1, this.rand_color2, this.rand_color3)
quad(this.x, this.y, this.xu + this.limit, this.y, this.xu, this.yu, this.x, this.yu + this.limit);
}
}
If you mean that your rectangular is going of the screen when rotating, it's rotating around x = 0, y= 0 point, so i guess you could do something like:
push() //push and pop acts as a way to "seperate" any style and translate and so on...
rectMode(CENTER) // basically the middle of the rect = x , y
translate(this.x,this.y) // **OR** translate(this.x - this.rectSizeX / 2, this.y - this.rectSizeY / 2)
//quad() // if you're not using the rectMode()
pop() // also you'll have to fill() and so on in here i believe, not too sure
also if you know it's allways going to be a long or tall square, you can just use rect(x,y,xSize,ySize) // if think it's the size anyways
If you just want to separate translate() in general, just put push() and pop() around it...
Oh yeah and translate() basically just makes whatever x and y you give it into 0,0... Dunno if i said that already i'm just editing this the next day.
I've created the grid below using a canvas as well as lines going through it, how would one go about making this grid clickable?
The ideal result would be when I click one of the boxes it turns yellow, and a value inside an array would be changed from 0 to 1. I'm fairly certain I know how to set up a 2D array but that is the general idea. Thanks for any help.
var cellCount=20;
var currentGrid = new Array(cellCount).fill().map(() => new Array(cellCount).fill(0));
var nextGrid = new Array(cellCount).fill().map(() => new Array(cellCount).fill(0));
for(var i=0;i<20;i++){
for(var j=0;j<20;j++){
currentGrid[i][j]=0;
nextGrid[i][j]=0;
}
}
var canvas=document.getElementById('grid');
var ctx=canvas.getContext('2d');
function drawGrid(h,w,id){
for(var x=0;x<w;x++){
ctx.moveTo(0,x*20);
ctx.lineTo(h,x*20);
ctx.lineWidth=1;
ctx.strokeStyle='rgb(211,211,211)';
ctx.stroke();
}
for(var y=0;y<h;y++){
ctx.moveTo(y*20,0);
ctx.lineTo(y*20,w);
ctx.lineWidth=1;
ctx.strokeStyle='rgb(211,211,211)';
ctx.stroke();
}
}
function getPosition(event)
{
if (event.x != undefined && event.y != undefined)
{
x = event.x;
y = event.y;
}
}
canvas.addEventListener("mousedown", getPosition, false);
drawGrid(421,421,'grid');
Looking at the code you provided, it appears there is no loop, meaning there will be no other updates to the grid visually. Also, the draw function appears that it would draw offscreen, as you are multiplying your values by 20, which should probably be cellCount.
First I would recommend setting up a 'game loop'. A pattern that involves updating and drawing in a loop:
function loop(tick) {
update(tick);
draw(tick);
requestAnimationFrame(loop);
}
// start the loop
requestAnimationFrame(loop);
Then you'll need to define the update and draw function. In this example you may not have anything to update yet, but you might in the future. The draw function would first erase the background and then draw the grid:
function draw(tick) {
// clear background
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw the grid
drawGrid(numCols, numRows);
}
Now you'll need to define numCols and numRows which is simply the width or height divided by the cellSize. This tells you how many cells in the height and width of the canvas. I see you have cellCount instead of cellSize, but cellSize can be calculated the opposite way by taking canvas height (or width) and dividing it by cellCount (assuming the canvas is square).
var numCols = Math.floor(canvas.width / cellSize);
var numRows = Math.floor(canvas.height / cellSize);
As for the click handler; you'll want to figure out which cell they clicked in by dividing the x and y you've captured by the cellSize.
// assuming x and y are the coordinates on the canvas where the user clicked
var cellX = Math.floor(x / cellSize);
var cellY = Math.floor(y / cellSize);
// now set the cell
nextGrid[cellY][cellX] = 1;
Aim of my code:
Draw a small rectangle on a HTML canvas whenever a user clicks the canvas. The rectangle should have a small number representing the number of rectangles made by the user.
The user should be able to connect any two rectangles using a straight line. (Preferably by just pressing left mouse button, and taking the mouse from first rectangle to second rectangle)
Approach and my attempt
As you can see in this jsFiddle , I have been able to achieve the first part of above very well. On clicking on the canvas, a rectangle with a number inside of it is made. But I am really clueless about the second part.
How do I make the user connect any two made rectangles? I want the connection to be made only if a rectangle is there ( So I would need to store coordinates of every rectangle that has been made, that's okay as I can use an array for that ).
Basically, I just want to check if the mousedown was at one place and mouseup at the other.
How do I get these two different coordinates ( one of mousedown and other of mouseup ) , and draw a line between them?
I have given the Fiddle above but still here's my jquery:
$(function () {
var x, y;
var globalCounter = 0;
$('#mycanvas').on("click", function (event) {
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
x -= mycanvas.offsetLeft;
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
y -= mycanvas.offsetLeft;
// alert("x:"+x+"y: "+y);
drawRectangle(x, y);
});
function drawRectangle(x, y) {
var acanvas = document.getElementById("mycanvas");
var context = acanvas.getContext("2d");
context.strokeRect(x, y, 25, 25);
globalCounter++;
writeNo(x, y, globalCounter);
}
function writeNo(x, y, n) {
var acanvas = document.getElementById("mycanvas");
var context = acanvas.getContext("2d");
context.font = "bold 14px sans-serif";
context.fillText(n, x + 8, y + 12.5);
}
});
The main question is therefore: connecting the two made rectangles by mousedrag
How do I achieve this?
Thank You.
How about this: http://jsfiddle.net/4jqptynt/4/
Ok, first I did a little refactoring for your code to make things easier. Just stuff like putting the code that gets the canvas coordinates into it's own function, and caching some variables (like the canvas context) in the outer function's scope. Oh, and defining your rectangle dimensions as constants because we'll be using the same numbers in a couple of different places.
As you said, the first thing we need is to keep track of the existing rectangles using an array rects (easy enough to do within drawRectangle). Then we need a function to check if a particular pair of coordinates are within some rectangle:
function inRectangle(x, y) {
for (var i = 0, l = rects.length; i < l; i++) {
if ((x - rects[i].x) <= RECT_X && (y - rects[i].y) <= RECT_Y &&
(x - rects[i].x) >= 0 && (y - rects[i].y) >= 0) {
return i;
}
}
}
where RECT_X & RECT_Y define the sides of the rectangle. If the coordinates do exist within some rectangle then this will return the index of that rectangle within the rects array.
Then it's a case of checking whether or not a mousedown occurred within a rectangle, noting that inRectangle will only return a number if the mousedown event was within a rectangle:
$acanvas.on("mousedown", function (event) {
var coords = getCoords(event),
rect = inRectangle(coords.x, coords.y);
if (typeof rect === "number") {
dragStart = rect + 1;
} else {
drawRectangle(coords.x, coords.y);
}
});
if so, make a note of which rectangle using dragStart, if not draw a rectangle as before.
Then to complete the drag, we need to attach a handler to mouseup:
$acanvas.on("mouseup", function (event) {
if (!dragStart) { return; }
var coords = getCoords(event),
rect = inRectangle(coords.x, coords.y);
if (typeof rect === "number") {
drawConnection(dragStart - 1, rect);
}
dragStart = 0;
});
If no drag was started, then it does nothing. If it's coordinates aren't within a rectangle, then it does nothing but reset dragStart. If however, it is within a rectangle, then it draws a connecting line:
function drawConnection(rect1, rect2) {
context.strokeStyle = "black";
context.lineWidth = 1;
context.beginPath();
context.moveTo(rects[rect1].x + RECT_X/2, rects[rect1].y + RECT_Y/2);
context.lineTo(rects[rect2].x + RECT_X/2, rects[rect2].y + RECT_Y/2);
context.stroke();
context.closePath();
}
So i want to make a line follow mouse and on click to draw that line, i need this for drawing polygons. My idea was to draw a line every time mouse moves but then it makes mess with a lot of lines, so i decided to draw over old lines after mouse moves with white lines to clean up and so that there would be only one line that goes from a point of last clicked spot to current mouse location.My jsFiddle for this. but it doesn't work the way i want yes i draws polygons on clicking but there is no line following the mouse so i can't see what line I'm drawing. I don't see where is it wrong ? Maybe there is some ready solution that i didn't find ? Mycode :
var polygonX = [];
var polygonY = [];
var lineReady = 0;
var whileLineX;
var whiteLineY;
$("#body").bind('mousemove', function (ev) {
if (lineReady >= 2) {
var context;
//clear old lines
if (whiteLineX !== null && whiteLineY !== null) {
context = $("#canvas")[0].getContext('2d');
context.moveTo(polygonX[lineReady - 1], polygonY[lineReady - 1]);
context.lineTo(whiteLineX, whiteLineY);
context.strokeStyle = '#ffffff';
context.stroke();
}
//draw a new line
context = $("#canvas")[0].getContext('2d');
var offset = $('#body').offset();
var x = ev.clientX - offset.left;
var y = ev.clientY - offset.top;
context.beginPath();
context.moveTo(polygonX[lineReady - 1], polygonY[lineReady - 1]);
context.strokeStyle = '#000000';
context.lineTo(x, y);
context.stroke();
whileLineX = x;
whiteLineY = y;
}
});
$("#body").bind('click', function (ev) {
var offset = $('#body').offset();
var x = ev.clientX - offset.left;
var y = ev.clientY - offset.top;
polygonX
.push(x);
polygonY.push(y);
lineReady++;
if (lineReady >= 2) {
var context = $("#canvas")[0].getContext('2d');
context.beginPath();
context.moveTo(polygonX[lineReady - 2], polygonY[lineReady - 2]);
context.lineTo(polygonX[lineReady - 1], polygonY[lineReady - 1]);
context.stroke();
}
});`
The best way to do this is to use a bit of animation.
Everytime you draw a line, push its coordinates (first point and last point) in an array. Then draw your array at every animation loop(check out this link which will explain you how to animate)
. Then you'll want to draw a single line, say colored in red, from the last point of the last line of the array where you're pushing lines to your mouse position.
Doing it this way will allow you to have a red line following your mouse at all times, giving you a "preview" of a line.
Algo-wise it would look like:
var arrayOflines = [];
canvas.onclick ->
var coordinates = mouseposition()
arrayOfLines.push(coordinates)
function mouseposition(){
returns x and y of your mouse on the canvas
}
function draw(array){
loop through array
draw line from array[0] to array[1], then from array[1] to array[2] on canvas
}
function drawPreview(){
draw line from last point of arrayOflines to mouseposition;
}
//so here we are:
function animationloop{
erase your canvas
draw(arrayOfLines); //draw your array
drawPreview(); //draw your 'preview' line
requestAnimationFrame(animationloop); //animate
}
Doing things this way will allow you to achieve a clean result.
I have an svg map with a g element container.
inside the g element I have items with x, y positions.
I am trying to implement a mouse wheel zoom that pans the g element so that the object under the mouse is always under the mouse. similar to the way Google maps pans the map when zooming via the mouse wheel so that you zoom to the mouse position.
I have exhausted all searches and tried many different ways to calculate out the mouse position verses the g element position.
I've tried:
var xPan = (mouse.x - (matrix.scale * mouse.x)) - matrix.panX;
var yPan = (mouse.y - (matrix.scale * mouse.y)) - matrix.panY;
pan(xPan, yPan);
I had an similar problem some time ago, with the difference that I am using canvas but because I use svg to save my transform matrix it may help you, if I post the necessary part of my code:
window.transform = svg.createSVGMatrix();
window.pt = svg.createSVGPoint();
transformedPoint = function (x, y) {
window.pt.x = x; window.pt.y = y;
return pt.matrixTransform(window.transform.inverse());
}
translate = function(dx, dy) {
window.transform = window.transform.translate(dx, dy);
}
scale = function (scaleX, scaleY) {
window.transform = window.transform.scaleNonUniform(scaleX, scaleY);
};
zoom = function (scaleX, scaleY, x, y) { //use real x and y i.e. mouseposition on element
var p = transformedPoint(x, y);
translate(x, y);
scale(scaleX, scaleY);
translate(-x, -y);
}
I hope you can use some of this code and get it to work for you
Credits going to Phrogz and his outstanding example here: https://stackoverflow.com/a/5527449/1293849