Ease in/out camera movement for canvas - javascript

I have a function which moves a camera to a position that the user clicks on the canvas. But currently the movement is linear. I would like it to use an ease in/out transition but am having difficulty understanding how to implement it with my current function.
This is the code I have:
animate.mouseEvent = function(e,el){
var mousePos = mouse.relativePosition(e,el);
var parent = el;
if(e.button == 0){
function update(){
if(centerX == mousePos.x && centerY == mousePos.y){
clearInterval(parent.timer);
//center X / Y is canvas width & height halved
}
var difx = centerX - mousePos.x,
dify = centerY - mousePos.y,
distance = Math.sqrt(difx*difx + dify*dify),
normalX = difx/distance,
normalY = dify/distance,
speed = 1, //currently linear [replace with ease in/out]
x = normalX * speed,
y = normalY * speed;
updateOffsets(x,y);
mousePos.x += x;
mousePos.y += y;
}
parent.timer = setInterval(update,1);
}
}
animate.updateOffsets = function(x,y){
canvas.offsetX -= x;
canvas.offsetY -= y;
}
So i was wondering how i would implement an ease in/out from my current linear method. Using the below function:
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
I don't fully know how to implement it or what exactly it would be returning for me to calculate the new offset with. Was hoping some one could explain.

This should give you an idea. I used another easing function, you can substitute your own. Example
var element = document.getElementById('moving');
var bg = document.getElementById('background');
bg.addEventListener('click', function(evt) {
animate({
x: evt.offsetX,
y: evt.offsetY
});
}, false);
function getPosition() {
return {
x: element.offsetLeft,
y: element.offsetTop
};
}
function setPosition(x, y) {
element.style.left = x + 'px';
element.style.top = y + 'px';
}
function easing(x) {
return 0.5 + 0.5 * Math.sin((x - 0.5) * Math.PI);
}
function animate(target) {
var initial = getPosition();
var initialX = initial.x;
var initialY = initial.y;
var targetX = target.x;
var targetY = target.y;
var deltaX = targetX - initialX;
var deltaY = targetY - initialY;
var timeStart = timestamp();
var timeLength = 800;
var timer = setInterval(update, 10);
function timestamp() {
return Date.now();
}
function stop() {
clearInterval(timer);
}
function update() {
var t = (timestamp() - timeStart) / timeLength;
if (t > 1) {
fraction(1);
stop();
} else {
fraction(easing(t));
}
}
function fraction(t) {
setPosition(initialX + t * deltaX, initialY + t * deltaY);
}
}
EDIT
Applied to the fiddle you provided:
var element = document.getElementById('background');
var ctx = element.getContext("2d");
var camera = {};
// camera.offset marks the position seen at the upper left corner
// (It would be better if it marked the center)
// Let these be negative, so (0,0) scene position is at the center of the image
camera.offsetX = -element.width/2;
camera.offsetY = -element.height/2;
var obj = {};
obj.x = 50;
obj.y = 50;
element.addEventListener('click', animate, false);
function easing(x) {
return 0.5 + 0.5 * Math.sin((x - 0.5) * Math.PI);
}
function animate(evt){
// Transform click position from screen coordinates to scene coordinates
var targetX = evt.offsetX + camera.offsetX - element.width / 2,
targetY = evt.offsetY + camera.offsetY - element.height / 2;
var initialX = camera.offsetX,
initialY = camera.offsetY;
var deltaX = targetX - initialX,
deltaY = targetY - initialY;
var timeStart = Date.now();
var timeLength = 800;
var timer = setInterval(update, 10);
function stop(){
clearInterval(timer);
}
function update(){
var t = (Date.now() - timeStart) / timeLength;
if (t > 1) {
fraction(1);
stop();
} else {
fraction(easing(t));
}
}
function fraction(t){
camera.offsetX = initialX + t * deltaX,
camera.offsetY = initialY + t * deltaY;
}
}
function draw(){
ctx.clearRect(0,0,element.width,element.height);
ctx.fillStyle = 'red';
ctx.fillRect((element.width/2)-2,(element.height/2)-2,4,4);
ctx.fillStyle = 'blue';
// ===> Here the size and position arguments were swapped
ctx.fillRect(obj.x-camera.offsetX,obj.y-camera.offsetY, 20, 20);
}
// Consider using requestAnimationFrame
setInterval(draw, 10);

Related

Why does html canvas line animation work but not after already drawing a line

