Related
I am new to this forum although I have been lurking here for help. I am currently messing with AR with A-frame and javascript for fun. I am working on moving my 3d model with a joystick I found on github. fortunately, I was able to move the model around with the joystick, but unfortunately I am unable to figure out how to rotate the model while it moves. Any help would be appreciated!
// turn joystick data into WASD movement in AFRAME
var f;
var ang;
var x_vec;
var y_vec;
var cam;
function updatePosition(data) {
f = data.force;
ang = data.angle.radian
cam = document.getElementById("model");
x_vec = Math.cos(ang + 3.14 / 180 * cam.getAttribute('rotation')['x']);
y_vec = Math.sin(ang + 3.14 / 180 * cam.getAttribute('rotation')['y']);
x = cam.getAttribute("position")["x"] + f / 15 * (x_vec);
y = cam.getAttribute("position")["y"]
z = cam.getAttribute("position")["z"] - f / 15 * (y_vec);
cam.setAttribute('position', `${x} ${y} ${z}`)
cam.setAttribute('rotation', `${x} ${y} ${z}`)
}
Having the new angle, just add it to the current rotation.y(since you're moving on an XZ plane).
The only catch is that the rotation should be in degrees:
ang = data.angle.radian
cam = document.getElementById("model");
// grab the rotation
var rotation = cam.getAttribute("rotation")
rotation.y += ang * 180 / Math.PI
// set the new rotation
cam.setAttribute('rotation', rotation)
A simple example of rotating an entity and moving it forward would be a component like this
AFRAME.registerComponent("joystick-controls", {
init: function() {
// initialize the joystick
this.joystick = new JoyStick("joyDiv");
// grab the rotation and position for later use
this.rot = this.el.getAttribute("rotation");
this.pos = this.el.getAttribute("position");
},
tick: function() {
// wait until the joystick is ready
if (!this.joystick) return;
// handle rotation
this.rot.y -= this.joystick.GetX() / 100;
this.el.setAttribute("rotation", this.rot);
// handle position
var speed = this.joystick.GetY() / 2000;
this.pos.z += speed * Math.cos(this.rot.y * Math.PI / 180);
this.pos.x += speed * Math.sin(this.rot.y * Math.PI / 180);
this.el.setAttribute("position", this.pos);
}
});
Glitch here. I'm using this joystick.
I'm trying to rotate an image inside a canvas.
Here's my Fiddle: https://jsfiddle.net/kevinludwig11/s6rgpjm9/
I try it with save and restore, but the path is also rotating.
The falcon should fly with his face towards and change the angle in the corners.
Can anybody help me?
Edit: One solution i've found: save the image 360 times with every rotation and load every image in the right position. But i think thats not the smartest solution.
Canvas 2D image lookat transform.
No need to create 360 images to rotate a single image. Also you had a few incorrect ways of doing things.
Code problems
Only load the image once. You were loading it each time it was rendered.
Use requestAnimationFrame on its own. Putting it inside a timer makes its use completely redundant.
If you find yourself typing in long lists of numbers, and especially if you repeat these numbers in other sections of code you should use a single store to hold everything. Eg your paths were all hand coded. Move them into an array then iterate the array for the various things you need to do with the paths. One of the top ten programing rules. "Don't repeat/duplicate anything."
The lookat transform
To do the bird you will need to get the direction it is heading towards so I added a second point on the curves that is ahead of the bird. With these two points (birds pos and lookat pos) I then create a transformation using the lookat direction as the xAxis of the transformation. See function drawImageLookat(image,pos,lookat) I found that the image is not along the X axis so I rotate the bird 90deg after finding the lookat transformation.
Lookat function
// function assumes front (forward) of image is along the x axis to the right
function drawImageLookat(image, point, lookat ) {
var xAx,xAy; // vector for x Axis of image
var x,y;
x = lookat.x - point.x;
y = lookat.y - point.y;
var dist = Math.max(0.01,Math.sqrt(x * x + y * y)); // Math.max to avoid zero which will create NaN
xAx = x / dist; // get x component of x Axis
xAy = y / dist; // get y component of x Axis
// y axis is at 90 deg so dont need y axis vector
ctx.setTransform( // position the image using transform
xAx, xAy, // set direction of x Axis
-xAy, xAx, // set direction oy y axis
point.x, point.y
);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
}
Demo from fiddle.
Your code that I took from the fiddle https://jsfiddle.net/kevinludwig11/s6rgpjm9/ and modified to run as your question implies.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// only load image once
var birdImage = new Image();
birdImage.src = 'http://www.happy-innovation.de/images/Falke_Flug.png';
birdImage.onload = function(){animate()}; // start animation when image has loaded
// set starting values
var speed = 0.25
var percent = speed;
var direction = speed;
var length = 300;
function animate() {
ctx.setTransform(1,0,0,1,0,0); // restore default transform incase its not
ctx.clearRect(0, 0, canvas.width, canvas.height);
percent += direction;
// need to keep the position away from the ends as there is no lookat beyond the path.
if(percent >= length - speed){
percent = length- speed;
direction = -speed;
}else if(percent <= speed){
percent = speed;
direction = speed;
}
draw(percent,direction);
requestAnimationFrame(animate);
}
function P(x,y){return {x,y}}; // quick way to create a point
var paths = [
{col : 'red', points : [P(100, 200), P(600, 350), P( 700, 400)]},
{col : "green", points : [P(700, 400), P( 900, 500), P( 200, 600), P( 950, 900)]},
{col : "blue", points : [P(950, 900), P(1200, 950), P( 300, 200), P( 150, 1200)]},
{col : "brown", points : [P(150, 1200),P( 120, 1700),P( 1000, 700),P(850, 1500)]},
{col : "Purple",points : [P(850, 1500),P(800, 1900), P( 200, 900), P( 250, 1800)]},
{col : "yellow", points : [P(250, 1800),P(250, 1950), P( 600, 1500),P(950, 1800)]},
]
// draw the current frame based on sliderValue
function draw(sliderValue,direction) {
var getPos = false; // true if need pos on current curve
var getForwardPos = false; // true if need pos on current curve
var percent,percent1; // get the percentage on curves
var birdPos; // get bird pos
var birdLookAtPos; // get bird look at pos
ctx.lineWidth = 5;
for(var i = 0; i < paths.length; i ++){
var path = paths[i]; // get a path from array
var p = path.points;
ctx.strokeStyle = path.col;
ctx.beginPath();
ctx.moveTo(p[0].x,p[0].y);
if(sliderValue >= i * 50 && sliderValue < (i+1) * 50){
getPos = true;
percent = (sliderValue % 50) / 50;
}
if(sliderValue + direction >= i * 50 && sliderValue + direction < (i+1) * 50){
getForwardPos = true;
percent1 = ((sliderValue + direction) % 50) / 50;
}
if(p.length > 3){
ctx.bezierCurveTo(p[1].x,p[1].y,p[2].x,p[2].y,p[3].x,p[3].y);
if(getPos){
birdPos = getCubicBezierXYatPercent(p[0],p[1],p[2],p[3],percent);
getPos = false;
}
if(getForwardPos){
birdLookAtPos = getCubicBezierXYatPercent(p[0],p[1],p[2],p[3],percent1);
getForwardPos = false;
}
}else{
ctx.quadraticCurveTo(p[1].x,p[1].y,p[2].x,p[2].y);
if(getPos){
birdPos = getQuadraticBezierXYatPercent(p[0],p[1],p[2],percent);
getPos = false;
}
if(getForwardPos){
birdLookAtPos = getQuadraticBezierXYatPercent(p[0],p[1],p[2],percent1);
getForwardPos = false;
}
}
ctx.stroke();
}
drawImageLookingAt(birdImage,birdPos,birdLookAtPos);
}
function drawImageLookingAt(image, point, lookat ) {
if(lookat === undefined){ // if no lookat then exit or it will crash.
return;
}
var xAx,xAy; // vector for x Axis of image
var x,y;
x = lookat.x - point.x;
y = lookat.y - point.y;
var dist = Math.max(0.01,Math.sqrt(x * x + y * y)); // Math.max to avoid zero which will create NaN
xAx = x / dist; // get x component of x Axis
xAy = y / dist; // get y component of x Axis
// y axis is at 90 deg so dont need y axis vector
ctx.setTransform( // position the image using transform
xAx, xAy, // set direction of x Axis
-xAy, xAx, // set direction oy y axis
point.x, point.y
);
// bird is pointing in the wrong direction. Not along x axis
// so rotate the image 90 deg clockwise
ctx.rotate(Math.PI / 2);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
ctx.setTransform(1,0,0,1,0,0); // Restore default Not really needed if you only use setTransform to do transforms
// but in case you use transform, rotate, translate or scale you need to reset the
// transform.
}
// line: percent is 0-1
function getLineXYatPercent(startPt, endPt, percent) {
var dx = endPt.x - startPt.x;
var dy = endPt.y - startPt.y;
var X = startPt.x + dx * percent;
var Y = startPt.y + dy * percent;
return ({
x: X,
y: Y
});
}
// quadratic bezier: percent is 0-1
function getQuadraticBezierXYatPercent(startPt, controlPt, endPt, percent) {
var x = Math.pow(1 - percent, 2) * startPt.x + 2 * (1 - percent) * percent * controlPt.x + Math.pow(percent, 2) * endPt.x;
var y = Math.pow(1 - percent, 2) * startPt.y + 2 * (1 - percent) * percent * controlPt.y + Math.pow(percent, 2) * endPt.y;
return ({
x: x,
y: y
});
}
// cubic bezier percent is 0-1
function getCubicBezierXYatPercent(startPt, controlPt1, controlPt2, endPt, percent) {
var x = CubicN(percent, startPt.x, controlPt1.x, controlPt2.x, endPt.x);
var y = CubicN(percent, startPt.y, controlPt1.y, controlPt2.y, endPt.y);
return ({
x: x,
y: y
});
}
// cubic helper formula at percent distance
function CubicN(pct, a, b, c, d) {
var t2 = pct * pct;
var t3 = t2 * pct;
return a + (-a * 3 + pct * (3 * a - a * pct)) * pct + (3 * b + pct * (-6 * b + b * 3 * pct)) * pct + (c * 3 - c * 3 * pct) * t2 + d * t3;
}
<canvas height="1961" width="1000" id="canvas"></canvas>
I am trying to imitate a pattern I found on the internet, but I get weird lines in the middle and when trying to connect another set of circles on top.
Also, when I try to fill, it becomes fully black.
console.log("grid");
var canvas = document.getElementById("canvas");
var image_b = document.getElementById("brown");
var image_g = document.getElementById("grey");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
var side = 160;
var side2 = 150;
ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';
function draw() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
var widthNbr = Math.ceil(window.innerWidth / side) + 1;
var heightNbr = Math.ceil(window.innerHeight / side) + 1;
var counter = 0;
for (var i = 0; i < widthNbr; i++) {
for (var j = 0; j < heightNbr; j++) {
ctx.beginPath();
var x = side * i + side / 2;
var y = side * j + side / 2;
var a = side * i + side / 2;
var s = side * j + side / 2;
var d = side * i + side / 2;
var f = side * j + side / 2;
var g = side * i + side / 2;
var h = side * j + side / 2;
var q = side * i + side / 2;
var w = side * j + side / 2;
var o = side * i + side / 2;
var p = side * j + side / 2;
var x1 = side2 * i + side2;
var y1 = side2 * j + side2;
var a1 = side2 * i + side2;
var s1 = side2 * j + side2;
var d1 = side2 * i + side2;
var f1 = side2 * j + side2;
var g1 = side2 * i + side2;
var h1 = side2 * j + side2;
var q1 = side2 * i + side2;
var w1 = side2 * j + side2;
var o1 = side2 * i + side2;
var p1 = side2 * j + side2;
ctx.arc(x, y, side / 2, 0, Math.PI * 2);
ctx.arc(a, s, side / 2.5, 0, Math.PI * 2);
ctx.arc(d, f, side / 3.5, 0, Math.PI * 2);
ctx.arc(g, h, side / 5.3, 0, Math.PI * 2);
ctx.arc(q, w, side / 9, 0, Math.PI * 2);
ctx.arc(o, p, side / 18, 0, Math.PI * 2);
ctx.lineWidth = 5;
ctx.arc(x1, y1, side2 / 2, 0, Math.PI * 2);
ctx.arc(a1, s1, side2 / 2.5, 0, Math.PI * 2);
ctx.arc(d1, f1, side2 / 3.5, 0, Math.PI * 2);
ctx.arc(g1, h1, side2 / 5.3, 0, Math.PI * 2);
ctx.arc(q1, w1, side2 / 9, 0, Math.PI * 2);
ctx.arc(o1, p1, side2 / 18, 0, Math.PI * 2);
ctx.stroke();
// ctx.fill();
ctx.closePath();
counter++;
}
}
}
draw();
<canvas id="canvas"></canvas>
You have to think about canvas Path drawings as pencil drawing on a paper :
Just after the path declaration (beginPath), when you say ctx.arc(x, y, rad, 0, Math.PI*2) your pen goes to coordinates (x, y), and because x and y are the center position of your arc it will be putted at a rad distance from this center to draw the circle. Your 0 tells it to start at 3 o'clock, so in this case, we just need to add this rad to the x value.
At this moment, your pen is on the paper.
It draws the arc, and when you tell it arc(x1, y1, rad, ...), it goes directly to coordinates (x1+rad, y1) and draws the new arc.
The problem here is that you never told it to raise the pencil from the paper, so you can see the line that goes from the last point on the first arc to the first point on the next one.
Fortunately, Canvas API comes with a handy set of operations, and the "Raise_the_pen_and_move_to_coordinates_x,y_without_ruining_my_paper" is simply called moveTo.
By telling the context to gently raise the pencil and to move to the next first drawing point, before actually drawing the arc, you'll avoid all these trailing lines.
So basically, for three arcs it would be :
// initialize a new drawing
ctx.beginPath();
// here we can set it directly because the pen is not on the paper yet
ctx.arc(x, y, rad, 0, Math.PI*2);
// tell it to raise the pen off the paper
// and to go to the next starting point (3 o'clock in our case)
ctx.moveTo(x1 + rad, y1);
ctx.arc(x1, y1, rad, 0, Math.PI*2);
// once again
ctx.moveTo(x2 + rad, y2);
ctx.arc(x2, y2, rad, 0, Math.PI*2);
// now we've got clear independents arcs
ctx.stroke();
And with your code (That you could clean a lot by using arrays btw)
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
var side = 160;
var side2 = 150;
ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';
function draw() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
var widthNbr = Math.ceil(window.innerWidth / side) + 1;
var heightNbr = Math.ceil(window.innerHeight / side) + 1;
var counter = 0;
for (var i = 0; i < widthNbr; i++) {
for (var j = 0; j < heightNbr; j++) {
ctx.beginPath();
var x = side * i + side / 2;
var y = side * j + side / 2;
var a = side * i + side / 2;
var s = side * j + side / 2;
var d = side * i + side / 2;
var f = side * j + side / 2;
var g = side * i + side / 2;
var h = side * j + side / 2;
var q = side * i + side / 2;
var w = side * j + side / 2;
var o = side * i + side / 2;
var p = side * j + side / 2;
var x1 = side2 * i + side2;
var y1 = side2 * j + side2;
var a1 = side2 * i + side2;
var s1 = side2 * j + side2;
var d1 = side2 * i + side2;
var f1 = side2 * j + side2;
var g1 = side2 * i + side2;
var h1 = side2 * j + side2;
var q1 = side2 * i + side2;
var w1 = side2 * j + side2;
var o1 = side2 * i + side2;
var p1 = side2 * j + side2;
ctx.moveTo(x + side / 2, y);
ctx.arc(x, y, side / 2, 0, Math.PI * 2);
ctx.moveTo(a + side / 2.5, s);
ctx.arc(a, s, side / 2.5, 0, Math.PI * 2);
ctx.moveTo(d + side / 3.5, f)
ctx.arc(d, f, side / 3.5, 0, Math.PI * 2);
ctx.moveTo(g + side / 5.3, h)
ctx.arc(g, h, side / 5.3, 0, Math.PI * 2);
ctx.moveTo(q + side / 9, w)
ctx.arc(q, w, side / 9, 0, Math.PI * 2);
ctx.moveTo(o + side / 18, p)
ctx.arc(o, p, side / 18, 0, Math.PI * 2);
ctx.lineWidth = 5;
ctx.moveTo(x1 + side2 / 2, y1)
ctx.arc(x1, y1, side2 / 2, 0, Math.PI * 2);
ctx.moveTo(a1 + side2 / 2.5, s1)
ctx.arc(a1, s1, side2 / 2.5, 0, Math.PI * 2);
ctx.moveTo(d1 + side2 / 3.5, f1)
ctx.arc(d1, f1, side2 / 3.5, 0, Math.PI * 2);
ctx.moveTo(g1 + side2 / 5.3, h1)
ctx.arc(g1, h1, side2 / 5.3, 0, Math.PI * 2);
ctx.moveTo(q1 + side2 / 9, w1)
ctx.arc(q1, w1, side2 / 9, 0, Math.PI * 2);
ctx.moveTo(o1 + side2 / 18, p1)
ctx.arc(o1, p1, side2 / 18, 0, Math.PI * 2);
ctx.stroke();
counter++;
}
}
}
draw();
<canvas id="canvas"></canvas>
As correctly noted by Spencer Wieczorek in comments above, to get the result you wanted, you'll also have to white-fill the largest arcs, but I let you find the way to do it as a training.
Also, a small note on closePath() that you were using in your code, his name might be quite confusing when we see the number of people misusing it, but note that it doesn't ends your Path declaration. All it does is a lineTo(last_time_I_putted_the_pencil). In the case of closed circle, it doesn't have any effect because last_time_I_putted_the_pencil === current_pencil_position_on_the_paper, but it's often the source of a lot of problems.
And an other small note, for users a bit more experienced (probably OP in few days / weeks) :
Other operations allow us to raise our pencil from the paper : the transformation commands.
(mainly setTransform, and its subsets transform, translate, rotate and scale).
These operations will first raise the pen, and then move the paper rather than the pen. This comes handy in a lot of situations.
And to set it back to its normal position, you just have to call setTransform(1,0,0,1,0,0).
As I was far too slow in responding to this question please consider this an addendum to the excellent answer already provided by Kaiido.
When thinking about problems like this it is sometimes useful to separate the calculation of values from their application. Of course, this kind of understanding only comes with experience, unless that is we can plug into the experience of others - which is exactly what sites like StackOverflow are for! :)
The image we're intending to make is made entirely of circles, so we can greatly reduce repetition in our code by creating a function that deals with just that one thing for us. Something like...
/* draw circle */
function drawCircle(x, y, r, LW) {
context.lineWidth = LW;
context.beginPath();
context.arc(x, y, r, 0, Math.PI*2, true);
context.fill();
context.stroke();
}
Although this function only draws a single circle to the canvas we can pass it values that can be used to draw every element we need.
The reference image is built out of sets of circles, where each circle-set has the same (x,y) position. If we know those starting co-ordinates for X & Y, and the radius of the largest circle in the set, then we can create a function to calculate the values we need and pass them into the drawCircles() function above...
function circleSet(X, Y, Radius) {
var count = 5; /* number of circles in each set */
var step = Radius / count;
var ln_width;
var rad;
while (count > 0) {
ln_width = count > 3 ? 3 : (count > 1 ? 2 : 1);
rad = count * step;
drawCircle(X, Y, rad, ln_width);
count--;
}
}
The while-loop reduces the count variable from 5 to 1 and line width and radius values are calculated before being passed into the aforementioned drawCircle() function, along with (x,y) co-ordinates.
The conditional in the first line of the while-loop says:
if count is 4 or 5 then ln_width equals 3;
or else if count is 2 or 3 then ln_width equals 2;
or else ln_width equals 1.
The rad variable holds a value that starts at Radius and is decreased by 1 x steps for each iteration of the loop.
Now that the circle-set parameters are ready to be calculated all that's needed is to calculate the values to pass to the circleSet() function, that is; the (x,y) co-ordinates and starting radius of each set.
As your original code snippet showed, we can use two nested for-loops for this. one to deal with the vertical and one to deal with the horizontal, but first we need to make some decisions.
If we want to have 10 circle-sets across the canvas then the width of each set would be...
var circleSet_Size = canvas.width / 10;
...and the maximum radius of each circle-set would therefore be...
var circleSet_Radius = circleSet_Size / 2;
We also need to draw two lots of circle-sets to make something like the reference image, one lot for the 'background' and one lot for the 'foreground'. So we need to determine the starting (x,y) co-ordinates of each pass as well as width/height of the area we want to draw over. We can create a function for that too. Something like...
function loopXYPosition(startX, startY) {
for (var y = startY; y < (canvas.height + circleSet_Radius); y += circleSet_Size) {
for (var x = startX; x < (canvas.width + circleSet_Radius); x += circleSet_Size) {
circleSet(x, y, setRadius);
}
}
}
/* 'background' pass */
loopXYPosition(0, 0);
/* 'foreground' pass */
loopXYPosition(circleSet_Radius, circleSet_Radius);
With all that in place we can collate our functions into a single script. But before we do that it's worth taking note of those values which need to be calculated each time a function is called and which values remain static once calculated. Bearing all that in mind we end up with something like...
var circlePattern = (function() {
/* define variables available
to all functions */
var canvas
, ctx
, cWidth
, cHeight
, circleCount
, circleSet_Size
, circleSet_Radius
, P360;
/* draw each cicle */
function drawCircle(x, y, r, LW) {
ctx.beginPath();
ctx.lineWidth = LW;
ctx.arc(x, y, r, 0, P360, true);
ctx.fill();
ctx.stroke();
}
/* calculate each set of circles (circle-set) */
function circleSet(X, Y, R) {
var count = circleCount,
radiusSteps = R / count,
ln_width,
rad;
while (count > 0) {
ln_width = count > 3 ? 2.5 : (count > 1 ? 2 : 1.5);
rad = count * radiusSteps;
drawCircle(X, Y, rad, ln_width);
count--;
}
}
function loopXYPosition(startX, startY) {
/* add circleSet_Radius to canvas width
and height to make sure we draw right
up to the edges of the canvas */
var cHcR = cHeight + circleSet_Radius;
var cWcR = cWidth + circleSet_Radius;
/* to get the effect we want we need to create
a little padding around each circle-set,
therefore we reduce the circelSet_Radius value
by 5% before passing to drawCircleSet() */
var setRadius = circleSet_Radius * 0.95;
/* step across and down canvas in
increments of 'circleSet_Size' */
for (var y = startY; y < cHcR; y += circleSet_Size) {
for (var x = startX; x < cWcR; x += circleSet_Size) {
circleSet(x, y, setRadius);
}
}
}
function begin(NoC, cCount) {
var numberOfCircles = NoC;
/* set variables needed later */
circleSet_Size = cWidth / numberOfCircles;
circleSet_Radius = circleSet_Size / 2;
circleCount = cCount;
/* draw rows of circles */
loopXYPosition(0, 0);
loopXYPosition(circleSet_Radius, circleSet_Radius);
}
/* initialise canvas */
function init(e) {
/* Set variables to use later */
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
cWidth = canvas.width;
cHeight = canvas.height;
P360 = Math.PI * 2;
/* fillStyle & strokeStyle are properties
of the Canvas, so we only need to set
them once here */
ctx.fillStyle = '#fff';
ctx.strokeStyle = '#000';
/* fill canvas (background) */
ctx.fillRect(0, 0, cWidth, cHeight);
/* first argument: number of horizontal circle-sets
second argument: number of circles in each set
!Try different values here! */
begin(7, 6);
}
return {
go: init
};
}());
window.onload = circlePattern.go;
body {
color:#000;
background-color:#fff;
font-family:sans-serif;
}
div.box {
width:100%;
height:auto;
text-align:center;
margin:2em auto;
display:block;
}
div.box h3 {
font-size:1.3em;
line-height:2.3em;
}
#refimg, #canvas {
width:600px;
margin:0 auto;
clear:both;
display:block;
}
#refimg {
height:360px;
}
#canvas {
height:400px;
}
<div class="box">
<h3>Refernece Image</h3>
<img id="refimg" src="http://i.stack.imgur.com/Uxm2Z.jpg)" alt="image" title="reference image" />
</div>
<div class="box">
<h3>Canvas Image</h3>
<canvas id="canvas" width="600" height="400" title="Canvas image"></canvas>
</div>
Not exactly the same results, though pretty close. With the exception of the (x,y) co-ordinates which pass through it the circleSet() function produces the same sequence of values each time it is called, so those values could be calculated once and stored in an Object, but I've included it here for simplicity and to highlight the sequence of events.
Feeding different values into the begin() function and playing with setRadius in the loopXYPosition() function yields some interesting results.
I hope the process outlined here gives you some hints in your continuing exploration of the HTML5 Canvas API.
;)
I have code that creates a ball and then gives it a random color. Not sure how to make the circle "spawn" in a random location on the screen.
var RADIUS = 120;
var circle;
function start(){
circle = new Circle(RADIUS);
circle.setPosition();
add(circle);
setTimer(color, 1000);
}
function color() {
circle.setColor(Randomizer.nextColor());
}
Here is a small example, hope you understand.
in your html:
in your script:
var canvas = document.getElementById("canvas_id");
var screen = canvas.getContext("2d");
function drawBall()
{
screen.beginPath();
screen.arc(x, y, ballRadius, 0, Math.PI*2);
screen.fillStyle = "#000000";
screen.fill();
screen.closePath();
}
Now just change the x and y respectively! Goodluck.
You could try possitionin with css
circle.style.position = "absolute";
circle.style.top = "580px";
circle.style.left = "205px";
This question is very old but figured I'd add my 2 cents in case someone lands here for the same question. To make the circle spawn at a random location onload, you would need to randomize the x and y variables. The aim of this is to avoid it spawning at the same place all the time thus making the game boring.
var x = Math.floor(Math.random() * canvas.width);
var y = Math.floor(Math.random() * canvas.height);
However, if you leave it like this, you will run into issues where the x and y values result in the ball only moving on a straight cross sectional line along the x-axis or vice versa along the y-axis when the value is too close to the max height or width of the canvas. To work around this issue, create a desired range for the values of your variables using:
Math.floor(Math.random() * (max - min) ) + min;
Example:
canvas.width = 800;
canvas.height = 600;
var x = Math.floor(Math.random() * (700 - 100)) + 100;
var y = Math.floor(Math.random() * (500-100)) + 100;
I'm creating a Canvas animation, and have managed to position x number of circles in a circular path. Here's a simplified version of the code I'm using:
var total = circles.length,
i = 0,
radius = 500,
angle = 0,
step = (2*Math.PI) / total;
for( i; i < total; i++ ) {
var circle = circles[i].children[0],
cRadius = circle[i].r,
x = Math.round(winWidth/2 + radius * Math.cos( angle ) - cRadius),
y = Math.round(winHeight/2 + radius * Math.sin( angle ) - cRadius);
circle.x = x;
circle.y = y;
angle += step;
}
Which results in this:
What I am trying to achieve is for all the circles to be next to each other with no gap between them (except the first and last). The circles sizes (radius) are generated randomly and shouldn't adjust to fit the circular path:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
I expect there to be a gap between the first and last circle.
I'm struggling to get my head around this so any help would be much appreciated :)
Cheers!
Main creation loop :
• take a current radius
• compute the angles it cover ( = atan2(smallRadius/bigRadius) )
• move base angle by this latest angle.
http://jsfiddle.net/dj2v7mbw/9/
function CircledCircle(x, y, radius, margin, subCircleCount, subRadiusMin, subRadiusMax) {
this.x = x;
this.y = y;
this.radius = radius;
this.subCircleCount = subCircleCount;
var circles = this.circles = [];
// build top sub-circles
var halfCount = Math.floor(subCircleCount / 2);
var totalAngle = addCircles(halfCount);
// re-center top circles
var correction = totalAngle / 2 + Math.PI / 2;
for (var i = 0; i < halfCount; i++) this.circles[i].angle -= correction;
// build bottom sub-circles
totalAngle = addCircles(subCircleCount - halfCount);
// re-center bottom circles
var correction = totalAngle / 2 - Math.PI / 2;
for (var i = halfCount; i < subCircleCount; i++) this.circles[i].angle -= correction;
// -- draw this C
this.draw = function (angleShift) {
for (var i = 0; i < this.circles.length; i++) drawDistantCircle(this.circles[i], angleShift);
}
//
function drawDistantCircle(c, angleShift) {
angleShift = angleShift || 0;
var thisX = x + radius * Math.cos(c.angle + angleShift);
var thisY = y + radius * Math.sin(c.angle + angleShift);
ctx.beginPath();
ctx.arc(thisX, thisY, c.r, 0, 2 * Math.PI);
ctx.fillStyle = 'hsl(' + (c.index * 15) + ',75%, 75%)';
ctx.fill();
ctx.stroke();
}
//
function addCircles(cnt) {
var currAngle = 0;
for (var i = 0; i < cnt; i++) {
var thisRadius = getRandomInt(subRadiusMin, subRadiusMax);
var thisAngle = Math.atan2(2 * thisRadius + margin, radius);
var thisCircle = new subCircle(thisRadius, currAngle + thisAngle / 2, i);
currAngle += thisAngle;
circles.push(thisCircle);
}
return currAngle;
}
}
with
function subCircle(radius, angle, index) {
this.r = radius;
this.angle = angle;
this.index = index;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
use with
var myCircles = new CircledCircle(winWidth / 2, winHeight / 2, 350, 2, 24, 5, 50);
myCircles.draw();
animate with :
var angleShift = 0;
function draw() {
requestAnimationFrame(draw);
ctx.clearRect(0, 0, winWidth, winHeight);
myCircles.draw(angleShift);
angleShift += 0.010;
}
draw();
It's something like this, but you're gonna have to figure out the last circle's size:
http://jsfiddle.net/rudiedirkx/ufvf62yf/2/
The main logic:
var firstStep = 0, rad = 0, step = 0;
firstStep = step = stepSize();
for ( var i=0; i<30; i++ ) {
draw.radCircle(rad, step);
rad += step;
step = stepSize();
rad += step;
}
stepSize() creates a random rad between Math.PI/48 and Math.PI/48 + Math.PI/36 (no special reason, but it looked good). You can fix that to be the right sizes.
draw.radCircle(rad, step) creates a circle at position rad of size step (also in rad).
step is added twice per iteration: once to step from current circle's center to its edge and once to find the next circle's center
firstStep is important because you have to know where to stop drawing (because the first circle crosses into negative angle)
I haven't figured out how to make the last circle the perfect size yet =)
There's also a bit of magic in draw.radCircle():
var r = rRad * Math.PI/3 * 200 * .95;
The 200 is obviously the big circle's radius
The 95% is because the circle's edge length is slightly longer than the (straight) radius of every circle
I have no idea why Math.PI/3 is that... I figured it had to be Math.PI/2, which is 1 rad, but that didn't work at all. 1/3 for some reason does..... Explain that!
If you want to animate these circle sizes and keep them aligned, you'll have a hard time. They're all random now. In an animation, only the very first iteration can be random, and the rest is a big mess of cache and math =)