I am creating a game for coursework, two tanks placed on a canvas with input boxes for the initial velocity and angle of the turret, then a button to fire a projectile (currently a div element in the shape of a circle), which calls a function in this case it is fire1. I have messed around for a few days and can't seem to get it to work, "bullet" is my div element.
function fire1 () {
var canvas = document.getElementById("canvas")
var bullet = document.getElementById("bullet");
bullet.style.visibility = "visible"
var start = null;
var intialVelocity = velocity1.value
var angle = angle1.value
var g = 9.81;
var progress, x, y;
function step(timestamp) {
if(start === null) start = timestamp;
progress = (timestamp - start)/1000;
x = (turret1.x + 80) + (intialVelocity*progress)
y = (turret1.y - 400) + (intialVelocity*progress)*Math.sin(angle*toRadians) - (0.5*g*(progress^2));//)
bullet.style.left = x + "px";
bullet.style.bottom = y + "px";
requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
Below is my css bit of my bullet.
#bullet {
position: absolute;
left: 0;
bottom: 50%;
width: 1em;
height: 1em;
border-radius: 0.5em;
background: red;
visibility: hidden;
}
I am very new to javascript, css and html so help would be very appriciated, I'm trying to incorporate the trajectory formula will this work? I also want it to be animated so it follows a path when fired. Thanks
I fixed this a long time ago but forgot to update with solution, below is how x and y are calculated for the trajectory:
x = ((turret.anchorX + negative*(initialVelocity*progress*Math.cos(angle*toRadians)))); //x-coordinate for bullet calculated by using x=ut.
y = ((720 - turret.anchorY + (initialVelocity*progress*Math.sin(angle*toRadians)) + (0.5*g*(Math.pow(progress,2))))); //y-coordinate for bullet calculated by usnig ut+0.5at^2.
Related
Its basicly an image, but I want to add some points with dropdowns, and its like 15 points, ajusting it with px would be very time consuming, I wonder if there is any other way around it. Thanks
<div id="map">
<div id="slide">
<img id="mapimg" src="https://i.imgur.com/NLS1KX0.jpg">
<div id="point">
<i id="pointIcon" class="fa-solid fa-location-dot"></i>
</div>
</div>
</div>
$(document).ready(function (){
var scroll_zoom = new ScrollZoom($('#map'),5,0.5)
})
//The parameters are:
//
//container: The wrapper of the element to be zoomed. The script will look for the first child of the container and apply the transforms to it.
//max_scale: The maximum scale (4 = 400% zoom)
//factor: The zoom-speed (1 = +100% zoom per mouse wheel tick)
function ScrollZoom(container,max_scale,factor){
var target = container.children().first()
var size = {w:target.width(),h:target.height()}
var pos = {x:0,y:0}
var scale = 1
var zoom_target = {x:0,y:0}
var zoom_point = {x:0,y:0}
var curr_tranform = target.css('transition')
var last_mouse_position = { x:0, y:0 }
var drag_started = 0
target.css('transform-origin','0 0')
target.on("mousewheel DOMMouseScroll",scrolled)
target.on('mousemove', moved)
target.on('mousedown', function() {
drag_started = 1;
target.css({'cursor':'move', 'transition': 'transform 0s'});
/* Save mouse position */
last_mouse_position = { x: event.pageX, y: event.pageY};
});
target.on('mouseup mouseout', function() {
drag_started = 0;
target.css({'cursor':'default', 'transition': curr_tranform});
});
function scrolled(e){
var offset = container.offset()
zoom_point.x = e.pageX - offset.left
zoom_point.y = e.pageY - offset.top
e.preventDefault();
var delta = e.delta || e.originalEvent.wheelDelta;
if (delta === undefined) {
//we are on firefox
delta = e.originalEvent.detail;
}
delta = Math.max(-1,Math.min(1,delta)) // cap the delta to [-1,1] for cross browser consistency
// determine the point on where the slide is zoomed in
zoom_target.x = (zoom_point.x - pos.x)/scale
zoom_target.y = (zoom_point.y - pos.y)/scale
// apply zoom
scale += delta * factor * scale
scale = Math.max(1,Math.min(max_scale,scale))
// calculate x and y based on zoom
pos.x = -zoom_target.x * scale + zoom_point.x
pos.y = -zoom_target.y * scale + zoom_point.y
update()
}
function moved(event){
if(drag_started == 1) {
var current_mouse_position = { x: event.pageX, y: event.pageY};
var change_x = current_mouse_position.x - last_mouse_position.x;
var change_y = current_mouse_position.y - last_mouse_position.y;
/* Save mouse position */
last_mouse_position = current_mouse_position;
//Add the position change
pos.x += change_x;
pos.y += change_y;
update()
}
}
function update(){
// Make sure the slide stays in its container area when zooming out
if(pos.x>0)
pos.x = 0
if(pos.x+size.w*scale<size.w)
pos.x = -size.w*(scale-1)
if(pos.y>0)
pos.y = 0
if(pos.y+size.h*scale<size.h)
pos.y = -size.h*(scale-1)
target.css('transform','translate('+(pos.x)+'px,'+(pos.y)+'px) scale('+scale+','+scale+')')
}
}
I tried using this, but the image is very long, and it would take to much time, I wonder if there is any easy way to do it.
#point {
position: relative;
overflow: hidden;
margin-left: 338px;
margin-top: -243px;
width: 25px;
height: 25px;
}
I tried using this, but the image is very long, and it would take to much time, I wonder if there is any easy way to do it.
#point {
position: relative;
overflow: hidden;
margin-left: 338px;
margin-top: -243px;
width: 25px;
height: 25px;
}
So I'm tyring to create a specific pointer trail effect. I'm pasting the code that I'm taking as an example below. The problem is that the trail is dotted, but I'm trying to make a line. I'm trying to recreate the trail that you can see on this site: [1]: https://argor-heraeus.com/
Example of the code that I'm using:
// dots is an array of Dot objects,
// mouse is an object used to track the X and Y position
// of the mouse, set with a mousemove event listener below
var dots = [],
mouse = {
x: 0,
y: 0
};
// The Dot object used to scaffold the dots
var Dot = function() {
this.x = 0;
this.y = 0;
this.node = (function() {
var n = document.createElement("div");
n.className = "trail";
document.body.appendChild(n);
return n;
}());
};
// The Dot.prototype.draw() method sets the position of
// the object's <div> node
Dot.prototype.draw = function() {
this.node.style.left = this.x + "px";
this.node.style.top = this.y + "px";
};
// Creates the Dot objects, populates the dots array
for (var i = 0; i < 12; i++) {
var d = new Dot();
dots.push(d);
}
// This is the screen redraw function
function draw() {
// Make sure the mouse position is set everytime
// draw() is called.
var x = mouse.x,
y = mouse.y;
// This loop is where all the 90s magic happens
dots.forEach(function(dot, index, dots) {
var nextDot = dots[index + 1] || dots[0];
dot.x = x;
dot.y = y;
dot.draw();
x += (nextDot.x - dot.x) * .6;
y += (nextDot.y - dot.y) * .6;
});
}
addEventListener("mousemove", function(event) {
//event.preventDefault();
mouse.x = event.pageX;
mouse.y = event.pageY;
});
// animate() calls draw() then recursively calls itself
// everytime the screen repaints via requestAnimationFrame().
function animate() {
draw();
requestAnimationFrame(animate);
}
// And get it started by calling animate().
animate();
body {
background-color: black;
}
.trail {
/* className for the trail elements */
position: absolute;
height: 6px;
width: 6px;
border-radius: 3px;
background: gold;
}
I'm playing with margins (left,right) , with width, but I can't "stick them" together. Anyone got an idea how I could make a clean line?
So I changed this in the given code:
x += (nextDot.x - dot.x) * **0.05**;
y += (nextDot.y - dot.y) * **0.05**;
And added more dots, 80, to be precise.
I also added a more smooth transition with this CSS rules
-webkit-transition:90ms;
transition:90ms;
pointer-events: none;
I have square that follows my cursor.
Its border top is red to see if the rotation is right.
I'm trying to rotate it depending on mouse movement angle. Like if mouse goes 45deg top right then square must rotate by 45deg.
The problem is that when I move my mouse slowly the square starts to rotate like crazy. But if I move my mouse fast enough square rotates pretty smooth.
Actually it's just a part of my task that I'm trying to accomplish. My whole task is to make custom circle cursor that stretches when mouse moving. The idea I'm trying to implement:
rotate circle by mouse movement angle and then just scaleX it to make stretching effect. But I cannot do it because of problem I described above. I need my follower to rotate smoothly when mouse speed is slow.
class Cursor {
constructor() {
this.prevX = null;
this.prevY = null;
this.curX = null;
this.curY = null;
this.angle = null;
this.container = document.querySelector(".cursor");
this.follower = this.container.querySelector(".cursor-follower");
document.addEventListener("mousemove", (event) => {
this.curX = event.clientX;
this.curY = event.clientY;
});
this.position();
}
position(timestamp) {
this.follower.style.top = `${this.curY}px`;
this.follower.style.left = `${this.curX}px`;
this.angle = Math.atan2(this.curY - this.prevY, this.curX - this.prevX) * 180/Math.PI;
console.log(this.angle + 90);
this.follower.style.transform = `rotateZ(${this.angle + 90}deg)`;
this.prevX = this.curX;
this.prevY = this.curY;
requestAnimationFrame(this.position.bind(this));
}
}
const cursor = new Cursor();
.cursor-follower {
position: fixed;
top: 0;
left: 0;
z-index: 9999;
pointer-events: none;
user-select: none;
width: 76px;
height: 76px;
margin: -38px;
border: 1.5px solid #000;
border-top: 1.5px solid red;
}
<div class="cursor">
<div class="cursor-follower"></div>
</div>
Following the cursor tangent smoothly isn't as simple as it first feels. In modern browsers mousemove event fires nearby at the frame rate (typically 60 FPS). When the mouse is moving slowly, the cursor moves only a pixel or two between the events. When calculating the angle, vertical + horizontal move of 1px resolves to 45deg. Then there's another problem, the event firing rate is not consistent, during the mouse is moving, event firing rate can drop to 30 FPS or even to 24 FPS, which actually helps to get more accurate angle, but makes the scale calculations heavily inaccurate (your real task seems to need scale calculations too).
One solution is to use CSS Transitions to make the animation smoother. However, adding a transition makes the angle calculations much more complex, because the jumps between negative and positive angles Math.atan2 returns when crossing PI will become visible when using transition.
Here's a sample code of how to use transition to make the cursor follower smoother.
class Follower {
// Default options
threshold = 4;
smoothness = 10;
stretchRate = 100;
stretchMax = 100;
stretchSlow = 100;
baseAngle = Math.PI / 2;
// Class initialization
initialized = false;
// Listens mousemove event
static moveCursor (e) {
if (Follower.active) {
Follower.prototype.crsrMove.call(Follower.active, e);
}
}
static active = null;
// Adds/removes mousemove listener
static init () {
if (this.initialized) {
document.removeEventListener('mousemove', this.moveCursor);
if (this.active) {
this.active.cursor.classList.add('hidden');
}
} else {
document.addEventListener('mousemove', this.moveCursor);
}
this.initialized = !this.initialized;
}
// Base values of instances
x = -1000;
y = -1000;
angle = 0;
restoreTimer = -1;
stamp = 0;
speed = [0];
// Prototype properties
constructor (selector) {
this.cursor = document.querySelector(selector);
this.restore = this.restore.bind(this);
}
// Activates a new cursor
activate (options = {}) {
// Remove the old cursor
if (Follower.active) {
Follower.active.cursor.classList.add('hidden');
Follower.active.cursor.classList.remove('cursor', 'transitioned');
}
// Set the new cursor
Object.assign(this, options);
this.setCss = this.cursor.style.setProperty.bind(this.cursor.style);
this.cursor.classList.remove('hidden');
this.cHW = this.cursor.offsetWidth / 2;
this.cHH = this.cursor.offsetHeight / 2;
this.setCss('--smoothness', this.smoothness / 100 + 's');
this.cursor.classList.add('cursor');
setTimeout(() => this.cursor.classList.add('transitioned'), 0); // Snap to the current angle
this.crsrMove({
clientX: this.x,
clientY: this.y
});
Follower.active = this;
return this;
}
// Moves the cursor with effects
crsrMove (e) {
clearTimeout(this.restoreTimer); // Cancel reset timer
const PI = Math.PI,
pi = PI / 2,
x = e.clientX,
y = e.clientY,
dX = x - this.x,
dY = y - this.y,
dist = Math.hypot(dX, dY);
let rad = this.angle + this.baseAngle,
dTime = e.timeStamp - this.stamp,
len = this.speed.length,
sSum = this.speed.reduce((a, s) => a += s),
speed = dTime
? ((1000 / dTime) * dist + sSum) / len
: this.speed[len - 1], // Old speed when dTime = 0
scale = Math.min(
this.stretchMax / 100,
Math.max(speed / (500 - this.stretchRate || 1),
this.stretchSlow / 100
)
);
// Update base values and rotation angle
if (isNaN(dTime)) {
scale = this.scale;
} // Prevents a snap of a new cursor
if (len > 5) {
this.speed.length = 1;
}
// Update angle only when mouse has moved enough from the previous update
if (dist > this.threshold) {
let angle = Math.atan2(dY, dX),
dAngle = angle - this.angle,
adAngle = Math.abs(dAngle),
cw = 0;
// Smoothen small angles
if (adAngle < PI / 90) {
angle += dAngle * 0.5;
}
// Crossing ±PI angles
if (adAngle >= 3 * pi) {
cw = -Math.sign(dAngle) * Math.sign(dX); // Rotation direction: -1 = CW, 1 = CCW
angle += cw * 2 * PI - dAngle; // Restores the current position with negated angle
// Update transform matrix without transition & rendering
this.cursor.classList.remove('transitioned');
this.setCss('--angle', `${angle + this.baseAngle}rad`);
this.cursor.offsetWidth; // Matrix isn't updated without layout recalculation
this.cursor.classList.add('transitioned');
adAngle = 0; // The angle was handled, prevent further adjusts
}
// Orthogonal mouse turns
if (adAngle >= pi && adAngle < 3 * pi) {
this.cursor.classList.remove('transitioned');
setTimeout(() => this.cursor.classList.add('transitioned'), 0);
}
rad = angle + this.baseAngle;
this.x = x;
this.y = y;
this.angle = angle;
}
this.scale = scale;
this.stamp = e.timeStamp;
this.speed.push(speed);
// Transform the cursor
this.setCss('--angle', `${rad}rad`);
this.setCss('--scale', `${scale}`);
this.setCss('--tleft', `${x - this.cHW}px`);
this.setCss('--ttop', `${y - this.cHH}px`);
// Reset the cursor when mouse stops
this.restoreTimer = setTimeout(this.restore, this.smoothness + 100, x, y);
}
// Returns the position parameters of the cursor
position () {
const {x, y, angle, scale, speed} = this;
return {x, y, angle, scale, speed};
}
// Restores the cursor
restore (x, y) {
this.state = 0;
this.setCss('--scale', 1);
this.scale = 1;
this.speed = [0];
this.x = x;
this.y = y;
}
}
Follower.init();
const crsr = new Follower('.crsr').activate();
body {
margin: 0px;
}
.crsr {
width: 76px;
height: 76px;
border: 2px solid #000;
border-radius: 0%;
text-align: center;
font-size: 20px;
}
.cursor {
position: fixed;
cursor: default;
user-select: none;
left: var(--tleft);
top: var(--ttop);
transform: rotate(var(--angle)) scaleY(var(--scale));
}
.transitioned {
transition: transform var(--smoothness) linear;
}
.hidden {
display: none;
}
<div class="crsr hidden">A</div>
The basic idea of the code is to wait until the mouse has moved enough pixels (threshold) to calculate the angle. The "mad circle" effect is tackled by setting the angle to the same position, but at the negated angle when crossing PI. This change is made invisibly between the renderings.
CSS variables are used for the actual values in transform, this allows to change a single parameter of the transform functions at the time, you don't have to rewrite the entire rule. setCss method is just syntactic sugar, it makes the code a little bit shorter.
The current parameters are showing a rectangle follower as it is in your question. Setting ex. stretchMax = 300 and stretchSlow = 125 and adding 50% border radius to CSS might be near to what you finally need. stretchRate defines the stretch related to the speed of the mouse. If the slow motion is still not smooth enough for your purposes, you can create a better algorithm to // Smoothen small angles section (in crsrMove method). You can play with the parameters at jsFiddle.
Try like this
class Cursor {
constructor() {
this.prevX = null;
this.prevY = null;
this.curX = null;
this.curY = null;
this.angle = null;
this.container = document.querySelector(".cursor");
this.follower = this.container.querySelector(".cursor-follower");
document.addEventListener("mousemove", (event) => {
this.curX = event.clientX;
this.curY = event.clientY;
});
this.position();
}
position(timestamp) {
this.follower.style.top = `${this.curY}px`;
this.follower.style.left = `${this.curX}px`;
if (this.curY !== this.prevY && this.curX !== this.prevX) {
this.angle = Math.atan2(this.curY - this.prevY, this.curX - this.prevX) * 180/Math.PI;
}
console.log(this.angle + 90);
this.follower.style.transform = `rotateZ(${this.angle + 90}deg)`;
this.prevX = this.curX;
this.prevY = this.curY;
requestAnimationFrame(this.position.bind(this));
}
}
const cursor = new Cursor();
I was trying to do some comparison between the angles of points, but quickly I ran into some strange results.
In this example I try rotate lines so that they point to the center, but the lines seem to angle more than they should quite quickly. Then, right above and below the center the lines start to point in all sorts of directions (These values are beyond the mathematical range of atan(x)).
How can I get accurate results from Math.atan()? Is there an alternative method to do this calculation?
I uploaded a 'working' version to this fiddle:
https://jsfiddle.net/0y2p6p3n/. I am working in Chrome.
html:
<div id="content">
</div>
javascript:
let points = [];
let amount = 1000;
let width = 300;
let height = 300;
for (let i = 0; i<amount;i++) {
let x = Math.random();
let y = Math.random();
let point = {x, y};
points.push(point);
points[i].tan = Math.atan(0.5 - points[i].y)/(0.5 - points[i].x);
points[i].error = Math.abs(points[i].tan) > 3.14/2 ? true : false;
}
for (let point of points) {
let line = document.createElement("div");
line.classList.add("line");
line.style.marginTop = point.y*height + "px";
line.style.marginLeft = point.x*width + "px";
line.style.transform = "rotateZ(" + ((point.tan*(180/Math.PI))) + "deg)";
point.error == true && line.classList.add("error");
document.getElementById("content").appendChild(line);
}
css:
#content {
position: relative;
}
.line {
position: absolute;
height: 1px;
width: 10px;
background-color: black;
}
.error {
background-color: red;
}
You are using
points[i].tan = Math.atan(0.5 - points[i].y)/(0.5 - points[i].x);
It should be
points[i].tan = Math.atan( (0.5 - points[i].y)/(0.5 - points[i].x) );
let points = [];
let amount = 1000;
let width = 300;
let height = 300;
for (let i = 0; i<amount;i++) {
let x = Math.random();
let y = Math.random();
let point = {x, y};
points.push(point);
points[i].tan = Math.atan( (0.5 - points[i].y)/(0.5 - points[i].x) );
points[i].error = Math.abs(points[i].tan) > 3.14/2 ? true : false;
}
for (let point of points) {
let line = document.createElement("div");
line.classList.add("line");
line.style.marginTop = point.y*height + "px";
line.style.marginLeft = point.x*width + "px";
line.style.transform = "rotateZ(" + ((point.tan*(180/Math.PI))) + "deg)";
point.error == true && line.classList.add("error");
document.getElementById("content").appendChild(line);
}
#content {
position: relative;
}
.line {
position: absolute;
height: 1px;
width: 10px;
background-color: black;
}
.error {
background-color: red;
}
<div id="content"></div>
As you mentioned, the exact asymptotes (i.e. pi/2 and -pi/2) are outside of the valid domain of atan, which makes taking the atan of those values impossible. You also may have to deal with the fact that atan always returns a reference angle, which may not be the quadrant you want your answer to be in. These are very well known issues, and most languages have a simple cure, called atan2. In the case of javascript, please see the MDN reference for atan2.
The change to your code is simple; simply change
points[i].tan = Math.atan(0.5 - points[i].y)/(0.5 - points[i].x);
to
points[i].tan = Math.atan2(0.5 - points[i].y, 0.5 - points[i].x);
If you check out the updated fiddle, you may see its behavior has improved considerably.
atan2 doesn't give you higher precision, but it does give you values over the complete range of [0..2pi], without you having to do all the extra work of figuring out which quadrant the answer should be in, as well as supporting pi/2 and -pi/2 within its range. It is helped in doing so by knowing whether the x or the y (or both) is negative, a fact which gets hidden if you do the division yourself.
It should be noted that the most significant change I made to your code was not atan2, however, it was changing around your use of parenthesis. While I'm an advocate for using atan2 any time you would normally use atan, your actual issue was misuse of parenthesis, making Oriol's answer the right one.
I was playing around with JavaScript/canvas and I want my objects color to depend on the distance to its center from current mouse position.This is my current function that gets color every mousemove event:
function getColorFromDistance(node1,node2){
var dist = getDist(node1,node2); //Getting distance;
var cl = (Math.round(255/dist*255)).toString(16); //this needs to be a propper formula
return "#" + cl + cl + cl; //converting to hex
}
Currently I get a blink effect when the distance gets 255.
I need a way to get the colors strength be depended on the distance, so that the further mouse is away from object the more its darken and when mouse is on the objects center its fully white.Well you get the idea.I just need the formula
The formula would be calculate the distance between the two points and get a percentage based on the maximum value (width of canvas/window)
//this would need to be recalulated on resize, but not doing it for demo
var targetElem = document.querySelector("div.x span");
box = targetElem.getBoundingClientRect(),
x = box.left + box.width/2,
y = box.top + box.height/2,
winBox = document.body.getBoundingClientRect(),
maxD = Math.sqrt(Math.pow(winBox.width/2, 2) + Math.pow(winBox.height/2, 2));
document.body.addEventListener("mousemove", function (evt) {
var diffX = Math.abs(evt.pageX-x),
diffY = Math.abs(evt.pageY-y),
distC = Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2)),
strength = Math.ceil(255 - (distC/maxD*255)).toString(16),
color = "#" + strength + strength + strength;
targetElem.style.backgroundColor = color;
});
html, body { height: 100%; }
div.x { position: absolute; top: 50%; left:50%; }
span { display: inline-block; width: 20px; height: 20px; border-radius: 50%; border: 1px solid black; overflow: hidden; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p>Test</p>
<div class="x"><span> </span></div>