I can draw a line dot by dot, but if I draw the line fully first, then try the dots, all the dots come at once.
So see code, in the window_onload function, if I instead call pathAll instead of startRedDots you can see what I mean. It will draw the line, then all the red dots come at once and not sure why.
<script type="text/javascript">
var pauseAmount = 300;
var m1, c1;
var pathInt;
var points1 = [new Vector(10, 10),new Vector(500, 500)];
function Vector(x, y)
{
this.X = x;
this.Y = y;
}
var counter;
var startX;
var startY;
var endX;
var endY;
var amount;
function resetVars() {
counter=0;
startX = points1[counter].X;
startY = points1[counter].Y;
endX = points1[counter+1].X;
endY = points1[counter+1].Y;
amount = 0;
c1.strokeStyle = "lightgrey";
c1.lineWidth = 15;
c1.lineCap = "round";
c1.setLineDash([1, 30]);
}
window.onload = function()
{
m1 = document.querySelector("#b");
c1 = m1.getContext("2d");
resetVars();
//pathInt = setTimeout(function(){ pathAll(); }, 1);
pathInt = setInterval(function(){ startRedDots(); }, pauseAmount);
};
function pathAll() {
c1.moveTo(startX, startY);
c1.lineTo(endX, endY);
c1.stroke();
resetVars();
setTimeout(function(){ startRedDots(); }, 2500);
}
function startRedDots() {
c1.strokeStyle = "red";;
pathInt = setInterval(function(){ oneAtATime(); }, pauseAmount);
}
function oneAtATime() {
c1.moveTo(startX, startY);
c1.lineTo(startX + (endX - startX) * amount, startY + (endY - startY) * amount);
//alert(startX + (endX - startX) * amount);
if (points1[counter+1].X < points1[counter].X) {
if(points1[counter+1].X < Math.round(eval(startX + (endX - startX) * amount))) {
c1.stroke();
}
else {
clearInterval(pathInt);
}
}
else if (points1[counter+1].X > points1[counter].X) {
if(points1[counter+1].X > Math.round(eval(startX + (endX - startX) * amount))) {
//alert(startX + (endX - startX) * amount);
c1.stroke();
}
else {
clearInterval(pathInt);
}
}
else if (points1[counter+1].X == points1[counter].X) {
if(points1[counter+1].X == Math.round(eval(startX + (endX - startX) * amount))) {
c1.stroke();
}
else {
clearInterval(pathInt);
}
}
amount += 0.01;
}
</script>
<canvas id="b" width="600" height="600" style="position:absolute;top:50px;left:50px;"></canvas>
OK I have it working if anyone interested...just need to add 'c1.beginPath();' as first line in oneAtATime function.

hyperdrive effect in canvas across randomly placed circles

