Collision detection not working as intended (Arrays, Javascript) - javascript

Ill start off by saying im not the best at explaining.
I have attached 2 images to help explain with my problem.
The problem is that the 'player' is colliding with the 'square 2' X position, when it is clearly not in range of the Y position. The green line shows where the player is colliding and stopping, (stopping as in if you hit a wall, you would stop). In image 2, below the black line, is the expected outcome, how do I achieve this? (Scroll for code)
Just in case you need to know, the enemy/player is 50x50px, the rectangle is 70x150px
My Code (JS):
blockX.forEach(blockX => { // left and right collision
blockY.forEach(blockY => {
if (enemy.y + enemy.h >= blockY && enemy.y <= blockY + rect.h) {
if (enemy.x + enemy.w >= blockX) {
if (enemy.x <= blockX + enemy.w) {
enemy.x = blockX - enemy.w
}
}
if (enemy.x <= blockX + rect.w + 13) {
if (enemy.x + enemy.w >= blockX) {
enemy.x = blockX + rect.w + 13
}
}
}
})
})

First of all, you can simplify the code by resetting collision.cr at the beginning of the function, and then all you have to do is set it to true once if met condition, no need for any else
Second, you should have a single condition, that checks both x and y coordinates, not two separates (which by the way, in your code the second condition WILL overwrite previous condition's result, because of the else)
const elPlayer = document.getElementById("player");
const enemies = [
{
x: 100,
y: 34,
w: 100,
h: 150
},
{
x: 300,
y: 34,
w: 100,
h: 150
},
{
x: 140,
y: 54,
w: 100,
h: 50
}
];
window.addEventListener("mousemove", e =>
{
const player = {
x: e.x,
y: e.y,
w: 20,
h: 20
}
//colistion detection
for(let i = 0; i < enemies.length; i++)
{
const enemy = enemies[i];
const collide = (enemy.x < player.x + player.w &&
enemy.x + enemy.w > player.x &&
enemy.y < player.y + player.h &&
enemy.y + enemy.h > player.y)
elPlayer.classList.toggle("collide", collide);
if (collide)
break;
}
elPlayer.style.top = player.y + "px";
elPlayer.style.left = player.x + "px";
});
[...document.querySelectorAll(".enemy")].forEach((enemy, i) =>
{
enemy.style.left = enemies[i].x + "px";
enemy.style.top = enemies[i].y + "px";
enemy.style.width = enemies[i].w + "px";
enemy.style.height = enemies[i].h + "px";
});
.enemy
{
position: absolute;
background-color: pink;
border: 1px solid black;
}
#player
{
position: absolute;
width: 20px;
height: 20px;
background-color: green;
border: 1px solid black;
}
#player.collide
{
background-color: red;
}
<div class="enemy"></div>
<div class="enemy"></div>
<div class="enemy"></div>
<div id="player"></div>

Related

jQuery code works on desktop, not in Android

My code moves a picture inside the div using the mouse. It works in a web browser on Windows. How can I change it so that it works in Android?
$(document).ready(function() {
var $bg = $('.bg-img'),
//data = $('#data')[0],
elbounds = {
w: parseInt($bg.width()),
h: parseInt($bg.height())
},
bounds = {
w: 1920 - elbounds.w,
h: 1080 - elbounds.h
},
origin = {
x: 0,
y: 0
},
start = {
x: 0,
y: 0
},
movecontinue = false;
function move(e) {
var inbounds = {
x: false,
y: false
},
offset = {
x: start.x - (origin.x - e.clientX),
y: start.y - (origin.y - e.clientY)
};
//data.value = 'X: ' + offset.x + ', Y: ' + offset.y;
inbounds.x = offset.x < 0 && (offset.x * -1) < bounds.w;
inbounds.y = offset.y < 0 && (offset.y * -1) < bounds.h;
if (movecontinue && inbounds.x && inbounds.y) {
start.x = offset.x;
start.y = offset.y;
$(this).css('background-position', start.x + 'px ' + start.y + 'px');
}
origin.x = e.clientX;
origin.y = e.clientY;
e.stopPropagation();
return false;
}
function handle(e) {
movecontinue = false;
$bg.unbind('mousemove', move);
if (e.type == 'mousedown') {
origin.x = e.clientX;
origin.y = e.clientY;
movecontinue = true;
$bg.bind('mousemove', move);
} else {
$(document.body).focus();
}
e.stopPropagation();
return false;
}
function reset() {
start = {
x: 0,
y: 0
};
$(this).css('backgroundPosition', '0 0');
}
$bg.bind('mousedown mouseup mouseleave', handle);
$bg.bind('dblclick', reset);
});
div.bg-img {
background-image: url(https://wallpapershome.com/images/pages/pic_h/23843.jpeg);
background-position: 0 0;
background-repeat: no-repeat;
background-color: blue;
border: 1px solid #aaa;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: auto;
}
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<div class="bg-img"></div>

Rendering to HTML canvas in recursive calls

I have implemented a recursive maze generation algorithm in Javascript, and am rendering using HTML canvas.
The problem I have, is that I am unable to render to the canvas while the recursive function is in progress. The render only appears after recursive calls have finished.
Here is my (rough) functioning recursive function :
function start(canvas_context)
{
//do some stuff here...
render(canvas_context);
if()...{
start(canvas_context);
}
else if(...)
{
if(...)
{
start(canvas_context);
}
}
}
Here is my rendering function:
function render(canvas_context)
{
canvas_context.fillStyle = 'black';
canvas_context.fillRect(0,0,canvas.width, canvas.height);
for(..)
{
for(...)
{
canvas_context.strokeStyle = 'red';
canvas_context.beginPath();
canvas_context.moveTo(start.x, start.y);
canvas_context.lineTo(end.x,end.y);
canvas_context.stroke();
}
}
}
Here are the full functions if they are needed:
function start(canvas_context)
{
//Vector2.print(game_config["current_grid_pos"]);
var current_block = game_config["grid_space"][game_config["current_grid_pos"].y][game_config["current_grid_pos"].x];
var random_neighbor_grid_pos = random_neighbor(current_block);
//render(canvas_context);
if(random_neighbor_grid_pos.x != -1 && random_neighbor_grid_pos.y != -1)
{
var next_block = game_config["grid_space"][random_neighbor_grid_pos.y][random_neighbor_grid_pos.x];
next_block.visited = true;
game_config["visited_stack"].push(random_neighbor_grid_pos);
remove_walls(current_block, next_block);
game_config["current_grid_pos"] = random_neighbor_grid_pos;
start(canvas_context);
}
else if (game_config["visited_stack"].length > 0)
{
current_block.visited = true;
game_config["visited_stack"].pop();
if(game_config["visited_stack"].length != 0)
{
game_config["current_grid_pos"] = game_config["visited_stack"][game_config["visited_stack"].length - 1];
start(canvas_context);
}
}
}
function render(canvas_context)
{
//Initialise the canvas background.
canvas_context.fillStyle = 'black';
canvas_context.fillRect(0,0,canvas.width, canvas.height);
for(var y = 0; y < game_config["grid_space"].length; y++)
{
for(var x = 0; x < game_config["grid_space"][0].length; x++)
{
var block = game_config["grid_space"][y][x];
var walls = block.get_walls();
for(var key in walls)
{
var wall = walls[key];
render_line(canvas_context, wall.start, wall.end);
}
}
}
}
function render_line(canvas_context, start, end)
{
canvas_context.strokeStyle = 'red';
canvas_context.beginPath();
canvas_context.moveTo(start.x, start.y);
canvas_context.lineTo(end.x,end.y);
canvas_context.stroke();
}
Is it even possible to render during recursive calls with HTML canvas?
I think you can do it without a recursion but I show both ways:
const cellsise = 10;
class block {
constructor (x, y){
this.x = x;
this. y = y;
this.delta = {x: x * cellsise , y: y * cellsise};
this.walls = {
left: {
start: {
x: 0 + this.delta.x,
y: 0 + this.delta.y
},
end: {
x: 0 + this.delta.x,
y: cellsise + this.delta.y
}
},
top: {
start: {
x: 0 + this.delta.x,
y: 0 + this.delta.y
},
end: {
x: cellsise + this.delta.x,
y: 0 + this.delta.y
}
},
right: {
start: {
x: cellsise + this.delta.x,
y: 0 + this.delta.y
},
end: {
x: cellsise + this.delta.x,
y: cellsise + this.delta.y
}
},
bottom: {
start: {
x: 0 + this.delta.x,
y: cellsise + this.delta.y
},
end: {
x: cellsise + this.delta.x,
y: cellsise + this.delta.y
}
}
};
}
get_walls(){
return this.walls;
}
}
const game_config = {
grid_space:[],
height: 10,
width: 10,
}
function generateGridSpace() {
game_config.grid_space=[];
for(y = 0 ; y < game_config.height; ++y){
game_config.grid_space.push([]);
for(x = 0 ; x < game_config.width; ++x){
game_config.grid_space[y].push(new block(x, y))
}
}
}
function remove_walls(current_block){
let left = Math.random() > 0.5;
let top = !left //Math.random() > 0.5;
let right = Math.random() > 0.5;
let bottom = !right //false// Math.random() > 0.5;
if (left && current_block.x - 1 > 0) {
delete current_block.walls.left;
delete game_config.grid_space[current_block.x-1][current_block.y].walls.right
}
if (top && current_block.y - 1 > 0) {
delete current_block.walls.top;
delete game_config.grid_space[current_block.x][current_block.y-1].walls.bottom;
}
if (right && current_block.x+1 < game_config.width -1) {
delete current_block.walls.right;
delete game_config.grid_space[current_block.x + 1][current_block.y].walls.left;
}
if (bottom && current_block.y+1 < game_config.height -1) {
delete current_block.walls.bottom;
delete game_config.grid_space[current_block.x][current_block.y + 1].walls.top;
}
}
function start_with_recursion(canvas_context, mix=null) {
if (!mix) {
mix = game_config.grid_space.reduce((acc, e) => [...acc,...e], [])
.sort(() => Math.random() - 0.5)
} else if (mix.length === 0) {
return;
}
const block = mix.pop();
const i = game_config.width * game_config.height - mix.length
setTimeout(() => {
remove_walls(block);
render_block(canvas_context, block);
}, i * 50)
start_with_recursion(canvas_context, mix)
}
function start_without_recursion(canvas_context) {
const mix = game_config.grid_space.reduce((acc, e) => [...acc,...e], [])
.sort(() => Math.random() - 0.5)
for(let i = 0; i < mix.length; ++i){
setTimeout(() => {
remove_walls(mix[i]);
render_block(canvas_context, mix[i]);
}, i * 50)
}
}
function render_block(canvas_context, block) {
var walls = block.get_walls();
for(var key in walls)
{
var wall = walls[key];
render_line(canvas_context, wall.start, wall.end);
}
}
function render_line(canvas_context, start, end) {
canvas_context.strokeStyle = 'red';
canvas_context.beginPath();
canvas_context.moveTo(
5 + start.x + .5,
5 + start.y + .5
);
canvas_context.lineTo(
5 + end.x + .5,
5 + end.y + .5
);
canvas_context.stroke();
}
canvas = document.querySelector('canvas');
canvas.height = game_config.height * cellsise + 5 * 2;
canvas.width = game_config.width * cellsise + 5 * 2;
ctx = canvas.getContext('2d');
ctx.fillStyle = 'black';
ctx.fillRect(0,0,canvas.width, canvas.height);
// ctx.lineWidth = 2;
generateGridSpace();
// start_without_recursion(ctx);
start_with_recursion(ctx);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<canvas></canvas>
</body>
</html>
also for get a random neighbor:
function random_neighbor(current_block) {
const {height, width} = game_config;
const neighbors = [
{x: current_block.x, y: current_block.y - 1}, // from top
{x: current_block.x - 1, y: current_block.y}, // from left
{x: current_block.x + 1, y: current_block.y}, // from right
{x: current_block.x, y: current_block.y + 1}, // from bottom
].filter(e => e.x >= 0 && e.x < width && e.y >= 0 && e.y < height)
const rnd = neighbors[Math.floor(Math.random() * neighbors.length)]
return rnd; // {x, y}
}
You also need to take into account that the drawn wall can be removed later, which will not correspond to reality. Therefore, it is better to render (even with a delay) after the maze is fully generated.
Suggest converting your recursive function into an iterator function, and then introducing a second function that makes use of setInterval to iteratively draw your maze, and once complete, then performs a clearInterval.
Here's a quick sample that I whipped up exemplifying the concept, which simply draws increasingly larger rectangles on the canvas every 50ms, with the iterator function returning the size of the next rectangle to be drawn. In your case, you'll likely want to yield an object defining the portion of the maze to be drawn...
var ctx = document.getElementById('canvas').getContext('2d');
function* recurse( n, max ) {
yield n;
if ( n < max ) {
yield* recurse( n + 1, max );
}
}
function drawRectangles() {
function drawIteratively() {
let nd = iterator.next();
if ( nd.done ) {
clearInterval( interval );
} else {
ctx.lineWidth = 1;
ctx.strokeStyle = '#FF0000';
ctx.beginPath();
ctx.rect( nd.value, nd.value, nd.value * 2, nd.value * 2 );
ctx.stroke();
}
}
let iterator = recurse( 25, 100 );
let interval = setInterval( drawIteratively, 50 );
}
drawRectangles();
<canvas id='canvas' width="300" height="300"></canvas>
Note that the iterator function is a very basic recursive function, but recursive nonetheless, showing the critical syntax involved in a recursive iterator function...

Javascript draggable div background

I got a little problem, I tried to modify the code for like one day.. without success.
The problem is that:
Draggable works good, I can drag without problem the image.. but it goes even further the image make me see the blue of background color.
I know right is a problem about bounds, but I really don't know to.. resolve that problem, I tried to grab X and Y from Image but without success.
$(document).ready(function(){
var $bg = $('.bg-img'),
data = $('#data')[0],
elbounds = {
w: parseInt($bg.width()),
h: parseInt($bg.height())
},
bounds = {w: 2350 - elbounds.w, h: 1750 - elbounds.h},
origin = {x: 0, y: 0},
start = {x: 0, y: 0},
movecontinue = false;
function move (e){
var inbounds = {x: false, y: false},
offset = {
x: start.x - (origin.x - e.clientX),
y: start.y - (origin.y - e.clientY)
};
data.value = 'X: ' + offset.x + ', Y: ' + offset.y;
inbounds.x = offset.x < 0 && (offset.x * -1) < bounds.w;
inbounds.y = offset.y < 0 && (offset.y * -1) < bounds.h;
if (movecontinue && inbounds.x && inbounds.y) {
start.x = offset.x;
start.y = offset.y;
$(this).css('background-position', start.x + 'px ' + start.y + 'px');
}
origin.x = e.clientX;
origin.y = e.clientY;
e.stopPropagation();
return false;
}
function handle (e){
movecontinue = false;
$bg.unbind('mousemove', move);
if (e.type == 'mousedown') {
origin.x = e.clientX;
origin.y = e.clientY;
movecontinue = true;
$bg.bind('mousemove', move);
} else {
$(document.body).focus();
}
e.stopPropagation();
return false;
}
function reset (){
start = {x: 0, y: 0};
$(this).css('backgroundPosition', '0 0');
}
$bg.bind('mousedown mouseup mouseleave', handle);
$bg.bind('dblclick', reset);
});
Example code: https://jsfiddle.net/zt1jjzqL/3/
Here I only modify a couple of things;
New function to calculate image dimensions
Calculate the most Left and Up the image can move.
limit the movement up and left with those.
$(document).ready(function() {
var $bg = $('.bg-img'),
data = $('#data')[0],
elbounds = {
w: parseInt($bg.width()),
h: parseInt($bg.height())
},
bounds = {
w: 2350 - elbounds.w,
h: 1750 - elbounds.h
},
origin = {
x: 0,
y: 0
},
start = {
x: 0,
y: 0
},
movecontinue = false;
bgSize($bg, function(w, h) { //act on and store the most up and left
console.log(`image dimensions => width:${w}, height:${h}`);
$bg.data("mostleft", (elbounds.w * -1) + w);
$bg.data("mostup", (elbounds.h * -1) + h);
});
function move(e) {
var inbounds = {
x: false,
y: false
},
offset = {
x: start.x - (origin.x - e.clientX),
y: start.y - (origin.y - e.clientY)
};
data.value = 'X: ' + offset.x + ', Y: ' + offset.y;
inbounds.x = offset.x < 0 && (offset.x * -1) < bounds.w;
inbounds.y = offset.y < 0 && (offset.y * -1) < bounds.h;
if (movecontinue && inbounds.x && inbounds.y) {
// ensure that up and left are limited appropriately
start.x = offset.x < ($bg.data("mostleft") * -1) ? ($bg.data("mostleft") * -1) : offset.x;
start.y = offset.y < ($bg.data("mostup") * -1) ? ($bg.data("mostup") * -1) : offset.y;
$(this).css('background-position', start.x + 'px ' + start.y + 'px');
}
origin.x = e.clientX;
origin.y = e.clientY;
e.stopPropagation();
return false;
}
function handle(e) {
movecontinue = false;
$bg.unbind('mousemove', move);
if (e.type == 'mousedown') {
origin.x = e.clientX;
origin.y = e.clientY;
movecontinue = true;
$bg.bind('mousemove', move);
} else {
$(document.body).focus();
}
e.stopPropagation();
return false;
}
function reset() {
start = {
x: 0,
y: 0
};
$(this).css('backgroundPosition', '0 0');
}
$bg.bind('mousedown mouseup mouseleave', handle);
$bg.bind('dblclick', reset);
});
//function to accurately calculate image size.
function bgSize($el, cb) {
$('<img />')
.load(function() {
cb(this.width, this.height);
})
.attr('src', $el.css('background-image').match(/^url\("?(.+?)"?\)$/)[1]);
}
div.bg-img {
background-image: url(http://i.imgur.com/ZCDMWnX.gif);
background-position: 0 0;
background-repeat: no-repeat;
background-color: blue;
border: 1px solid #aaa;
width: 500px;
height: 250px;
margin: 25px auto;
}
p,
#data {
text-align: center;
}
#data {
background: red;
font-weight: bold;
color: white;
padding: 5px;
font-size: 1.4em;
border: 1px solid #ddd;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="bg-img"></div>
<p>
<input type="text" id="data" />
</p>

Collision between two elements with rotating

var keys = new Array();
var direction;
var direction;
var iNr = 0;
$(document).ready(function(){
looper();
$("#demo1").css("margin-top", 400 + "px");
$("#demo2").css("margin-left", 380 + "px");
myFunction();
});
function myFunction()
{
iNr = iNr + 0.5;
$("#main").css("transition","all 0.1s");
$("#main").css("transform","rotate(" + iNr + "deg)");
setTimeout(function()
{
myFunction();
}, 50);
}
function looper()
{
var p =$("#circle");
var offset = p.offset();
var t =$(".red");
var roffset = t.offset();
var rect1 = {x: offset.left, y: offset.top, width: p.width(), height: p.height()}
var rect2 = {x: roffset.left, y: roffset.top, width: t.width(), height: t.height()}
if (rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.height + rect1.y > rect2.y) {
console.log("now");
}
if(direction == "left")
{
if(offset.left - 50 > 0)
{
$("#circle").css("left", ($("#circle").position().left - 2) + "px");
}
}
if(direction == "up")
{
if(offset.top - 50 > 0)
{
$("#circle").css("top", ($("#circle").position().top - 2) + "px");
}
}
if(direction == "right")
{
if((offset.left + 50) < $(window).width())
{
$("#circle").css("left", ($("#circle").position().left + 2) + "px");
}
}
if(direction == "down")
{
if((offset.top + 50) < $(window).height())
{
$("#circle").css("top", ($("#circle").position().top + 2) + "px");
}
}
ID=window.setTimeout("looper();", 1);
}
$(document).keyup(function(event) {
if (event.keyCode == 37)
{
var index = keys.indexOf("37");
keys.splice(index, 1);
direction = "";
}
if (event.keyCode == 38)
{
var index = keys.indexOf("38");
keys.splice(index, 1);
direction = "";
}
if (event.keyCode == 39)
{
var index = keys.indexOf("39");
keys.splice(index, 1);
direction = "";
}
if (event.keyCode == 40)
{
var index = keys.indexOf("40");
keys.splice(index, 1);
direction = "";
}
});
$(document).keydown(function(event) {
if (event.keyCode == 37)
{
keys.push("37");
direction = "left";
}
if (event.keyCode == 38)
{
keys.push("38");
direction = "up";
}
if (event.keyCode == 39)
{
keys.push("39");
direction = "right";
}
if (event.keyCode == 40)
{
keys.push("40");
direction = "down";
}
});
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>test</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body style="background-color:black; overflow-y:scroll;">
<div style="width:400px; margin-left:500px; height:400px;" id="main">
<div id="demo1" style="width:400px; height:20px; background-color:red; position:absolute;" class="red test all"></div>
<div id="demo2" style="width:20px; height:400px; background-color:yellow; position:absolute;" class="test all"></div>
<div id="demo3" style="width:400px; height:20px; background-color:blue; position:absolute;" class="test all"></div>
<div id="demo4" style="width:20px; height:400px; background-color:green; position:absolute;" class="test all"></div>
</div>
<div style="width:25px; height:25px; background-color:white; position:absolute; border-radius:50%;" id="circle"></div>
</body>
</html>
I have programmed a game.
In this game my function checks, whether there is a collision between div1 and div2.
Or if they are overlapping or so... how you want to spell it.
Without a rotation everything is ok.
But now i have a problem.
I want to rotate div2 with transform:rotate(Xdeg);
but if I do this my calculation for the collision dosen't work.
I use this:
var rect1 = {x: 5, y: 5, width: 50, height: 50}
var rect2 = {x: 20, y: 10, width: 10, height: 10}
if (rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.height + rect1.y > rect2.y) {
// collision detected!
}
do you have any ideas to solve this problem?
Thanks for helping :-)
There are several ways to do this. This example just guides you of how it could be done with rectangles.
These are the steps that are done here:
You have to calculate the position of all rotated corners of all rectangles that you want to check whether they are being collided. To get these rotated corners, you can use several methods. In this example 2d-vectors and a 2d-rotation-matrix are used:
a vector that has its origin in the center of a rectangle and directs to the top-left-corner(x,y) of a rectangle:
var center = {
x: x + width / 2,
y: y + height / 2
};
var vector = {
x: (x - center.x),
y: (y - center.y)
};
multiply this vector with a rotation-matrix to rotate this vector:
// modified sin function to calulcate sin in the unit degrees instead of radians
function sin(x) {
return Math.sin(x / 180 * Math.PI);
}
// modified cos function
function cos(x) {
return Math.cos(x / 180 * Math.PI);
}
var rotationMatrix = [[cos(angle), -sin(angle)],[sin(angle), cos(angle)]];
var rotatedVector = {
x: vector.x * rotationMatrix[0][0] + vector.y * rotationMatrix[0][1],
y: vector.x * rotationMatrix[1][0] + vector.y * rotationMatrix[1][1]
};
And finally get the rotated top-left-corner, you can start from the center of a rectangle and go to where the rotated vector points to. This is where top-left-corner is located after rotation:
{
x: (center.x + rotatedVector.x),
y: (center.y + rotatedVector.y)
}
All the steps described above are done by getRotatedTopLeftCornerOfRect and must be done with all other corners as well. Before the location of the next
corner (right-top) can be calculated next vector must be calulcated that points to this corner. To get the next vector that points to the top-right-corner the angle between the first vector (left-top) and the second vector (right-top) is calculated. The third vector points to the right-bottom-corner when its angle is incremended by the first angle and the second angle and the fourth vector is rotated by an angle that is summed up the first, second and third angle. All of this is done in the setCorners-method and this image shows this process partly:
To detect a collision there are tons of algorithms. In this example the Point in polygon algorithm is used to check each rotated corner of a rectangle whether a corner is with the border or within another rectangle, if so, then the method isCollided returns true. The Point in polygon algorithm is used in pointInPoly and can also be found here.
Combining all of the steps described above was tricky, but it works with all rectangles of all sizes and the best of all you can test it right here without a library by clicking on "Run code snippet".
(tested browsers: FF 50.1.0, IE:10-EDGE, Chrome:55.0.2883.87 m):
var Rectangle = (function () {
function sin(x) {
return Math.sin(x / 180 * Math.PI);
}
function cos(x) {
return Math.cos(x / 180 * Math.PI);
}
function getVectorLength(x, y, width, height){
var center = {
x: x + width / 2,
y: y + height / 2
};
//console.log('center: ',center);
var vector = {
x: (x - center.x),
y: (y - center.y)
};
return Math.sqrt(vector.x*vector.x+vector.y*vector.y);
}
function getRotatedTopLeftCornerOfRect(x, y, width, height, angle) {
var center = {
x: x + width / 2,
y: y + height / 2
};
//console.log('center: ',center);
var vector = {
x: (x - center.x),
y: (y - center.y)
};
//console.log('vector: ',vector);
var rotationMatrix = [[cos(angle), -sin(angle)],[sin(angle), cos(angle)]];
//console.log('rotationMatrix: ',rotationMatrix);
var rotatedVector = {
x: vector.x * rotationMatrix[0][0] + vector.y * rotationMatrix[0][1],
y: vector.x * rotationMatrix[1][0] + vector.y * rotationMatrix[1][1]
};
//console.log('rotatedVector: ',rotatedVector);
return {
x: (center.x + rotatedVector.x),
y: (center.y + rotatedVector.y)
};
}
function getOffset(el) {
var _x = 0;
var _y = 0;
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return {
top: _y,
left: _x
};
}
function pointInPoly(verties, testx, testy) {
var i,
j,
c = 0
nvert = verties.length;
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
if (((verties[i].y > testy) != (verties[j].y > testy)) && (testx < (verties[j].x - verties[i].x) * (testy - verties[i].y) / (verties[j].y - verties[i].y) + verties[i].x))
c = !c;
}
return c;
}
function Rectangle(htmlElement, width, height, angle) {
this.htmlElement = htmlElement;
this.width = width;
this.height = height;
this.setCorners(angle);
}
function testCollision(rectangle) {
var collision = false;
this.getCorners().forEach(function (corner) {
var isCollided = pointInPoly(rectangle.getCorners(), corner.x, corner.y);
if (isCollided) collision = true;
});
return collision;
}
function checkRectangleCollision(rect, rect2) {
if (testCollision.call(rect, rect2)) return true;
else if (testCollision.call(rect2, rect)) return true;
return false;
}
function getAngleForNextCorner(anc,vectorLength) {
var alpha = Math.acos(anc/vectorLength)*(180 / Math.PI);
return 180 - alpha*2;
}
Rectangle.prototype.setCorners = function (angle) {
this.originalPos = getOffset(this.htmlElement);
this.leftTopCorner = getRotatedTopLeftCornerOfRect(this.originalPos.left, this.originalPos.top, this.width, this.height, angle);
var vecLength = getVectorLength(this.originalPos.left, this.originalPos.top, this.width, this.height);
//console.log('vecLength: ',vecLength);
angle = angle+getAngleForNextCorner(this.width/2, vecLength);
//console.log('angle: ',angle);
this.rightTopCorner = getRotatedTopLeftCornerOfRect(this.originalPos.left, this.originalPos.top, this.width, this.height, angle);
angle = angle+getAngleForNextCorner(this.height/2, vecLength);
//console.log('angle: ',angle);
this.rightBottomCorner = getRotatedTopLeftCornerOfRect(this.originalPos.left, this.originalPos.top, this.width, this.height, angle);
angle = angle+getAngleForNextCorner(this.width/2, vecLength);
//console.log('angle: ',angle);
this.leftBottomCorner = getRotatedTopLeftCornerOfRect(this.originalPos.left, this.originalPos.top, this.width, this.height, angle);
//console.log(this);
};
Rectangle.prototype.getCorners = function () {
return [this.leftTopCorner,
this.rightTopCorner,
this.rightBottomCorner,
this.leftBottomCorner];
};
Rectangle.prototype.isCollided = function (rectangle) {
return checkRectangleCollision(this, rectangle);
};
return Rectangle;
}) ();
var rotA = 16;
var widthA = 150;
var heightA = 75;
var htmlRectA = document.getElementById('rectA');
var rotB = 28.9;
var widthB = 50;
var heightB = 130;
var htmlRectB = document.getElementById('rectB');
var msgDiv = document.getElementById('msg');
var rectA = new Rectangle(htmlRectA, widthA, heightA, rotA);
var rectB = new Rectangle(htmlRectB, widthB, heightB, rotB);
window.requestAnimFrame = function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame;
}();
function draw(){
rotA+=1.2;
htmlRectA.setAttribute('style','-ms-transform: rotate('+rotA+'deg);-webkit-transform: rotate('+rotA+'deg);transform: rotate('+rotA+'deg)');
rotB+=5.5;
htmlRectB.setAttribute('style','-ms-transform: rotate('+rotB+'deg);-webkit-transform: rotate('+rotB+'deg);transform: rotate('+rotB+'deg)');
rectA.setCorners(rotA);
rectB.setCorners(rotB);
if(rectA.isCollided(rectB)){
msgDiv.innerHTML = 'Collision detected!';
msgDiv.setAttribute('style','color: #FF0000');
}
else {
msgDiv.innerHTML = 'No Collision!';
msgDiv.setAttribute('style','color: #000000');
}
setTimeout(function(){
window.requestAnimFrame(draw);
},50);
}
window.requestAnimFrame(draw);
#rectA{
background-color: #0000FF;
width:150px;
height:75px;
position:absolute;
top:60px;
left:180px;
-ms-transform: rotate(16deg);
-webkit-transform: rotate(16deg);
transform: rotate(16deg);
}
#rectB{
background-color: #FF0000;
width:50px;
height:130px;
position:absolute;
top:140px;
left:250px;
-ms-transform: rotate(28.9deg);
-webkit-transform: rotate(28.9deg);
transform: rotate(28.9deg);
}
<div id="rectA">A</div>
<div id="rectB">B</div>
<div id="msg"></div>

