Ok, here is something simple, I hope. I've a div container in which I can click and what I'm trying to do is to create two perpendicular lines which cross each other where I've clicked. So far I've written those:
#fr{
float: right;
margin-top: 5%;
height: 720px;
width: 1280px;
position: relative;
background-image: url(blueboard.jpg);
border: 1px solid black;
clear:none;
}
canvas{
border:1px solid red;
float: right;
height: 720px;
width: 1280px;
clear:none;
}//css part, I actually place the canvas on top of my div
//and on html...
<canvas id="line"></canvas>
//js
function saveOO(e){
var xo, yo;
xo=e.clientX;
yo=e.clientY;
...
document.getElementById("saved").innerHTML="The (0;0) = " +"("+xo+";"+yo+")";
document.getElementById("ball").style.left=xo+'px';
document.getElementById("ball").style.top=yo+'px';
xylines(xo, yo);
...;
}
function lines(xo, yo){
var xo, yo, xl, xline, yl, yline, Dx, Dy, a, b, c, d, e;
xo=xo;
yo=yo;
a=$("#fr").position();
b=$("#fr").width();
c=$("#fr").height();
Dy=a.top+100;
Dx=a.left;
d=Dx+b;
e=Dy+c;
xline = document.getElementById("line");
xl=xline.getContext("2d");
xl.beginPath();
xl.moveTo(Dx,yo);
xl.lineTo(d,yo);
xl.lineWidth = 15;
xl.stroke();
yline = document.getElementById("line");
yl=yline.getContext("2d");
yl.beginPath();
yl.moveTo(xo,Dy);
yl.lineTo(xo,e);
yl.lineWidth = 15;
yl.stroke();}
I have, as well, checked whether all variables assign a value and everything is good with that. The crossing point should be exactly where the blue ball is, it also positions on the place where it's clicked. As you can see on the image no lines show, even if I remove the blue background. Please, help me, maybe something is missing. I'm looking forward to your answers.
:)
P.S. Dy is Y of the top left corner, Dx respectively X of the top left corner
Update 2
A slightly amended fiddle where the lines span the entire width/height of the canvas:
https://jsfiddle.net/0y37qwvw/5/
This is by using 0 as the starting point for each and canvas.width/canvas.height as the ending point.
Update
Here is a fiddle demonstrating the use of a canvas overlay and responding to a click events.
https://jsfiddle.net/0y37qwvw/4/
The important thing is to get the relative x and y co-ordinates right, which I have done using event.offsetX and event.offsetY and a fallback for when they are not implemented:
document.getElementById("canvas-overlay").addEventListener("click", function( event ) {
var x, y, clientRect;
clientRect = event.target.getBoundingClientRect();
x = (typeof event.offsetX !== 'undefined' ? event.offsetX : (event.clientX - clientRect.left));
y = (typeof event.offsetY !== 'undefined' ? event.offsetY : (event.clientY - clientRect.top));
lines(x, y, 50, 50);
}, false);
Original answer
Your code is incomplete, so it is hard to be sure what the exact problems are.
One problem though is that you have not directly set the width and height of the canvas element, which will result in the canvas being scaled.
You can do this on the canvas element:
<canvas id="line" height="720" width="1280"></canvas>
Demonstrated at:
https://jsfiddle.net/0y37qwvw/1/
You could also set the width and height programmatically:
var canvas = document.getElementById("line");
canvas.width = 1280;
canvas.height = 720;
https://jsfiddle.net/0y37qwvw/3/
Compare to what happens if the width and height are only set by CSS:
https://jsfiddle.net/0aph7yno/
If you still can't get it to work, set up a plunker/fiddle with the broken code.
Here is some code I have written, I tried to use as much as OOP style so it's easier to expand it. But still simple and clear.
http://jsfiddle.net/eeqhc2tn/2/
var canvas = document.getElementById('can');
var ctx = canvas.getContext('2d');
var drawThese = [];
canvas.width = 500;
canvas.height = 500;
render();
//Line Code
function Line(sx, sy, ex, ey) {
this.sx = sx;
this.sy = sy;
this.ex = ex;
this.ey = ey;
}
Line.prototype.draw = function () {
ctx.beginPath();
ctx.moveTo(this.sx, this.sy);
ctx.lineTo(this.ex, this.ey);
ctx.stroke();
};
Line.drawCrossLines = function (x, y) {
var horizontal = new Line(0, y, canvas.width, y);
var vertical = new Line(x, 0, x, canvas.height);
drawThese.push(horizontal);
drawThese.push(vertical);
}
canvas.addEventListener('click', function (e) {
Line.drawCrossLines(e.offsetX, e.offsetY);
});
//Circle code
function Circle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
Circle.prototype.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.fill();
};
//Moving Circle Code that extends Circle
function MovingCircle() {
Circle.call(this,0,0,10);
}
MovingCircle.prototype = Object.create(Circle.prototype);
MovingCircle.prototype.constructor = MovingCircle;
MovingCircle.prototype.move = function (x, y) {
this.x = x;
this.y = y;
}
var pointer = new MovingCircle();
drawThese.push(pointer);
canvas.addEventListener('mousemove',function(e){
pointer.move(e.offsetX, e.offsetY);
});
//Rendering and animation code
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawThese.forEach(function (e) {
e.draw();
});
requestAnimationFrame(render);
}
I've found a working solution on my own, of course inspired by rpn. If you would like to make same or similar thing to what I this topic is about, a system including a container, canvas and a mouse event (click, in this case) you will have to follow this steps.
1. You cannot draw directly to a div, you've to create something like an invisible overlay for canvas and to place it perfectly over the div.
2. An important part is getting the proper coordinates, take into account that there are some tad but important differences between the various coords get methods. (`clientX/Y`, `offsetX/Y` and so on). It matters a lot.
3. Last but not at least, you should carefully consider how you get container's dimensions, as you'll need them later. Remember! We are in a container, a.k.a. working with an area which is just part of the whole window (your browser).
At first I was receiving my coordinates with clientX/Y but maybe due to some mismatches between the get method for coordinates and the one for div's dimensions I was hammered by bugs. To solve this, I've created a method from which I take mouse click coordinates with offsetX/Y. This way you have to think of you container as if it is a separate coordinate system, different from your window's(which by default is general). This means that now the top left corner of this div has x:y = (0;0);
function getcooords(){
xo = e.offsetX;
yo = e.offsetY;
DrawLines(x0, y0); //invoke func DrawLines
}
Now we go to the moment where we should draw our lines.
function DrawLines(x0,y0){
//your variables here...
w=$("#yourcontainer").width();
h=$("#yourcontainer").height();
Dy=0;
Dx=0;
}
That's the second step, Dx and Dy are the top and left properties of the div. Hence, from the what I've just said above, they will be equal to 0, both. Width and height of the container I take simply with jq but you can do it on another preferred way.
Generally speaking, that's the core of your drawing algorithm after we've taken needed dimensions and coordinates:
xl.beginPath();
xl.moveTo(b,yo);//start point of line
xl.lineTo(Dx,yo);//end point of line
xl.stroke(); //DRAW IT :)
Now, I've forgot to tell you that you have to define your lines at first, where should they be, what properties, styles should they have, etc..:
var xline = document.getElementById("line");
xl=xline.getContext("2d");
Remember! If you would like to delete, to erase the what you've drawn you'd have to define your lines FIRST and delete them after their definition. I'm saying definition, not drawing and you can draw the next 1, 2, 3... n lines and delete them over and over again if you follow this principle.
That's it guys, I hope I've explained it well, sorry for my English, probably I've made some tad mistakes but I hope you understand me. If you have any questions, please feel free to ask me, I'll try to do my best in order to help you.
:)
Related
I have been looking around for this function and thus far I just can't find any I can make any sense of. I already have a rotating function to make it equal to the position but slowly is proving to be a bit harder with 0-360 and all.
I am using a html canvas 2d context to render the objects on a Cartesian coordinate system .
I would like object1 to face at positionX and positionY at a turn rate (R) , fairly straightforward.
there is no need for me to supply any code since your likely going to make your own anyways. But I will anyways here you go:
let faceAt = function (thisObject,positionX,positionY) {
let desiredLocationX = positionX - thisObject.transform.x;
let desiredLocationY = positionY -thisObject.transform.y;
thisObject.transform.rotation = Math.degrees(Math.atan2(desiredLocationY, desiredLocationX));
};
The (Math.degrees) function converts radians to degrees.
This thread says it all : https://www.google.ca/amp/s/jibransyed.wordpress.com/2013/09/05/game-maker-gradually-rotating-an-object-towards-a-target/amp/
This question is quite unclear. But, I'm assuming you essentially just want to rotate an element around an arbitrary point on a HTML5 canvas.
On a canvas, you can only draw one element at a time. You can't really manipulate singular elements - for example, you can't rotate an element by itself. Instead, you'd need to rotate the entire canvas. This will always rotate around the centre of the canvas, but if you move the canvas origin, then you will draw on a different part of the canvas; thus allowing you to rotate around a point.
Check out the following example. You can click anywhere on the canvas to make the square rotate around that point. Hopefully this is what you are after:
let cv = document.getElementById("cv");
let ctx = cv.getContext("2d");
let angle = 0;
//Variables you can change:
let speed = 1; //Degrees to rotate per frame
let pointX = 250; //The x-coord to rotate around
let pointY = 250; //The y-coord to rotate around
ctx.fillStyle = "#000";
setInterval(()=>{ //This code runs every 40ms; so that the animation looks smooth
angle = (angle + speed) % 360; //Increment the angle. Bigger changes here mean that the element will rotate faster. If we go over 360deg, reset back to 0.
ctx.clearRect(0, 0, 400, 400); //Clear away the previous frame.
//Draw the point we are rotating around
ctx.beginPath();
ctx.arc(pointX,pointY,5,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.save(); //Save the state before we transform and rotate the canvas; so we can go back to the unrotated canvas for the next frame
ctx.translate(pointX, pointY); //Move the origin (0, 0) point of the canvas to the point to rotate around. The canvas always rotates around the origin; so this will allow us to rotate around that point
ctx.rotate(angle*Math.PI/180); //Rotate the canvas by the current angle. You can use your Math.degrees function to convert between rads / degs here.
ctx.fillStyle = "#f00"; //Draw in red. This is also restored when ctx.restore() is called; hence the point will always be black; and the square will always be red.
ctx.fillRect(0, 0, 50, 50); //Draw the item we want rotated. You can draw anything here; I just draw a square.
ctx.restore(); //Restore the canvas state
}, 40);
//Boring event handler stuff
//Move the point to where the user clicked
//Not too robust; relys on the body padding not changing
//Really just for the demo
cv.addEventListener("click", (event)=>{
pointX = event.clientX - 10;
pointY = event.clientY - 10;
});
#cv {
border:solid 1px #000; /*Just so we can see the bounds of the canvas*/
padding:0;
margin:0;
}
body {
padding:10px;
margin:0;
}
<canvas id="cv" width="400" height="400"></canvas><br>
Click on the canvas above to make the rectangle rotate around the point that was clicked.
I'm searching for a way to "live" draw rectangles or circles on a canvas.
I found various ways with fillRect() to draw rectangles, but not live. What I mean is, to be able to mouseDown() on one point and move it to another point in the canvas, which defines the size of the canvas, just like for example in Microsoft Paint, OneNote and so on.
Can anybody help me out and give me an advice on how to get started? I might be thinking of a way on how to do it, without seeing the rectangle (or circle) size changing, something like:
$("canvas").mousedown(function(event){
var ctx = this.getContext("2d");
ctx.clearRect(0,0,$(this).width(),$(this).height());
var initialX = event.clientX - this.getBoundingClientRect().left;
var initialY = event.clientY - this.getBoundingClientRect().top;
$(this).mousemove(function(evt) {
ctx.strokeRect(initialX, initialY, evt.clientX - event.clientX, evt.clientY - event.clientY);
});
});
But I want to see it live, so how the rectangles size changes when the user moves the mouse.
https://jsfiddle.net/zb66mxra/2/
To do it live you need to keep a constant image of your Canvas. This is accomplished easily by keeping an array of objects to be drawn over and over again by your JavaScript.
let drawArr = [];
An example object contains an x and y coordinate to begin drawing, a width, and a height:
{ x: 100,
y: 100,
w: 10,
h: 10 }
when your mouse moves over the canvas you ONLY want it to change the array if the mouse is down. This means you need to set a flag to see if this case is either true or false:
let mousedown = false;
canvas.addEventListener('mousedown', function(e) {
mousedown = true;
...
});
canvas.addEventListener('mouseup', function(e) {
mousedown = false;
});
When your mouse is down you want to add an item to draw to the array:
canvas.addEventListener('mousedown', function(e) {
mousedown = true;
drawArr.push({
x: e.pageX,
y: e.pageY,
w: 0,
h: 0
});
});
The height and width are initially set to 0. What we want to do now, if you can imagine, is create a height and width of the rectangle while we're dragging the mouse over the canvas and the mouse is down. We want to adjust this on the fly so that when the screen is re-rendered it is seen as it's being drawn.
It's easy to manipulate the height and width because, as long as you're only able to draw one at a time, it will ALWAYS be the most recent object added to the draw array.
canvas.addEventListener('mousemove', function(e) {
if (mousedown) {
let i = drawArr.length -1;
let {
x,
y
} = drawArr[i];
drawArr[i].w = e.pageX - x;
drawArr[i].h = e.pageY - y;
}
});
Finally we use requestAnimationFrame to constantly draw any object within the draw array. We do this by calling it when the page is loaded:
requestAnimationFrame(draw);
And then recursively within the draw function:
function draw() {
...
requestAnimationFrame(draw);
}
Then we simply need to clear the previous screen render and iterate over the draw array and draw everything to the screen again.
function draw() {
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
for (let obj of drawArr) {
let {
x,
y,
w,
h
} = obj;
ctx.strokeRect(x, y, w, h);
}
requestAnimationFrame(draw);
}
voila.
window.onload=function(){
var c = document.getElementById('canvas'),
ctx = c.getContext('2d'),
x=0, y=0, cnt=1;
for(var i=0;i<(window.innerWidth)/10;i++){
ctx.moveTo(x, y); x+=5;
if(cnt%2){
y=5; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}else{
y=0; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}
}
}
<canvas id="canvas" style="width:100%; height:250px"></canvas>
If you run the above code then the resolution of lines in the zig-zag pattern in the if fine but in here you can see the image the resoultion of this pattern is very poor (please click on this image to view this problem):
what i have tried is that i have changed the condition (window.innerWidth)/10 to (winodw.innerWidth)/4 and x+=5 to x+=2
but what it does is that it makes the line so thick and bad that you don't want to see it.
so, what should i do to increase the resolution of the lines of the pattern?
Just make sure your canvas element is as big as you are displaying it.
i added c.width = windows.innerWidth and also c.heigth = 250 and the resolution looks correct now.
window.onload=function(){
var c = document.getElementById('canvas'),
ctx = c.getContext('2d'),
x=0, y=0, cnt=1;
c.width = window.innerWidth;
c.height = 250;
for(var i=0;i<(window.innerWidth);i++){
ctx.moveTo(x, y); x+=5;
if(cnt%2){
y=5; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}else{
y=0; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}
}
}
<canvas id="canvas" style="width:100%; height:250px"></canvas>
There are a couple of things, but mostly it comes down to this: you are drawing at a width of 100%, which is stretching the default size of a canvas you are drawing in - thats why it blurs. Set your width correctly using javascript and the sharpness increases. The only thing is, a difference of 5 pixels is barely noticeable, so you have to increase your size to something more... average. I have opted for 1/100 of the windows width, but you can turn it into anything.
// For safety, use event listeners and not global window method overwriting.
// It will become useful if you have multiple scripts you want to
// execute only after loading them!
window.addEventListener('DOMContentLoaded', function(){
var c = document.getElementById('canvas'),
ctx = c.getContext('2d'),
x = 0, y = 0;
// Set the correct width and height
c.width = window.innerWidth;
c.height = window.innerWidth / 100;
// Use moveTo once, then keep drawing from your previous lineTo call
ctx.moveTo(x, y);
// You only need your x value here, once we are off screen we can stop drawing and end the for loop!
for(; x < window.innerWidth; x += window.innerWidth / 100){
// Use lineTo to create a path in memory
// You can also see if your y needs to change because y = 0 = falsy
ctx.lineTo(x, (y = y ? 0 : window.innerWidth / 100));
}
// Call stroke() only once!
ctx.stroke();
// And for safety, call closePath() as stroke does not close it.
ctx.closePath();
}, false);
<canvas id="canvas"></canvas>
<!-- Remove all styling from the canvas! Do this computationally -->
maybe you can find your ans here
Full-screen Canvas is low res
basically it summarizes that instead of setting height and width in the css, you should set it via html (inside the canvas element via width and height attr) or via javaScript.
because when doing it in css, you are basically scaling it and thus reducing the resolution, so you have to mention the actual size in html element and not scale it in css.
I'm trying to learn how to learn basic animation using ONLY canvas with the setInterval function.
I'm trying to draw a simple rectangle to the screen and move it to the right 1 pixel every 100 milliseconds. However when I do it paints over the previous rectangle. I called clearRect() but that doesn't seem to do anything.
How can I make this rectangle smoothly travel across the screen without leaving a trail?
Also if there is a better way to do this rather than using clearRect() and translate() please do share.
var ctx = document.getElementById('mycanvas').getContext('2d');
var a = setInterval(draw,100);
var x = 50;
function draw()
{
ctx.clearRect(0,0,300,300);
ctx.translate(1,0);
ctx.rect(x,50,50,50);
ctx.stroke();
}
You can do it two different ways:
You can continue to use rect() and stroke(), but you need to call beginPath() beforehand. When you call methods like rect(), a list, called the "path," is kept of all of the shapes, or "subpaths," that you've created. Then, when you call stroke(), the entire path is drawn. Thus, even if you clear the screen, all of the past rectangles are still remembered in the path, and drawn again. beginPath() clears that list.
var x = 50;
function draw() {
ctx.clearRect(0, 0, 300, 300);
ctx.beginPath();
ctx.rect(x, 50, 50, 50);
ctx.stroke();
x++;
}
Or, you can combine the rect() and stroke() into one line, and not need to call beginPath(). That's because the rectangle is both created and drawn at the same time, and isn't put in the list.
var x = 50;
function draw() {
ctx.clearRect(0, 0, 300, 300);
ctx.strokeRect(x, 50, 50, 50);
x++;
}
Either way, I advise incrementing x instead of using translate(), because translate() basically moves the imaginary "pen" that is drawing on the canvas. So if you translate(50, 50), and then you try to draw a rectangle at (0, 0) on the canvas, it will actually be at (50, 50).
As Microsoft puts it on MSDN, "The translate method effectively remaps the (0,0) origin on a canvas."
If you repeatedly do that, it will become difficult to keep track of where you're actually drawing.
Your x variable never changes, so your shape will not move. You need to increment x to get movement:
var x = 50;
function draw(){
ctx.clearRect(0,0,300,300);
ctx.translate(1,0);
ctx.rect(x,50,50,50);
ctx.stroke();
x++;
}
In order to get a smooth animation with shapes and other sprites moving across the screen (or even staying still) it would be better to make a clearScreen method that will basically draw over the entire canvas in whatever background color the canvas is. It is basically just a function that will draw a white (or whatever background color you are using) rectangle over the entire canvas. Then, you call the draw function that will make all the necessary drawings. That way, there won't be any trail or anything of the past movements and you won't have to call clearRect() on every single rectangle you make.
Basically, the function will erase the canvas and you can redraw whatever you need to in order to make the animation of the box moving across the screen.
Does that make sense?
EDIT:
Also, to be clear, you would make your own clearScreen method based on what size your canvas is and what color your background is. Its not hard, all it does is draw a rectangle over the screen.
Simply increment x on every call:
var canvas = document.getElementById('mycanvas')
var ctx = canvas.getContext('2d');
var a = setInterval(draw,100);
var x = 50;
function draw(){
canvas.width = canvas.width; // clears the canvas
ctx.rect(x++,50,50,50);
ctx.stroke();
if (x > 250) // resets the position
x = 50;
}
<canvas id="mycanvas"></canvas>
I also removed that translation since there's no need to do it just for the square animation.
Try beginPath() and closePath():
var maxX, x = 0,
s = 50,
maxY;
var repaint = function(ctx) {
if (x + s >= maxX) { //reached end of the canvas
return;
}
ctx.clearRect(0, 0, maxX, maxY); //clear previous
ctx.beginPath(); //start drawing
ctx.rect(x, s, s, s);
ctx.stroke();
ctx.closePath(); //stop drawing
x++;
setTimeout(function() {
repaint(ctx); //continue here
}, 100);
};
var cnvs = document.getElementById('canvas');
maxX = cnvs.width;
maxY = cnvs.height;
repaint(cnvs.getContext('2d'));
canvas {
border: 1px solid grey;
}
<canvas width="360" height="180" id='canvas'>HTML5 canvas not supported</canvas>
I try to make an animation of character by reading this tutorial:
http://mrbool.com/html5-canvas-moving-a-character-with-sprites/26239 .
It's quite ease to make the character go left ('go right' is already done). But how to make the character jump (with animation)?
I was thinking about something like this:
case 38:
if (y + dy > HEIGHT){
y += dy
}
break;
...but it just move character up (without animation). Can someone help me? Some code example will be useful.
You get the jumping behavior like this (using the same code on the tutorial)
JSFiddle
var canvas;// the canvas element which will draw on
var ctx;// the "context" of the canvas that will be used (2D or 3D)
var dx = 50;// the rate of change (speed) horizontal object
var x = 30;// horizontal position of the object (with initial value)
var y = 150;// vertical position of the object (with initial value)
var limit = 10; //jump limit
var jump_y = y;
var WIDTH = 1000;// width of the rectangular area
var HEIGHT = 340;// height of the rectangular area
var tile1 = new Image ();// Image to be loaded and drawn on canvas
var posicao = 0;// display the current position of the character
var NUM_POSICOES = 6;// Number of images that make up the movement
var goingDown = false;
var jumping;
function KeyDown(evt){
switch (evt.keyCode) {
case 39: /* Arrow to the right */
if (x + dx < WIDTH){
x += dx;
posicao++;
if(posicao == NUM_POSICOES)
posicao = 1;
Update();
}
break;
case 38:
jumping = setInterval(Jump, 100);
}
}
function Draw() {
ctx.font="20px Georgia";
ctx.beginPath();
ctx.fillStyle = "red";
ctx.beginPath();
ctx.rect(x, y, 10, 10);
ctx.closePath();
ctx.fill();
console.log(posicao);
}
function LimparTela() {
ctx.fillStyle = "rgb(233,233,233)";
ctx.beginPath();
ctx.rect(0, 0, WIDTH, HEIGHT);
ctx.closePath();
ctx.fill();
}
function Update() {
LimparTela();
Draw();
}
var Jump = function(){
if(y > limit && !goingDown){
y-=10;
console.log('jumping: ' + y);
} else{
goingDown = true;
y +=10;
if(y > jump_y){
clearInterval(jumping);
goingDown = false;
}
}
}
function Start() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
return setInterval(Update, 100);
}
window.addEventListener('keydown', KeyDown);
Start();
There's no one right answer to this question, and unless you find a game-design library, there's no simple one, either. Your problem is that you're moving the character instantaneously in response to input, but a jump requires movement over time. You'll have to either find a moving sprites library - I don't have one in particular to recommend, but I'm sure Google has several - or set up something yourself that runs every so many milliseconds and updates the character's position and some sort of velocity variable.
Edit: Looking at that tutorial, the simplest solution that comes to mind is to put your animation code inside of Update(), like so:
function Update() {
LimparTela();
Animate();
Draw();
}
Inside of Animate(), you should keep track of the character's height and vertical momentum. If the momentum is positive, increase the y position a little, otherwise decrease it a little. Either way, reduce momentum a bit. Add something to keep the character from going through the floor, and have the up key set the character's momentum to be positive if he's on the floor.
Note that this is an incredibly bare-bones solution, but for a basic tutorial it'll do the job.