I'm trying to create a hyperdrive effect, like from Star Wars, where the stars have a motion trail. I've gotten as far as creating the motion trail on a single circle, it still looks like the trail is going down in the y direction and not forwards or positive in the z direction.
Also, how could I do this with (many) randomly placed circles as if they were stars?
My code is on jsfiddle (https://jsfiddle.net/5m7x5zxu/) and below:
var canvas = document.querySelector("canvas");
var context = canvas.getContext("2d");
var xPos = 180;
var yPos = 100;
var motionTrailLength = 16;
var positions = [];
function storeLastPosition(xPos, yPos) {
// push an item
positions.push({
x: xPos,
y: yPos
});
//get rid of first item
if (positions.length > motionTrailLength) {
positions.pop();
}
}
function update() {
context.clearRect(0, 0, canvas.width, canvas.height);
for (var i = positions.length-1; i > 0; i--) {
var ratio = (i - 1) / positions.length;
drawCircle(positions[i].x, positions[i].y, ratio);
}
drawCircle(xPos, yPos, "source");
var k=2;
storeLastPosition(xPos, yPos);
// update position
if (yPos > 125) {
positions.pop();
}
else{
yPos += k*1.1;
}
requestAnimationFrame(update);
}
update();
function drawCircle(x, y, r) {
if (r == "source") {
r = 1;
} else {
r*=1.1;
}
context.beginPath();
context.arc(x, y, 3, 0, 2 * Math.PI, true);
context.fillStyle = "rgba(255, 255, 255, " + parseFloat(1-r) + ")";
context.fill();
}
Canvas feedback and particles.
This type of FX can be done many ways.
You could just use a particle systems and draw stars (as lines) moving away from a central point, as the speed increase you increase the line length. When at low speed the line becomes a circle if you set ctx.lineWidth > 1 and ctx.lineCap = "round"
To add to the FX you can use render feedback as I think you have done by rendering the canvas over its self. If you render it slightly larger you get a zoom FX. If you use ctx.globalCompositeOperation = "lighter" you can increase the stars intensity as you speed up to make up for the overall loss of brightness as stars move faster.
Example
I got carried away so you will have to sift through the code to find what you need.
The particle system uses the Point object and a special array called bubbleArray to stop GC hits from janking the animation.
You can use just an ordinary array if you want. The particles are independent of the bubble array. When they have moved outside the screen they are move to a pool and used again when a new particle is needed. The update function moves them and the draw Function draws them I guess LOL
The function loop is the main loop and adds and draws particles (I have set the particle count to 400 but should handle many more)
The hyper drive is operated via the mouse button. Press for on, let go for off. (It will distort the text if it's being displayed)
The canvas feedback is set via that hyperSpeed variable, the math is a little complex. The sCurce function just limits the value to 0,1 in this case to stop alpha from going over or under 1,0. The hyperZero is just the sCurve return for 1 which is the hyper drives slowest speed.
I have pushed the feedback very close to the limit. In the first few lines of the loop function you can set the top speed if(mouse.button){ if(hyperSpeed < 1.75){ Over this value 1.75 and you will start to get bad FX, at about 2 the whole screen will just go white (I think that was where)
Just play with it and if you have questions ask in the comments.
const ctx = canvas.getContext("2d");
// very simple mouse
const mouse = {x : 0, y : 0, button : false}
function mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));
// High performance array pool using buubleArray to separate pool objects and active object.
// This is designed to eliminate GC hits involved with particle systems and
// objects that have short lifetimes but used often.
// Warning this code is not well tested.
const bubbleArray = () => {
const items = [];
var count = 0;
return {
clear(){ // warning this dereferences all locally held references and can incur Big GC hit. Use it wisely.
this.items.length = 0;
count = 0;
},
update() {
var head, tail;
head = tail = 0;
while(head < count){
if(items[head].update() === false) {head += 1 }
else{
if(tail < head){
const temp = items[head];
items[head] = items[tail];
items[tail] = temp;
}
head += 1;
tail += 1;
}
}
return count = tail;
},
createCallFunction(name, earlyExit = false){
name = name.split(" ")[0];
const keys = Object.keys(this);
if(Object.keys(this).indexOf(name) > -1){ throw new Error(`Can not create function name '${name}' as it already exists.`) }
if(!/\W/g.test(name)){
let func;
if(earlyExit){
func = `var items = this.items; var count = this.getCount(); var i = 0;\nwhile(i < count){ if (items[i++].${name}() === true) { break } }`;
}else{
func = `var items = this.items; var count = this.getCount(); var i = 0;\nwhile(i < count){ items[i++].${name}() }`;
}
!this.items && (this.items = items);
this[name] = new Function(func);
}else{ throw new Error(`Function name '${name}' contains illegal characters. Use alpha numeric characters.`) }
},
callEach(name){var i = 0; while(i < count){ if (items[i++][name]() === true) { break } } },
each(cb) { var i = 0; while(i < count){ if (cb(items[i], i++) === true) { break } } },
next() { if (count < items.length) { return items[count ++] } },
add(item) {
if(count === items.length){
items.push(item);
count ++;
}else{
items.push(items[count]);
items[count++] = item;
}
return item;
},
getCount() { return count },
}
}
// Helpers rand float, randI random Int
// doFor iterator
// sCurve curve input -Infinity to Infinity out -1 to 1
// randHSLA creates random colour
// CImage, CImageCtx create image and image with context attached
const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
const rand = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;
const doFor = (count, cb) => { var i = 0; while (i < count && cb(i++) !== true); }; // the ; after while loop is important don't remove
const sCurve = (v,p) => (2 / (1 + Math.pow(p,-v))) -1;
const randHSLA = (h, h1, s = 100, s1 = 100, l = 50, l1 = 50, a = 1, a1 = 1) => { return `hsla(${randI(h,h1) % 360},${randI(s,s1)}%,${randI(l,l1)}%,${rand(a,a1)})` }
const CImage = (w = 128, h = w) => (c = document.createElement("canvas"),c.width = w,c.height = h, c);
const CImageCtx = (w = 128, h = w) => (c = CImage(w,h), c.ctx = c.getContext("2d"), c);
// create image to hold text
var textImage = CImageCtx(1024, 1024);
var c = textImage.ctx;
c.fillStyle = "#FF0";
c.font = "64px arial black";
c.textAlign = "center";
c.textBaseline = "middle";
const text = "HYPER,SPEED FX,VII,,Battle of Jank,,Hold the mouse,button to increase,speed.".split(",");
text.forEach((line,i) => { c.fillText(line,512,i * 68 + 68) });
const maxLines = text.length * 68 + 68;
function starWarIntro(image,x1,y1,x2,y2,pos){
var iw = image.width;
var ih = image.height;
var hh = (x2 - x1) / (y2 - y1); // Slope of left edge
var w2 = iw / 2; // half width
var z1 = w2 - x1; // Distance (z) to first line
var z2 = (z1 / (w2 - x2)) * z1 - z1; // distance (z) between first and last line
var sk,t3,t3a,z3a,lines, z3, dd = 0, a = 0, as = 2 / (y2 - y1);
for (var y = y1; y < y2 && dd < maxLines; y++) { // for each line
t3 = ((y - y1) * hh) + x1; // get scan line top left edge
t3a = (((y+1) - y1) * hh) + x1; // get scan line bottom left edge
z3 = (z1 / (w2 - t3)) * z1; // get Z distance to top of this line
z3a = (z1 / (w2 - t3a)) * z1; // get Z distance to bottom of this line
dd = ((z3 - z1) / z2) * ih; // get y bitmap coord
a += as;
ctx.globalAlpha = a < 1 ? a : 1;
dd += pos; // kludge for this answer to make text move
// does not move text correctly
lines = ((z3a - z1) / z2) * ih-dd; // get number of lines to copy
ctx.drawImage(image, 0, dd , iw, lines, t3, y, w - t3 * 2, 1.5);
}
}
// canvas settings
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
// diagonal distance used to set point alpha (see point update)
var diag = Math.sqrt(w * w + h * h);
// If window size is changed this is called to resize the canvas
// It is not called via the resize event as that can fire to often and
// debounce makes it feel sluggish so is called from main loop.
function resizeCanvas(){
points.clear();
canvas.width = innerWidth;
canvas.height = innerHeight;
w = canvas.width;
h = canvas.height;
cw = w / 2; // center
ch = h / 2;
diag = Math.sqrt(w * w + h * h);
}
// create array of points
const points = bubbleArray();
// create optimised draw function itterator
points.createCallFunction("draw",false);
// spawns a new star
function spawnPoint(pos){
var p = points.next();
p = points.add(new Point())
if (p === undefined) { p = points.add(new Point()) }
p.reset(pos);
}
// point object represents a single star
function Point(pos){ // this function is duplicated as reset
if(pos){
this.x = pos.x;
this.y = pos.y;
this.dead = false;
}else{
this.x = 0;
this.y = 0;
this.dead = true;
}
this.alpha = 0;
var x = this.x - cw;
var y = this.y - ch;
this.dir = Math.atan2(y,x);
this.distStart = Math.sqrt(x * x + y * y);
this.speed = rand(0.01,1);
this.col = randHSLA(220,280,100,100,50,100);
this.dx = Math.cos(this.dir) * this.speed;
this.dy = Math.sin(this.dir) * this.speed;
}
Point.prototype = {
reset : Point, // resets the point
update(){ // moves point and returns false when outside
this.speed *= hyperSpeed; // increase speed the more it has moved
this.x += Math.cos(this.dir) * this.speed;
this.y += Math.sin(this.dir) * this.speed;
var x = this.x - cw;
var y = this.y - ch;
this.alpha = (Math.sqrt(x * x + y * y) - this.distStart) / (diag * 0.5 - this.distStart);
if(this.alpha > 1 || this.x < 0 || this.y < 0 || this.x > w || this.h > h){
this.dead = true;
}
return !this.dead;
},
draw(){ // draws the point
ctx.strokeStyle = this.col;
ctx.globalAlpha = 0.25 + this.alpha *0.75;
ctx.beginPath();
ctx.lineTo(this.x - this.dx * this.speed, this.y - this.dy * this.speed);
ctx.lineTo(this.x, this.y);
ctx.stroke();
}
}
const maxStarCount = 400;
const p = {x : 0, y : 0};
var hyperSpeed = 1.001;
const alphaZero = sCurve(1,2);
var startTime;
function loop(time){
if(startTime === undefined){
startTime = time;
}
if(w !== innerWidth || h !== innerHeight){
resizeCanvas();
}
// if mouse down then go to hyper speed
if(mouse.button){
if(hyperSpeed < 1.75){
hyperSpeed += 0.01;
}
}else{
if(hyperSpeed > 1.01){
hyperSpeed -= 0.01;
}else if(hyperSpeed > 1.001){
hyperSpeed -= 0.001;
}
}
var hs = sCurve(hyperSpeed,2);
ctx.globalAlpha = 1;
ctx.setTransform(1,0,0,1,0,0); // reset transform
//==============================================================
// UPDATE the line below could be the problem. Remove it and try
// what is under that
//==============================================================
//ctx.fillStyle = `rgba(0,0,0,${1-(hs-alphaZero)*2})`;
// next two lines are the replacement
ctx.fillStyle = "Black";
ctx.globalAlpha = 1-(hs-alphaZero) * 2;
//==============================================================
ctx.fillRect(0,0,w,h);
// the amount to expand canvas feedback
var sx = (hyperSpeed-1) * cw * 0.1;
var sy = (hyperSpeed-1) * ch * 0.1;
// increase alpha as speed increases
ctx.globalAlpha = (hs-alphaZero)*2;
ctx.globalCompositeOperation = "lighter";
// draws feedback twice
ctx.drawImage(canvas,-sx, -sy, w + sx*2 , h + sy*2)
ctx.drawImage(canvas,-sx/2, -sy/2, w + sx , h + sy)
ctx.globalCompositeOperation = "source-over";
// add stars if count < maxStarCount
if(points.getCount() < maxStarCount){
var cent = (hyperSpeed - 1) *0.5; // pulls stars to center as speed increases
doFor(10,()=>{
p.x = rand(cw * cent ,w - cw * cent); // random screen position
p.y = rand(ch * cent,h - ch * cent);
spawnPoint(p)
})
}
// as speed increases make lines thicker
ctx.lineWidth = 2 + hs*2;
ctx.lineCap = "round";
points.update(); // update points
points.draw(); // draw points
ctx.globalAlpha = 1;
// scroll the perspective star wars text FX
var scrollTime = (time - startTime) / 5 - 2312;
if(scrollTime < 1024){
starWarIntro(textImage,cw - h * 0.5, h * 0.2, cw - h * 3, h , scrollTime );
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>
Here's another simple example, based mainly on the same idea as Blindman67, concetric lines moving away from center at different velocities (the farther from center, the faster it moves..) also no recycling pool here.
"use strict"
var c = document.createElement("canvas");
document.body.append(c);
var ctx = c.getContext("2d");
var w = window.innerWidth;
var h = window.innerHeight;
var ox = w / 2;
var oy = h / 2;
c.width = w; c.height = h;
const stars = 120;
const speed = 0.5;
const trailLength = 90;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = "#fff"
ctx.fillRect(ox, oy, 1, 1);
init();
function init() {
var X = [];
var Y = [];
for(var i = 0; i < stars; i++) {
var x = Math.random() * w;
var y = Math.random() * h;
X.push( translateX(x) );
Y.push( translateY(y) );
}
drawTrails(X, Y)
}
function translateX(x) {
return x - ox;
}
function translateY(y) {
return oy - y;
}
function getDistance(x, y) {
return Math.sqrt(x * x + y * y);
}
function getLineEquation(x, y) {
return function(n) {
return y / x * n;
}
}
function drawTrails(X, Y) {
var count = 1;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, w, h);
function anim() {
for(var i = 0; i < X.length; i++) {
var x = X[i];
var y = Y[i];
drawNextPoint(x, y, count);
}
count+= speed;
if(count < trailLength) {
window.requestAnimationFrame(anim);
}
else {
init();
}
}
anim();
}
function drawNextPoint(x, y, step) {
ctx.fillStyle = "#fff";
var f = getLineEquation(x, y);
var coef = Math.abs(x) / 100;
var dist = getDistance( x, y);
var sp = speed * dist / 100;
for(var i = 0; i < sp; i++) {
var newX = x + Math.sign(x) * (step + i) * coef;
var newY = translateY( f(newX) );
ctx.fillRect(newX + ox, newY, 1, 1);
}
}
body {
overflow: hidden;
}
canvas {
position: absolute;
left: 0;
top: 0;
}

Canvas: Movement only allowed on X axis?

The Problem
I am creating a game where you have to move away from or dodge projectiles coming towards you, I have enabled the user to move the image they are controlling but at the moment the user can place the image anywhere on the canvas.
The Question
How can I only allow the user to move along designated part of the canvas and only along the X axis? for example:
Here is my game, "working progress":
The user is in control of the ship, they should only be able to move left or right like this for example:
The Code
<script>
var game = create_game();
game.init();
//music
var snd = new Audio("menu.mp3");
snd.play();
document.getElementById('mute').addEventListener('click', function (evt) {
if ( snd.muted ) {
snd.muted = false
evt.target.innerHTML = 'mute'
}
else {
snd.muted = true
evt.target.innerHTML = 'unmute'
}
})
function create_game() {
debugger;
var level = 1;
var projectiles_per_level = 1;
var min_speed_per_level = 1;
var max_speed_per_level = 2;
var last_projectile_time = 0;
var next_projectile_time = 0;
var width = 600;
var height = 500;
var delay = 1000;
var item_width = 30;
var item_height = 30;
var total_projectiles = 0;
var projectile_img = new Image();
var projectile_w = 30;
var projectile_h = 30;
var player_img = new Image();
var c, ctx;
var projectiles = [];
var player = {
x: 200,
y: 400,
score: 0
};
function init() {
projectile_img.src = "apple.png";
player_img.src = "basket.png";
level = 1;
total_projectiles = 0;
projectiles = [];
c = document.getElementById("c");
ctx = c.getContext("2d");
ctx.fillStyle = "#ff6600";
ctx.fillRect(0, 0, 500, 600);
c.addEventListener("mousemove", function (e) {
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
player.x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - player_img.width / 2;
player.y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - player_img.height / 2;
}, false);
setupProjectiles();
requestAnimationFrame(tick);
}
function setupProjectiles() {
var max_projectiles = level * projectiles_per_level;
while (projectiles.length < max_projectiles) {
initProjectile(projectiles.length);
}
}
function initProjectile(index) {
var max_speed = max_speed_per_level * level;
var min_speed = min_speed_per_level * level;
projectiles[index] = {
x: Math.round(Math.random() * (width - 2 * projectile_w)) + projectile_w,
y: -projectile_h,
v: Math.round(Math.random() * (max_speed - min_speed)) + min_speed,
delay: Date.now() + Math.random() * delay
}
total_projectiles++;
}
function collision(projectile) {
if (projectile.y + projectile_img.height < player.y + 74) {
return false;
}
if (projectile.y > player.y + 74) {
return false;
}
if (projectile.x + projectile_img.width < player.x + 177) {
return false;
}
if (projectile.x > player.x + 177) {
return false;
}
return true;
}
function maybeIncreaseDifficulty() {
level = Math.max(1, Math.ceil(player.score / 10));
setupProjectiles();
}
function tick() {
var i;
var projectile;
var dateNow = Date.now();
c.width = c.width;
for (i = 0; i < projectiles.length; i++) {
projectile = projectiles[i];
if (dateNow > projectile.delay) {
projectile.y += projectile.v;
if (collision(projectile)) {
initProjectile(i);
player.score++;
} else if (projectile.y > height) {
initProjectile(i);
} else {
ctx.drawImage(projectile_img, projectile.x, projectile.y);
}
}
}
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "#ff6600";
ctx.fillText(player.score, c.width - 50, 50);
ctx.fillText("Level: " + level, 20, 50);
ctx.drawImage(player_img, player.x, player.y);
maybeIncreaseDifficulty();
requestAnimationFrame(tick);
}
return {
init: init
};
}
</script>
JSFiddle (Broken)
https://jsfiddle.net/3oc4jsf6/10/
If your ship is tied to mouse movement, and you want to only allow movement across the X-axis you can simply avoid changing its .y property in your mousemove listener.
Remove this line:
player.y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - player_img.height / 2;

how can i make two objects belonging to the same array move independently of each other using javascript and the canvas tag?

I am trying to create a blackhole simulation, where all the balls that are outside of it go away from it at a given speed and those that fall on it are dragged towards the circle until they reach the center of it, where they would stop and disappear, here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>blackhole simulation escape velocity</title>
<script>
var canvas, ctx;
var blackhole;
var circle;
var circles = new Array();
var G = 6.67e-11, //gravitational constant
pixel_G = G / 1e-11,
c = 3e8, //speed of light (m/s)
M = 12e31, // masseof the blackhole in kg (60 solar masses)
pixel_M = M / 1e32
Rs = (2 * G * M) / 9e16, //Schwarzchild radius
pixel_Rs = Rs / 1e3, // scaled radius
ccolor = 128;
function update() {
var pos, i, distance, somethingMoved = false;
for (i = 0; i < circles.length; i++) {
pos = circles[i].position;
distance = Math.sqrt(((pos.x - 700) * (pos.x - 700)) + ((pos.y - 400) * (pos.y - 400)));
if (distance > pixel_Rs-5 ) {
var delta = new Vector2D(0, 0);
var forceDirection = Math.atan2(pos.y - 400, pos.x - 700);
var evelocity = Math.sqrt((2 * pixel_G * pixel_M) / (distance * 1e-2));
delta.x += Math.cos(forceDirection) * evelocity;
delta.y += Math.sin(forceDirection) * evelocity;
pos.x += delta.x;
pos.y += delta.y;
somethingMoved = true;
} else {
var delta2 = new Vector2D (0,0);
var forceDirection2 = Math.atan2(pos.y - 400, pos.x - 700);
var g = (pixel_G*pixel_M)/(distance*distance*1e2);
delta2.x += Math.cos(forceDirection2)*g;
delta2.y += Math.sin(forceDirection2)*g;
pos.x -= delta2.x;
pos.y -= delta2.y;
somethingMoved = true;
circles[i].color -= 1;
if (pos.x == 700 && pos.y == 400){
somethingMoved = false;
};
}
}
if (somethingMoved) {
drawEverything();
requestAnimationFrame(update);
};
}
function drawEverything() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
blackhole.draw(ctx);
for (var i = 0; i < circles.length; i++) {
circles[i].draw(ctx);
}
}
function init(event) {
canvas = document.getElementById("space");
ctx = canvas.getContext('2d');
blackhole = new Ball(pixel_Rs, { x: 700,
y: 400 }, 0);
for (var i = 0; i < 200; i++) {
var vec2D = new Vector2D(Math.floor(Math.random() * 1400), Math.floor(Math.random() * 800));
circle = new Ball(5, vec2D, ccolor);
circles.push(circle);
}
drawEverything();
requestAnimationFrame(update);
}
function Ball(radius, position, color) {
this.radius = radius;
this.position = position;
this.color = color;
}
Ball.prototype.draw = function(ctx) {
var c=parseInt(this.color);
ctx.fillStyle = 'rgba(' + c + ',' + c + ',' + c + ',1)';
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
};
function Vector2D(x, y) {
this.x = x;
this.y = y;
}
function onClick (){
canvas = document.getElementById ('space');
ctx = canvas.getContext ('2d')
canvas.addEventListener ("mousedown", init, false)
blackhole = new Ball (5, {x: 700,
y: 400 }, 0);
blackhole.draw (ctx) ;
}
window.onload = onClick;
</script>
<style>
body {
background-color:#021c36 ;
margin: 0px;
}
</style>
</head>
<body>
<canvas id = "space", width = "1400", height = "800">
</canvas>
</body>
</html>
Now as you can see, I created a second variable called delta2, but the problem is that it can't update the position of the circles, which in term makes it impossible to move the circle, can someone tell me what is wrong. Also, how can I make the big black circle after a certain amount of time, i know i probably should create a timer, but i don't know how they work
The gravity is too weak. I put a pseudo gravity to demonstrate.
var canvas, ctx;
var blackhole;
var circle;
var circles = new Array();
var bh = {
w:500,
h:300
};
bh.cx = Math.floor(bh.w/2);
bh.cy = Math.floor(bh.h/2)
var G = 6.67e-11, //gravitational constant
pixel_G = G / 1e-11,
c = 3e8, //speed of light (m/s)
M = 12e31, // masseof the blackhole in kg (60 solar masses)
pixel_M = M / 1e32
Rs = (2 * G * M) / 9e16, //Schwarzchild radius
pixel_Rs = Rs / 1e3, // scaled radius
ccolor = 128;
function update() {
var pos, i, distance, somethingMoved = false;
for (i = 0; i < circles.length; i++) {
pos = circles[i].position;
distance = Math.sqrt(((pos.x - bh.cx) * (pos.x - bh.cx)) + ((pos.y - bh.cy) * (pos.y - bh.cy)));
if (distance > pixel_Rs - 5) {
var delta = new Vector2D(0, 0);
var forceDirection = Math.atan2(pos.y - bh.cy, pos.x - bh.cx);
var evelocity = Math.sqrt((2 * pixel_G * pixel_M) / (distance * 1e-2));
delta.x += Math.cos(forceDirection) * evelocity;
delta.y += Math.sin(forceDirection) * evelocity;
pos.x += delta.x;
pos.y += delta.y;
somethingMoved = true;
} else {
var delta2 = new Vector2D(0, 0);
var forceDirection2 = Math.atan2(pos.y - bh.cy, pos.x - bh.cx);
// FIX THIS!!!
var g = 1;//(pixel_G * pixel_M) / (distance * distance * 1e2);
delta2.x += Math.cos(forceDirection2) * g;
delta2.y += Math.sin(forceDirection2) * g;
pos.x -= delta2.x;
pos.y -= delta2.y;
somethingMoved = true;
circles[i].color -= 1;
if (pos.x == bh.cx && pos.y == bh.cy) {
somethingMoved = false;
};
}
}
if (somethingMoved) {
drawEverything();
requestAnimationFrame(update);
};
}
function drawEverything() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
blackhole.draw(ctx);
for (var i = 0; i < circles.length; i++) {
circles[i].draw(ctx);
}
}
function init(event) {
canvas = document.getElementById("space");
canvas.width = bh.w;
canvas.height = bh.h;
ctx = canvas.getContext('2d');
blackhole = new Ball(5, { //pixel_Rs, {
x: bh.cx,
y: bh.cy
}, 0);
for (var i = 0; i < 200; i++) {
var vec2D = new Vector2D(Math.floor(Math.random() * bh.w), Math.floor(Math.random() * bh.h));
circle = new Ball(5, vec2D, ccolor);
circles.push(circle);
}
drawEverything();
requestAnimationFrame(update);
}
function Ball(radius, position, color) {
this.radius = radius;
this.position = position;
this.color = color;
}
Ball.prototype.draw = function(ctx) {
var c = parseInt(this.color);
ctx.fillStyle = 'rgba(' + c + ',' + c + ',' + c + ',1)';
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
};
function Vector2D(x, y) {
this.x = x;
this.y = y;
}
function onClick() {
canvas = document.getElementById('space');
ctx = canvas.getContext('2d')
canvas.addEventListener("mousedown", init, false)
blackhole = new Ball(5, {
x: bh.cx,
y: bh.cy
}, 0);
blackhole.draw(ctx);
}
window.onload = onClick;
body {
background-color: #021c36;
margin: 0px;
}
<canvas id="space" , width="700" , height="400"></canvas>

Angle of rotation of a needle following the cursor

Hello I am trying to write a simple program for fun.. But I am not able to get the angle of rotation and convert it into the number where the needle lands after it follows the crusor.I am letting the needle follow inside the dial. I am including my javascript code. Can anybody help me please?
function alienEye(x, y, size, append, img, theNum) {
var self = this;
var i = 0;
var myintID;
this.x = x;
this.y = y;
this.size = size;
//Create the Eye Dom Node using canvas.
this.create = function create(x, y, size, append) {
//Create dom node
var eye = document.createElement('canvas');
eye.width = size;
eye.height = size;
eye.style.position = 'relative';
eye.style.top = y + 'px';
eye.style.left = x + 'px';
document.getElementById(append).appendChild(eye);
//Get canvas
canvas = eye.getContext("2d")
radius = size / 2;
//draw eye
//canvas.beginPath();
//canvas.arc(radius, radius, radius, 0, Math.PI*2, true);
//canvas.closePath();
//canvas.fillStyle = "rgb(255,255,255)";
//canvas.fill();
//draw pupil
//canvas.beginPath();
//canvas.arc(radius, radius/2, radius/4, 0, Math.PI*2, true);
//canvas.closePath();
//canvas.fillStyle = "rgb(0,0,0)";
//canvas.fill();
//var img = new Image();
canvas.drawImage(img, - 20, - 20, 100, 100);
img.onload = function () {
canvas.drawImage(img, - 20, - 20, 100, 100);
}
img.src = 'Stuff/needle.png';
return eye;
}
//Rotate the Dom node to a given angle.
this.rotate = function (x) {
this.node.style.MozTransform = "rotate(" + x + "deg)";
this.node.style.WebkitTransform = "rotate(" + x + "deg)";
this.node.style.OTransform = "rotate(" + x + "deg)";
this.node.style.msTransform = "rotate(" + x + "deg)";
this.node.style.Transform = "rotate(" + x + "deg)";
}
this.letsBegin = function () {
//Update every 100 miliseconds
myintID = setInterval(function () {
//Math!
angleFromEye = Math.atan2((cursorLocation.y - self.my_y), cursorLocation.x - self.my_x) * (180 / Math.PI) + 90;
//Rotate
self.rotate(angleFromEye);
//Refresh own position every 25th time (in case screen is resized)
i++;
if (i > 25) {
self.locateSelf();
i = 0;
}
}, 20);
}
this.letsEnd = function () {
clearInterval(myintID);
}
this.locateSelf = function () {
this.my_x = this.node.offsetLeft + (this.size / 2);
this.my_y = this.node.offsetTop + (this.size / 2);
//If it has offsetParent, add em up to get the objects full position.
if (this.node.offsetParent) {
temp = this.node;
while (temp = temp.offsetParent) {
this.my_x += temp.offsetLeft;
this.my_y += temp.offsetTop;
}
}
}
//Call the node create function when the AlienEye Object is created.
this.node = this.create(x, y, size, append);
this.locateSelf();
//Now the node has been added to the page, lets figure out exact where
//it is relative to the documents top.
//Get the basic position
var cursorLocation = new function () {
this.x = 0;
this.y = 0;
//This function is called onmousemove to update the stored position
this.update = function (e) {
var w = window,
b = document.body;
this.x = e.clientX + (w.scrollX || b.scrollLeft || b.parentNode.scrollLeft || 0);
this.y = e.clientY + (w.scrollY || b.scrollTop || b.parentNode.scrollTop || 0);
}
//Hook onmousemove up to the above update function.
document.onmousemove = function (e) {
cursorLocation.update(e);
};
If that's all your code, all you need to do is call the functions.
I managed to get it running by adding this to the bottom of the script (the body element was given an ID of "body"):
var img = new Image();
img.src = "Stuff/needle.png";
var eye = new alienEye(40, 40, 50, "body", img, 20);
eye.letsBegin();
Edit:
Here's how I got the angle based off two points in my GameAPI. It's a bit verbose... the main angle code is at the bottom:
getAngle : function(point1X, point1Y, point2X, point2Y, hyp) {
//This function uses the arcsine to calculate the angle between
//the points, but only if necessary.
//This function includes some shortcuts for common angles.
if(point1Y == point2Y) {
if(point1X < point2X) return 0;
else return 180;
}
if(point1X == point2X) {
if(point1Y < point2Y) return 90;
else return 270;
}
var xDist = point1X - point2X;
var yDist = point1Y - point2Y;
if(xDist == yDist) {
if(point1X < point2X) return 45;
else return 225;
}
if(-xDist == yDist) {
if(point1X < point2X) return 315;
else return 135;
}
if(hyp==null)
hyp = Math.sqrt( xDist*xDist + yDist*yDist );
var D_TO_R = this.D_TO_R;
if(point1X<point2X) {
//console.log(Math.round(-Math.asin((point1Y-point2Y)/hyp) * D_TO_R));
return Game.Util.fixDirection(-Math.asin((point1Y-point2Y)/hyp) * this.D_TO_R);
} else {
//console.log(Math.round(Math.asin((point1Y-point2Y)/hyp) * D_TO_R + 180));
return Game.Util.fixDirection(Math.asin((point1Y-point2Y)/hyp) * this.D_TO_R+180);
}
}
(The D_TO_R is the constant 180/PI for radian/angle conversion. The Game.Util.fixDirection ensures that the angle is between 0 and 360 The hyp argument is an optional argument for if the hypothenuse of the right triangle is already known, as to save CPU cycles)

Categories