Removing mousemove

I'm developing an app to help autistic children prepare to learn to write. It's very straight forward. They just need to draw a line straight down. I have it working very similar to "connect the dots" where they start at a green light, progress to yellow and then to red. However, on my webpage using a mouse everything works great because the "dots" are "touched" using the mouseover, like so:
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script src="jquery.min.js" type="text/javascript"></script>
<script src="jquery.jplayer.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
var dots = [13, 15, 13, 25, 13, 55, -1, -1,
45, 15, 45, 40, -1, -1,
70, 15, 70, 40, -1, -1,
80, 15, 80, 40, 80, 60, -1, -1];
function contains(arr, value) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] == value) {
return true;
}
}
return false;
}
function getRandomPoints(totalPoints) {
var indexes = new Array();
for (var i = 0; i < totalPoints; i++) {
var done = false;
while (!done) {
var index = Math.floor(Math.random() * dots.length);
if (index % 2 != 0) {
index++;
if (index > dots.length) {
continue;
}
}
if (!contains(indexes, index)) {
indexes.push(index);
done = true;
}
}
}
return indexes.sort(function(a, b) {
return a - b;
});
}
function displayGrid(ctx) {
var gridSize = 20, width = 900;
for (var ypos = 0; ypos < width; ypos += gridSize) {
ctx.moveTo(0, ypos);
ctx.lineTo(width, ypos);
}
for (var xpos = 0; xpos < width; xpos += gridSize) {
ctx.moveTo(xpos, 0);
ctx.lineTo(xpos, width);
}
ctx.strokeStyle = "#eee";
ctx.lineWidth = .7;
ctx.stroke();
}
function addPoint(index, x1, y1) {
for (var i = 0; i < points.length; i++) {
var x2 = points[i].x, y2 = points[i].y;
var d1 = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2));
var d2 = radius * 2 + 2;
if (d2 > d1) {
return false;
}
}
points.push({ 'x': x1, 'y': y1, 'index': index });
return true;
}
//Initialization....
var $graph = $('#graph'), gpos = $graph.position();
var $timer = $('#timer');
var points = new Array();
var ctx = $graph.get(0).getContext("2d");
//Parameters...
var indexes = getRandomPoints(7), ratio = 3, hops = 0, point = 0, maxTotalHops = 60, radius = 12;
var lineWidth = 11.5;
var xDisplacement = 0, yDisplacement = 0;
var borderColor = 'rgb(0,102,204)';
//Display the character's fixed lines...
ctx.beginPath();
ctx.translate(xDisplacement, yDisplacement);
ctx.lineWidth = lineWidth;
for (var i = 0; i < dots.length; i += 2) {
var newLine = dots[i] == -1;
if (newLine) {
i += 2;
}
var x = ratio * dots[i], y = ratio * dots[i + 1];
if (hops == 0 && contains(indexes, i)) {
hops++;
ctx.moveTo(x, y);
continue;
}
if (newLine || i == 0) {
ctx.moveTo(x, y);
}
else {
if (hops == 0) {
ctx.lineTo(x, y);
}
else {
ctx.strokeStyle = borderColor;
ctx.stroke();
ctx.beginPath();
if (addPoint(i, x, y)) {
var cx = gpos.left + xDisplacement - radius + 1 + x;
var cy = gpos.top + yDisplacement - radius + 1 + y;
$('<span></span>')
.addClass('circle')
.html(++point)
.data('pos', { 'x': cx, 'y': cy })
.css({ 'top': 0, 'left': 0 })
.insertAfter($graph);
}
}
}
if (hops > maxTotalHops) {
hops = 0;
}
else if (hops > 0) {
hops++;
}
}
ctx.strokeStyle = borderColor;
ctx.stroke();
//Create and initialize hotspots...
var passed = 0;
$('.circle').each(function() {
var pos = $(this).data('pos');
$(this).animate({
left: pos.x,
top: pos.y
}, 700);
}).mousemove(function() { // <====================== this part
var index = parseInt($(this).text());
if (passed != index - 1) {
return;
}
$(this).css({
'color': '#c00',
'font-weight': 'bold'
}).animate({
left: 0,
top: 0,
opacity: 0
}, 1000);
ctx.beginPath();
var start, end, done = passed + 1 == points.length;
if (done) /*The entire hotspots are detected...*/{
start = 0;
end = dots.length - 2;
clearInterval(tid);
$timer.html('Well done, it took ' + $timer.html() + ' seconds!').animate({
left: gpos.left + $graph.width() - $timer.width() - 20
}, 1000);
}
else {
start = passed == 0 ? points[passed].index - 4 : points[passed - 1].index;
end = points[passed].index;
}
for (var i = start; i <= end; i += 2) {
var newLine = dots[i] == -1;
if (newLine) {
i += 2;
}
var x = ratio * dots[i], y = ratio * dots[i + 1];
if (newLine || i == start) {
ctx.moveTo(x, y);
}
else {
ctx.lineTo(x, y);
}
}
ctx.lineWidth = lineWidth;
ctx.strokeStyle = borderColor;
ctx.stroke();
if (done) {
$('.filled').css({
left: gpos.left + xDisplacement + 10,
top: gpos.top + yDisplacement + 150
}).fadeIn('slow');
}
passed++;
});
//Initialize timer...
$timer.css({
top: gpos.top + 10,
left: gpos.left + 10
});
var timer = 0, tid = setInterval(function() {
timer += 30 / 1000;
$timer.html(timer.toFixed(2));
}, 30);
});
</script>
<style type="text/css">
.circle {
background: url('start.png');
width: 24px;
height: 24px;
text-align: center;
font-size: .8em;
line-height: 24px;
display: block;
position: absolute;
cursor: pointer;
color: #333;
z-index: 100;
}
.filled {
background: url('train.gif');
position: absolute;
width: 172px;
height: 251px;
display: none;
}
#timer {
position: absolute;
font-family: Arial;
font-weight: bold;
font-size: 1em;
background: #c00;
color: #fff;
padding: 5px;
text-align: center;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
font-variant: small-caps;
}
#graph {
background: url('vlinesbackground.jpg');
left: 5px;
top: 20px;
position: relative;
border: 1px dotted #ddd;
}
</style>
But I'm trying to replace the mousemove so the app can be used on the iphone. I've worked out everything else but triggering the "dots" and although I've looked at all the touchstart/touchmove info I can google, nothing appears to be working. Suggestions? Thanks!

Categories