I have code like this:
$(function() {
var $selection = $('.selection');
$('li').filter(function() {
var self = $(this);
return /* ????? */;
}).addClass('selected');
});
.widget {
width: 320px;
height: 200px;
border: 1px solid gray;
position: absolute;
overflow: scroll;
}
.selection {
position: absolute;
top: 90px;
left: 90px;
border: 1px dotted black;
height: 120px;
width: 120px;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
float: left;
background: blue;
width: 40px;
height: 40px;
margin: 10px;
}
li.selected {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="widget">
<div class="selection"></div>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
How can I select only those li elements that are intersecting with .selection rectangle?
Using standard DOM techniques, you can iterate over each LI element and obtain a bounding rectangle, which gives the coordinates of the LI's rectangle.
Do this for the selection rectangle too and then you can simply check whether the coordinates are within the range of the selection's.
Element.getBoundingClientRect()
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
The returned value is a DOMRect object which is the union of the rectangles returned by getClientRects() for the element, i.e., the CSS border-boxes associated with the element.
The returned value is a DOMRect object, which contains read-only left, top, right, bottom, x, y, width, height properties describing the border-box in pixels. Properties other than width and height are relative to the top-left of the viewport.
Please see below, the edited code which selects the LI elements that are fully contained or partially intersecting the selection.
var selection = document.querySelector(".selection");
var rectSelection = selection.getBoundingClientRect();
// Iterate over all LI elements.
[].forEach.call(document.querySelectorAll("li"), function(li) {
var rect = li.getBoundingClientRect();
if(rect.bottom > rectSelection.top
&& rect.right > rectSelection.left
&& rect.top < rectSelection.bottom
&& rect.left < rectSelection.right) {
li.classList.add("selected");
}
});
.widget {
width: 320px;
height: 200px;
border: 1px solid gray;
position: absolute;
overflow: scroll;
}
.selection {
position: absolute;
top: 90px;
left: 90px;
border: 1px dotted black;
height: 120px;
width: 120px;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
float: left;
background: blue;
width: 40px;
height: 40px;
margin: 10px;
}
li.selected {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="widget">
<div class="selection"></div>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
The concept to understand box collision Origin answer
If You have to check the 2 rects with rotation you have to make projection of rect corner on the axes of the other rect. If all projections of rect1 hit the rect2, and rect2 projections hit rect1 so the two rect collide.
Some projection don't collide here, the 2 rects are not collide.
The 4 projections hit the other rect, the 2 rects are collide.
I have made a presentation on this CodePen for more comprehension.
Clone of the code here (it's old and durty, don't be rude it's just to display.. don't look the code to much :p)
function merge_object(obj1, obj2){
var obj3 = {};
for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
return obj3;
};
/**
* Transforme degrees to radians
*/
Math.radians = function(degrees) {
return degrees * Math.PI / 180;
};
Math.degrees = function(radians) {
return radians * 180 / Math.PI;
};
/**
* Square Javascript File
*
* -- Changelog
* Version 1.0.0 (11/05/2013) AGE
* - Début du versionnement
*
* @package js
* @author AGE
* @version 1.0.0
*/
// App Object
var collide = {
// Define
PROJECT_ID : 'collide',
PROJECT_SPEED : 50, // FPS : 1000/PROJECT_SPEED
FIXED_SQUARE_SIZE : 100,
FIXED_SQUARE_X : 250,
FIXED_SQUARE_Y : 250,
CURSOR_SQUARE_SIZE : 50,
// Attribute
$el : null,
$ctx : null,
project_pos_top : null,
project_pos_left : null,
width : null,
height : null,
cursor_x : 0,
cursor_y : 0,
fixed_angle : 0,
fixed_rotaton_speed : null,
cursor_angle : 0,
cursor_rotaton_speed : null,
draw_approx : true,
fixed_projections : {},
cursor_projections : {},
/**
* Init Square Game
*/
__init : function( options ){
// Get element and informations
collide.$el = $('#' + collide.PROJECT_ID);
collide.$ctx = collide.$el[0].getContext('2d');
var offset = collide.$el.offset();
collide.project_pos_top = offset.top;
collide.project_pos_left = offset.left;
collide.width = collide.$el.width();
collide.height = collide.$el.height();
// Events on Move
collide.$el.mousemove(function(e){
// Calculate new position
collide.cursor_x = e.pageX - collide.project_pos_left;
collide.cursor_y = e.pageY - collide.project_pos_top;
});
$('#cursor_set_angle').change(function(){
collide.cursor_angle = parseInt($(this).val());
});
$('#fixed_set_angle').change(function(){
collide.fixed_angle = parseInt($(this).val());
});
// Run the project
collide.run();
},
/**
* Run the project
*/
run : function(){
// Reset HTML
$('.corners, .functions, .projections').remove();
// Get 2 angles rotation speed
collide.fixed_rotaton_speed = parseInt($('#fixed_rotation_speed').val());
collide.cursor_rotaton_speed = parseInt($('#cursor_rotation_speed').val());
// Upadte rotation
collide.fixed_angle = collide.fixed_angle+collide.fixed_rotaton_speed;
if(collide.fixed_angle > 360) {collide.fixed_angle -= 360};
if(collide.fixed_angle < 0) {collide.fixed_angle += 360};
$('#fixed_angle').val(collide.fixed_angle);
collide.cursor_angle = collide.cursor_angle+collide.cursor_rotaton_speed;
if(collide.cursor_angle > 360) {collide.cursor_angle -= 360};
if(collide.cursor_angle < 0) {collide.cursor_angle += 360};
$('#cursor_angle').val(collide.cursor_angle);
// Approx Collide
if(collide.is_approx_collide()){
collide.draw_approx = false;
// Get corners
var fixer_corner = collide.get_corners(
collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.FIXED_SQUARE_SIZE, collide.fixed_angle);
var cursor_corner = collide.get_corners(
collide.cursor_x, collide.cursor_y,
collide.CURSOR_SQUARE_SIZE, collide.cursor_angle);
// Get projections
// CURSOR on FIXED.X && FIXED.Y
collide.cursor_projections = collide.get_projections(
collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.fixed_angle,cursor_corner);
// FIXED on CURSOR.X && CURSOR.Y
collide.fixed_projections = collide.get_projections(
collide.cursor_x, collide.cursor_y,
collide.cursor_angle, fixer_corner);
}else{
collide.draw_approx = true;
}
collide.draw();
// Re-lauch Run
setTimeout(function(){
collide.run();
}, collide.PROJECT_SPEED);
},
draw : function(){
// Clear
collide.$ctx.clearRect(0, 0, collide.width, collide.height);
collide.$ctx.setTransform(1, 0, 0, 1, 0, 0);
collide.$ctx.save();
// Draw squares
collide.draw_square(collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.FIXED_SQUARE_SIZE, collide.fixed_angle, '0,0,255');
collide.draw_square(collide.cursor_x, collide.cursor_y,
collide.CURSOR_SQUARE_SIZE, collide.cursor_angle, '255,128,0');
if(collide.draw_approx){
// Approx
collide.draw_approx_square(collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.FIXED_SQUARE_SIZE, '0,0,255');
collide.draw_approx_square(collide.cursor_x, collide.cursor_y,
collide.CURSOR_SQUARE_SIZE, '255,128,0');
}
else{
// Axes
collide.draw_axe(collide.FIXED_SQUARE_X, collide.FIXED_SQUARE_Y,
collide.fixed_angle, '0,0,255');
collide.draw_axe(collide.cursor_x, collide.cursor_y,
collide.cursor_angle, '255,128,0');
// Projections
collide.draw_projections( collide.fixed_projections, '0,0,255');
collide.draw_projections( collide.cursor_projections, '255,128,0');
}
},
draw_square : function(center_x, center_y, size, angle, rgb){
// Is collide ?
if(collide.is_approx_collide() && collide.is_collide()){ rgb = "255,0,0"; }
collide.$ctx.save();
// Draw Fixed square
collide.$ctx.translate(center_x, center_y);
collide.$ctx.rotate( Math.radians( angle ) );
collide.$ctx.fillStyle = 'rgba('+rgb+',.2)';
collide.$ctx.fillRect(size / -2, size / -2, size, size);
// Draw Corner
collide.$ctx.translate(size/2, size/2);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.translate(-size, 0);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.translate(0, -size);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.translate(size,0);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.restore();
},
draw_approx_square : function(center_x, center_y, size, rgb){
collide.$ctx.save();
// Draw Fixed square
collide.$ctx.translate(center_x, center_y);
collide.$ctx.strokeStyle = 'rgba('+rgb+',1)';
collide.$ctx.lineWidth = 1;
collide.$ctx.strokeRect(-size, -size, 2*size, 2*size);
collide.$ctx.restore();
},
draw_axe : function (center_x, center_y, angle, rgb){
collide.$ctx.save();
collide.$ctx.translate(center_x, center_y);
collide.$ctx.rotate( Math.radians( angle ) );
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = 'rgba('+rgb+',1)';
collide.$ctx.moveTo(-500, 0);
collide.$ctx.lineTo(500,0);
collide.$ctx.stroke();
collide.$ctx.restore();
collide.$ctx.save();
collide.$ctx.translate(center_x, center_y);
collide.$ctx.rotate( Math.radians( angle+90 ) );
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = 'rgba('+rgb+',1)';
collide.$ctx.moveTo(-500,0);
collide.$ctx.lineTo(500,0);
collide.$ctx.stroke();
collide.$ctx.restore();
},
draw_projections : function(projections, rgb){
for(axe in projections){
for(key in ['min', 'max']){
var projection = projections[axe][key==0?"min":"max"];
collide.$ctx.save();
collide.$ctx.translate(projection.x, projection.y);
collide.$ctx.beginPath();
collide.$ctx.fillStyle = 'rgba('+rgb+',1)';
collide.$ctx.arc(0, 0, 2, 0, Math.PI*2, true);
collide.$ctx.closePath();
collide.$ctx.fill();
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = 'rgba('+rgb+',.2)';
collide.$ctx.moveTo(00,0);
collide.$ctx.lineTo(projection.corner.x - projection.x,
projection.corner.y - projection.y);
collide.$ctx.stroke();
collide.$ctx.restore();
}
collide.$ctx.save();
collide.$ctx.beginPath();
collide.$ctx.strokeStyle = (projections[axe].is_collide)? 'rgba(255,0,0,1)' : 'rgba('+rgb+',1)';
collide.$ctx.lineWidth = 2;
collide.$ctx.moveTo(projections[axe].min.x, projections[axe].min.y);
collide.$ctx.lineTo(projections[axe].max.x,projections[axe].max.y);
collide.$ctx.stroke();
collide.$ctx.restore();
}
},
/**
* Calculate corners position and draw it
*/
get_corners : function(x, y, size, angle){
var corners = [];
var radius = parseInt(Math.sqrt(2*size*size)/2);
for(var i_angle=0; i_angle<=270; i_angle+=90){
var corner = {
x : parseInt(x + radius * Math.cos(Math.radians(angle + 45 + i_angle) )),
y : parseInt(y + radius * Math.sin(Math.radians(angle + 45 + i_angle) ))
};
corners.push(corner);
}
return corners;
},
/**
* Calculate square functions and draw it
*/
get_functions : function(x, y, size, angle){
var functions = {
x : Math.tan(Math.radians( - angle%90)),
y : Math.tan(Math.radians(90 - angle%90))
};
return functions;
},
get_projections : function(center_x, center_y,angle, corners){
// Genere start Min-Max projection on center of Square
var projections = {
"x": {
'min' : null,
'max' : null,
'distance' : null
},
"y": {
'min' : null,
'max' : null,
'distance' : null
}
};
for(i in corners){
var corner = corners[i];
var projection_x = {}, projection_y = {};
/**
* Global calcul for projection X and Y
*/
// Angle 0:horizontale (center > left) 90:verticatale (center > top)
var angle90 = -(angle%90);
//Distance :
var distance_corner_center = Math.floor(Math.sqrt((center_x-corner.x)*(center_x-corner.x) + (center_y-corner.y)*(center_y-corner.y)));
// Angle between segment [center-corner] and real axe X (not square axe), must be negative (radius are negative clockwise)
var angle_with_axeX = -Math.floor(Math.degrees(Math.atan((corner.y-center_y) / (corner.x-center_x)))); // Tan(alpha) = opposé (ecart sur Y) / adjacent (ecart sur X)
// If angle is ]0;90[, he is on the 2em et 4th quart of rotation
if(angle_with_axeX > 0) {angle_with_axeX -= 180;}
// If corner as upper (so with less pixel on y) thant center, he is on 3th or 4th quart of rotation
if(corner.y < center_y || (corner.y == center_y && corner.x < center_x) ){angle_with_axeX -= 180;}
// Calculate difference between 2 angles to know the angle between [center-corner] and Square axe X
var delta_angle = angle_with_axeX - angle90;
// If angle is on ]-180;-360], corner are upper than Square axe X, so set a positive angle on [0;180]
if(delta_angle < -180){delta_angle += 360;}
/**
* Projection on X
*/
// Calculate distance between center and projection on axe X
var distance_center_projection_x = Math.floor(distance_corner_center * Math.cos(Math.radians( delta_angle )));
// Create projection
projection_x.x = Math.floor(center_x + distance_center_projection_x * Math.cos(Math.radians( -angle90 )));
projection_x.y = Math.floor(center_y + distance_center_projection_x * Math.sin(Math.radians( -angle90 )));
// If is the min ?
if(projections.x.min == null
|| distance_center_projection_x < projections.x.min.distance){
projections.x.min = projection_x;
projections.x.min.distance = distance_center_projection_x;
projections.x.min.corner = corner;
}
// Is the max ?
if(projections.x.max == null
|| distance_center_projection_x > projections.x.max.distance){
projections.x.max = projection_x;
projections.x.max.distance = distance_center_projection_x;
projections.x.max.corner = corner;
}
/**
* Projection on Y
*/
// Calculate distance between center and projection on axe Y
var distance_center_projection_y = Math.floor(distance_corner_center * Math.cos(Math.radians( delta_angle-90 )));
// Create projection
projection_y.x = Math.floor(center_x + distance_center_projection_y * Math.cos(Math.radians( -angle90 -90 )));
projection_y.y = Math.floor(center_y + distance_center_projection_y * Math.sin(Math.radians( -angle90 -90)));
// If is the min ?
if(projections.y.min == null
|| distance_center_projection_y < projections.y.min.distance){
projections.y.min = projection_y;
projections.y.min.distance = distance_center_projection_y;
projections.y.min.corner = corner;
}
// Is the max ?
if(projections.y.max == null
|| distance_center_projection_y > projections.y.max.distance){
projections.y.max = projection_y;
projections.y.max.distance = distance_center_projection_y;
projections.y.max.corner = corner;
}
}
// Return object
return projections;
},
is_approx_collide : function(){
return (collide.FIXED_SQUARE_X + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE >= collide.cursor_x
&& collide.FIXED_SQUARE_Y + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE >= collide.cursor_y
&& collide.FIXED_SQUARE_X <= collide.cursor_x + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE
&& collide.FIXED_SQUARE_Y <= collide.cursor_y + collide.FIXED_SQUARE_SIZE + collide.CURSOR_SQUARE_SIZE) ? true : false;
},
is_collide : function(){
collide.fixed_projections.x.is_collide =
( (collide.fixed_projections.x.min.distance <= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.x.max.distance >= -collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.x.min.distance <= collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.x.max.distance >= collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.x.min.distance >= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.x.max.distance <= collide.CURSOR_SQUARE_SIZE/2 )) ? true : false;
collide.fixed_projections.y.is_collide =
( (collide.fixed_projections.y.min.distance <= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.y.max.distance >= -collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.y.min.distance <= collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.y.max.distance >= collide.CURSOR_SQUARE_SIZE/2 )
|| (collide.fixed_projections.y.min.distance >= -collide.CURSOR_SQUARE_SIZE/2 && collide.fixed_projections.y.max.distance <= collide.CURSOR_SQUARE_SIZE/2 )) ? true : false;
collide.cursor_projections.x.is_collide =
( (collide.cursor_projections.x.min.distance <= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.x.max.distance >= -collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.x.min.distance <= collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.x.max.distance >= collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.x.min.distance >= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.x.max.distance <= collide.FIXED_SQUARE_SIZE/2 )) ? true : false;
collide.cursor_projections.y.is_collide =
( (collide.cursor_projections.y.min.distance <= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.y.max.distance >= -collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.y.min.distance <= collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.y.max.distance >= collide.FIXED_SQUARE_SIZE/2 )
|| (collide.cursor_projections.y.min.distance >= -collide.FIXED_SQUARE_SIZE/2 && collide.cursor_projections.y.max.distance <= collide.FIXED_SQUARE_SIZE/2 )) ? true : false;
return (collide.fixed_projections.x.is_collide
&& collide.fixed_projections.y.is_collide
&& collide.cursor_projections.x.is_collide
&& collide.cursor_projections.y.is_collide) ? true : false;
}
};
$(document).ready(function(){
collide.__init();
});
var debug = function(attr){
if($('.debugjs').length == 0){
$('<span class="debugjs"></span>').appendTo('body');
}
$('.debugjs').text(attr);
}
#collide {
margin: 25px;
border: 1px solid black;
float: left;
}
#form {
margin-left: 550px;
padding-top: 2px;
}
#fixed_form h2 {
color: #0033FF;
}
#cursor_form h2 {
color: #FF8800;
}
#fixed_form input, #cursor_form input {
margin-left: 25px;
width: 50px;
}
#fixed_rotation_speed, #cursor_rotation_speed {
margin-right: 150px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="collide" width="500" height="500"></canvas>
<div id="form">
<h3>Understand how to detect if 2 rotates squares collide.</h3>
<p>The 2 rotates squares collide only when all the projection of a square hit the second one.</p>
<div id="fixed_form">
<h2>Fixed square :</h2>
Rotation speed : <input type="number" id="fixed_rotation_speed" value="0"/>
Angle : <input type="number" id="fixed_angle" value="0"/>
(Set : <input type="number" id="fixed_set_angle" value="0"/>)
</div>
<div id="cursor_form">
<h2>Cursor square :</h2>
Rotation speed : <input type="number" id="cursor_rotation_speed" value="-1"/>
Angle : <input type="number" id="cursor_angle" value="0"/>
(Set : <input type="number" id="cursor_set_angle" value="0"/>)
</div>
</div>
Do the same job with no-rotate rect (it will be way easier) and it will work on every case
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With