I'm trying to achieve something similar to an air hockey table effect with Famo.us. The idea is to have multiple circle bodies that can collide (see battle).
Right now my first concern is getting the ball's vector attributes to zero out on drag start.
I'm trying to use the 'reset()' method from Particle.reset, but am running into tons of issues. Here's some of the code from the codepen I have so far:
ball.draggable.on("start", function(pos) {
var zeroV = new Vector(0, 0, 0);
// need to remove force here
ball.circle.reset(pos.position, zeroV, ball.circle.orientation, zeroV);
});
Any idea how to best zero out the force on the ball once I begin a drag? Also how might I determine velocity on release relative to how fast the user is dragging before release?
The answer to both of your questions lie in the adding and removing a body particle from the physics engine in Famo.us.
Here is example code: jsBin code
Note: This example does not solve your whole solution, but does answer your questions and should help you to get to your desired effect.
Any idea how to best zero out the force on the ball once I begin a drag?
Rather than zero out the force, you will detach the particle from the engine temporarily.
physicsEngine.removeBody(this.particle);
In the example, I am doing this on click of a created circle surface.
ball.particle = new Circle({
radius: radius,
position: [x, y, 0]
});
ball.physicsID = physicsEngine.addBody(ball.particle);
physicsEngine.attach(collision, balls, ball.particle);
ball.on('click', function(){
if (!this.stopped) {
physicsEngine.removeBody(this.particle);
} else {
this.physicsID = physicsEngine.addBody(this.particle);
physicsEngine.attach(collision, balls, this.particle);
balls.push(this.particle);
}
console.log('balls', balls);
this.stopped = !this.stopped;
});
How might I determine velocity on release relative to how fast the user is dragging before release?
When you drag the square surface and on('end'... you pass the velocity to the creation of your particle. You use the velocity from the drag end to start your particle in motion with setVelocity.
ball.particle.setVelocity(velocity);
As you can see in the example code:
sync.on('end', function(data){
if (!surface.createdBall && data.velocity){
var velocity = data.velocity;
surface.createdBall = true;
var endX = position[0] + 0;
var endY = position[1] + 0;
createBall(endX, endY, velocity);
}
});
...
function createBall(x, y, velocity) {
var ball = new Surface({
size: [radius * 2, radius * 2],
properties: {
backgroundColor: 'blue',
borderRadius: (radius * 2) + 'px'
}
});
ball.particle = new Circle({
radius: radius,
position: [x, y, 0]
});
ball.physicsID = physicsEngine.addBody(ball.particle);
physicsEngine.attach(collision, balls, ball.particle);
node.add(ball.particle).add(ball);
balls.push(ball.particle);
console.log('created ball', velocity);
ball.particle.setVelocity(velocity);
surface.createdBall = false;
ball.on('click', function(){
if (!this.stopped) {
physicsEngine.removeBody(this.particle);
} else {
this.physicsID = physicsEngine.addBody(this.particle);
physicsEngine.attach(collision, balls, this.particle);
balls.push(this.particle);
}
console.log('balls', balls);
this.stopped = !this.stopped;
});
}
Related
I've been trying to draw an arc on the canvas, using p5.js. I got start & end points, the chord length i calculate using pythagoras using the two points, the height & width values are also given.
In order to draw an arc, i need to use the following function;
arc(x, y, w, h, start, stop, [mode], [detail]) for docs refer to here
The start & stop parameters refer to the start&stop angles specified in radians. I can't draw the arc without those angles and i'm unable to calculate them using what i got.
I searched for lots of examples similar to my question, but it is suggested to calculate the center angle, which i'm also unable to do so. Even though i was able to calculate the center angle, how i'm supposed to get the start&stop angles afterwards?
I have drawn some example illustrations on GeoGebra;
The angle of a vector can be calculated by atan2().
Note, that:
tan(alpha) = sin(alpha) / cos(alpha)
If you've a vector (x, y), then than angle (alpha) of the vector relative to the x-axis is:
alpha = atan2(y, x);
The start_angle and stop_angle of an arc, where the center of the arc is (cpt_x, cpt_y), the start point is (spt_x, spt_y) and the end point is (ept_x, ept_y), can be calculated by:
start_angle = atan2(spt_y-cpt_y, spt_x-cpt_x);
stop_angle = atan2(ept_y-cpt_y, ept_x-cpt_x);
See the example, where the stop angle depends on the mouse position:
var sketch = function( p ) {
p.setup = function() {
let sketchCanvas = p.createCanvas(p.windowWidth, p.windowHeight);
sketchCanvas.parent('p5js_canvas')
}
p.windowResized = function() {
p.resizeCanvas(p.windowWidth, p.windowHeight);
}
p.draw = function() {
let cpt = new p5.Vector(p.width/2, p.height/2);
let rad = p.min(p.width/2, p.height/2) * 0.9;
let stop_angle = p.atan2(p.mouseY-cpt.y, p.mouseX-cpt.x);
p.background(192);
p.stroke(255, 64, 64);
p.strokeWeight(3);
p.noFill();
p.arc(cpt.x, cpt.y, rad*2, rad*2, 0, stop_angle);
}
};
var circle = new p5(sketch);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>
<div id="p5js_canvas"></div>
I am drawing circles on an html5 canvas (this.ctx) with the drawCircle function. Now I would like to move the cirlce to a different position with a move Circle function. Is there any way to see the circle move from one place to the other? At this point I am not even sure how to remove the previous circle for a user. Could I assign the arc to an object or so?
customobject.prototype.drawCircle = function drawCircle(userID, x, y) {
var radius = 10;
this.ctx.beginPath();
this.ctx.arc(100, 00, 10, 0, Math.PI*2, true);
this.ctx.closePath();
this.ctx.fillStyle = 'blue';
this.ctx.fill();
this.ctx.lineWidth = 2;
this.ctx.strokeStyle = '#003300';
this.ctx.stroke();
}
customobject.prototype.moveCircle = function moveCircle(userID, x, y) {
}
I did see a way to potentially delete a circle (not animate - move it):
remove circle(x, y, radius){
this.ctx.globalCompositeOperation = 'destination-out'
this.ctx.arc(x, y, radius, 0, Math.PI*2, true);
this.ctx.fill();
}
-> so in this case I would specify the coordinates of the original circle and it would be cut?
I also saw this post on making a circle move. But I don't know how to do that with multiple circles. (Each userID would have a circle)
Removing a Circle from the canvas once it is drawn is not possible a priori, you could draw another circle in the place but with the background color set, but that will fast conflict with other drawn objects.
If I am getting this right, you would like to animate the movement of the circle. That is basically done like that:
var start = new Date().getTime(),
end = start + 1000; //one second of animation
p1 = { x: 0, y: 0 }, //start coordinates
p2 = { x: 100, y: 10 }; // end coordinates
function render (progress) {
var x = p1.x + progress * (p2.x - p1.x),
y = p1.y + progress * (p2.y - p1.y);
clearCanvas();
drawCircle(x,y);
}
function loop () {
var now = new Date().getTime(),
progress = (now - start)/(end - start);
if (progress >= 0 && progress <= 1) {
render(progress);
window.requestAnimationFrame(loop);
}
}
loop();
The basics:
you need an animation loop, no for or while loop, something that uses a timer, setTimeout() or setInterval() would do, but requestAnimationFrame() is made for such things.
Find the progress, in animation this is usually a Number between 0 and 1, where 0 refers to the start, 1 to the end and everything in between the progress.
clear the canvas and re-render everything, depending on the progress.
repeat until the progress is bigger than one.
Working on a project and cannot seem to get my animation right. I will not be showing the code because it simply doesn't work but it would be cool if someone were to give me a few pointers on how to animate a cloud of smoke moving upwards while slowly fading and increasing in size.
This effect should technically repeat once the y value reaches 0 i.o.w. the cloud reaches the top of the canvas.
What I need to know is how do I animate this, and which methods do I use. This is a kind of a self learning assignment.
Thanks in advance.
Here is a Plunker example of sprites growing in size and fading in transparency.
It is done using Pixi.js which actually renders in webgl with a canvas fallback. It should be possible to take the algorithm and apply it to raw canvas (although it would take some work).
var insertAfter = function(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
var range = function(aCount) {
return new Array(aCount)
}
function main() {
var el_main = document.getElementById("animation_main");
var el_div = document.createElement('div');
el_div.setAttribute('id', 'main_stage');
insertAfter(el_div, el_main);
renderer = PIXI.autoDetectRenderer(300, 300, {
transparent: true,
antialias: true
});
el_div.appendChild(renderer.view);
window.stage = new PIXI.Container();
window.stage.x = 0;
window.stage.y = 0;
renderer.render(window.stage);
var s = [];
for (x of range(400)) {
tCircle = new PIXI.Graphics();
tCircle.beginFill(0x000000, 1);
tCircle.s = (Math.random() * 2) + 1;
tCircle.drawCircle(0, 0, 5 - tCircle.s);
tCircle.x = Math.random() * 300
tCircle.y = (Math.random() * 50) + 20
tCircle.endFill();
s.push(tCircle);
window.stage.addChild(tCircle)
}
window.t = 0
animate = function(t) {
d = t - window.t
window.t = t
//Animation Start
for (n in s){
s[n].x += ((s[n].s / 25) * d)
s[n].alpha = 1 - s[n].x / 300
s[n].scale.x = 1 - s[n].alpha
s[n].scale.y = 1 - s[n].alpha
if (s[n].x > 300) {
s[n].x = 0
s[n].y = (Math.random() * 50) + 20
}
}
renderer.render(window.stage)
//Animation End
requestAnimationFrame(animate);
}
requestAnimationFrame(animate)
}
document.addEventListener("DOMContentLoaded", function(e){
main();
});
At the moment all of the tweening is linear ... it might look more realistic with a logarithmic or exponential tween ... but for simplicity i just left it as linear.
Jakob Jenkov has done a really nice on-line book about canvas here:
http://tutorials.jenkov.com/html5-canvas/index.html
Since yours is a learning experience, I would just point you towards:
The basic workflow of html5 Canvas: Anything drawn on the canvas cannot be altered, so all canvas animation requires repeatedly doing these things in an animation loop: (1) clearing the canvas, (2) calculating a new position for your objects, and (3) redrawing the objects in their new positions.
Animations: requestAnimationFrame as a timing loop: http://blog.teamtreehouse.com/efficient-animations-with-requestanimationframe
Transformations: Canvas gives you the ability to scale, rotate and move the origin of its drawing surface.
Styling: Canvas provides all the essential styling tools for drawing--including globalAlpha which sets opacity.
I'm trying to draw a rectangle (or other path) at the cursors position.
The issue is, if you move your mouse fast enough, the drawing lags behind the cursor considerably (chases it).
To reproduce/ test the issue, I've tried to produce code that is as lean as possible. However there's still a noticeable gap [between the cursor and the rectangle] due to rendering latency, even on a computer with decent specs (Chrome Beta 37, Fedora 20, i7 4770k, etc)
Can anyone posit to the cause, or suggest improvements to the following code to reduce latency:
http://jsfiddle.net/AGp2w/4/
var canvas = document.getElementsByTagName('canvas')[0];
var canvasDim = {
width: canvas.width,
height: canvas.height
};
var canvasOffset = canvas.getBoundingClientRect();
var context = canvas.getContext('2d');
context.stroke = "#000000";
context.fill = "#000000";
var currentPosition = {x:0, y:0};
var previousPosition = currentPosition;
var onlyClearPreviousPositon = true;
canvas.onmousemove = function(e){
currentPosition = {
x: e.clientX - canvasOffset.left,
y: e.clientY - canvasOffset.top
};
};
function drawAtCursor(){
if (onlyClearPreviousPositon){
// experiment - try not clearing the whole canvas
context.clearRect(previousPosition.x - 4, previousPosition.y - 4, 8, 8);
previousPosition = currentPosition;
} else {
context.clearRect(0, 0, canvasDim.width, canvasDim.height);
}
context.fillRect(currentPosition.x - 4, currentPosition.y - 4, 8, 8);
window.requestAnimationFrame(drawAtCursor);
}
drawAtCursor();
This has a tiny bit less latency, but is not useful in a real app:
function handleMouseMove(e){
ctx.clearRect(mouseX-1,mouseY-1,9,9);
mouseX=e.clientX-offsetX;
mouseY=e.clientY-offsetY;
ctx.fillRect(mouseX,mouseY,8,8);
}
The mouse pointer will always be quicker than drawing, so your best bet is not to give the user's eye a reason to perceive latency:
Turn off the mouse cursor while the user is drawing.
http://jsfiddle.net/m1erickson/Cf5TX/
The moving rectangle will act as the mouse cursor, but if the user needs a visual guide, you can:
Also draw crosshairs using a couple of lines.
I have a paint-style application that works with touch events.
The JavaScript code is...
var RADIUS = 10;
var ctx = document.querySelector("canvas").getContext("2d");
var getRandomColorFragment = function () {
return Math.floor(Math.random() * 255);
};
document.body.addEventListener("touchstart", function (event) {
ctx.fillStyle = "rgb(" + [getRandomColorFragment(), getRandomColorFragment(), getRandomColorFragment()].join() + ")";
});
document.body.addEventListener("touchmove", function (event) {
// Prevent default behavior of scrolling elements.
event.preventDefault();
// Get a reference to the first touch placed.
var touch = event.touches[0];
// Draw a circle at the location of the finger.
ctx.beginPath();
ctx.arc(touch.pageX - RADIUS, touch.pageY - RADIUS, RADIUS, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
});
jsFiddle.
You can test this on a platform that doesn't support touch events by using Chrome and opening the Web Inspector's settings and choosing Emulate touch events.
When the finger/pointer moves very fast, it fails to paint the canvas continually like which would be expected (see screenshot above). It seems I can only get the touches' coordinates only as fast as the touchmove event is triggered.
I thought that I could determine if there is a big enough gap between arcs using the Distance Formula (c2 = a2 + b2) and determining if it's larger than the RADIUS constant. This part worked well. The next thing I needed to figure out is how to interpolate between the two points: the previous registered touch's coordinates and the newly registered coordinates'.
So, essentially, I'd like to know how I could interpolate between the two points I have to determine how to fill-in the gap.
You want to draw a line from last mouse position to the new mouse postition. But you are drawing a circle at the new position instead. Why?
Why don't you do it this way?
onTouchStart: ctx.moveTo(x,y);
onTouchMove: ctx.lineTo(x,y);
All interpolation, antialiasing etc. is already included in "lineTo" function. Use ctx.lineCap = "round"; to have round corners.
If you still want to interpolate numbers, here is the function:
function interpolate(a, b, frac) // points A and B, frac between 0 and 1
{
var nx = a.x+(b.x-a.x)*frac;
var ny = a.y+(b.y-a.y)*frac;
return {x:nx, y:ny};
}
interpolate({x:2, y:2}, {x:4,y:4}, 0.5); // returns {x:3, y:3}