Drawing a scalloped rectangle - javascript

I am trying to draw a scalloped rectangle with mouse like in below image
I am able to draw rectangle with code below
function Rectangle(start, end) {
var w = (end.x - start.x);
var h = (end.y - start.y);
return ["M", start.x, start.y, "L", (start.x + w), start.y, "L", start.x + w, start.y + h, "L", start.x, start.y + h, "L", start.x, start.y].join(' ');
}
var point;
document.addEventListener('mousedown', function(event) {
point = {
x: event.clientX,
y: event.clientY
}
});
document.addEventListener('mousemove', function(event) {
var target = {
x: event.clientX,
y: event.clientY
}
if(point) {
var str = Rectangle(point, target);
document.getElementById('test').setAttribute('d', str);
}
});
document.addEventListener('mouseup', function(event) {
point = null;
});
body, html {
height: 100%;
width: 100%;
margin: 0;
padding: 0
}
svg {
height:100%;
width: 100%
}
<svg>
<path id="test" style="stroke-width: 4; stroke: RGBA(212, 50, 105, 1.00); fill: none" />
</svg>
But when I try to convert into scalloped rectangle I am seeing different patterns exactly matching Infinite Monkey Theorm
The approach I tried is, draw a rectangular path on a virtual element. Take each point at length multiplied by 15 till total length. Then drawing arcs between those points. It is not working. Also, I want to avoid using getPointAtLength because it's mobile support is not great.
var pathEle = document.createElementNS('http://www.w3.org/2000/svg', 'path');
pathEle.setAttribute('d', rectangle(point, target));
window.pathEle = pathEle;
var points = [];
for (var i = 0; i < pathEle.getTotalLength(); i += 15) {
points.push(pathEle.getPointAtLength(i));
}
document.getElementById('test1').setAttribute('d', toSVGPath(points));

Something like this?
I'm using arcs to make the scallops. You may want to tweak how the scallops are calculated to get better corners. But I'll leave that to you.
var scallopSize = 30;
function Rectangle(start, end) {
var minX = Math.min(start.x, end.x);
var minY = Math.min(start.y, end.y);
var w = Math.abs(end.x - start.x);
var h = Math.abs(end.y - start.y);
// Calculate scallop sizes
var numW = Math.round(w / scallopSize);
if (numW === 0) numW = 1;
var numH = Math.round(h / scallopSize);
if (numH === 0) numH = 1;
var stepW = w / numW;
var stepH = h / numH;
// top
var p = minX + stepW/2; // start each size at half a scallop along
var path = ["M", p, minY];
for (var i=1; i < numW; i++) { // numW-1 scallops per side
p += stepW;
path.push('A');
path.push(stepW/2 + 1); // Add 1 to the radius to ensure it's
path.push(stepW/2 + 1); // big enough to span the stepW
path.push("0 0 1");
path.push(p);
path.push(minY);
}
// top right
var p = minY + stepH/2;
path.push('A');
path.push(stepH/2.8); // 2 * sqrt(2)
path.push(stepH/2.8); // corners are a little smaller than the scallops
path.push("0 0 1");
path.push(minX + w);
path.push(p);
// right
for (var i=1; i < numH; i++) {
p += stepH;
path.push('A');
path.push(stepH/2 + 1);
path.push(stepH/2 + 1);
path.push("0 0 1");
path.push(minX + w);
path.push(p);
}
// bottom right
var p = minX + w - stepW/2;
path.push('A');
path.push(stepH/2.8);
path.push(stepH/2.8);
path.push("0 0 1");
path.push(p);
path.push(minY + h);
// bottom
for (var i=1; i < numW; i++) {
p -= stepW;
path.push('A');
path.push(stepW/2 + 1);
path.push(stepW/2 + 1);
path.push("0 0 1");
path.push(p);
path.push(minY + h);
}
// bottom left
var p = minY + h - stepH/2;
path.push('A');
path.push(stepH/2.8);
path.push(stepH/2.8);
path.push("0 0 1");
path.push(minX);
path.push(p);
// left
for (var i=1; i < numH; i++) {
p -= stepH;
path.push('A');
path.push(stepH/2 + 1);
path.push(stepH/2 + 1);
path.push("0 0 1");
path.push(minX);
path.push(p);
}
// top left
path.push('A');
path.push(stepH/2.8);
path.push(stepH/2.8);
path.push("0 0 1");
path.push(minX + stepW/2);
path.push(minY);
path.push('Z');
return path.join(' ');
}
var point;
document.addEventListener('mousedown', function(event) {
point = {
x: event.clientX,
y: event.clientY
}
});
document.addEventListener('mousemove', function(event) {
var target = {
x: event.clientX,
y: event.clientY
}
if(point) {
var str = Rectangle(point, target);
document.getElementById('test').setAttribute('d', str);
}
});
document.addEventListener('mouseup', function(event) {
point = null;
});
body, html {
height: 100%;
width: 100%;
margin: 0;
padding: 0
}
svg {
height:100%;
width: 100%
}
<svg>
<path id="test" style="stroke-width: 4; stroke: RGBA(212, 50, 105, 1.00); fill: none" />
</svg>

Related

Creating click events for looped rectangles in canvas [duplicate]

I dont know how to register click event on each rectangle.
here is the sample:
http://jsfiddle.net/9WWqG/1/
You're basically going to have to track where your rectangles are on the canvas, then set up an event listener on the canvas itself. From there you can take the coordinates of the click event and go through all your rectangles to test for 'collisions'.
Here's an example of doing just that: http://jsfiddle.net/9WWqG/2/
html:
<canvas id="myCanvas" width="300" height="150"></canvas>
javascript:
// get canvas element.
var elem = document.getElementById('myCanvas');
function collides(rects, x, y) {
var isCollision = false;
for (var i = 0, len = rects.length; i < len; i++) {
var left = rects[i].x, right = rects[i].x+rects[i].w;
var top = rects[i].y, bottom = rects[i].y+rects[i].h;
if (right >= x
&& left <= x
&& bottom >= y
&& top <= y) {
isCollision = rects[i];
}
}
return isCollision;
}
// check if context exist
if (elem && elem.getContext) {
// list of rectangles to render
var rects = [{x: 0, y: 0, w: 50, h: 50},
{x: 75, y: 0, w: 50, h: 50}];
// get context
var context = elem.getContext('2d');
if (context) {
for (var i = 0, len = rects.length; i < len; i++) {
context.fillRect(rects[i].x, rects[i].y, rects[i].w, rects[i].h);
}
}
// listener, using W3C style for example
elem.addEventListener('click', function(e) {
console.log('click: ' + e.offsetX + '/' + e.offsetY);
var rect = collides(rects, e.offsetX, e.offsetY);
if (rect) {
console.log('collision: ' + rect.x + '/' + rect.y);
} else {
console.log('no collision');
}
}, false);
}
This is an old question but what was once hard to do when it was posted is now much easier.
There are many libraries that keep track of the position of your objects that were drawn on canvas and handle all of the complexities of handling mouse interactions. See EaselJS,
KineticJS,
Paper.js or
Fabric.js and this comparison of canvas libraries for more.
You can also take a different approach and use
Raphaël and gRaphaël
to have a solution that uses SVG and VML instead of canvas and works even on IE6.
Your example changed to use Raphaël would look like this:
var r = Raphael(0, 0, 300, 150);
r.rect(0, 0, 50, 50)
.attr({fill: "#000"})
.click(function () {
alert('first rectangle clicked');
});
r.rect(75, 0, 50, 50)
.attr({fill: "#000"})
.click(function () {
alert('second rectangle clicked');
});
See DEMO.
Update 2015
You may also be able to use ART, a retained mode vector drawing API for HTML5 canvas - see this answer for more info.
I found a way to make this work in mozilla using the clientX,clientY instead of offsetX/offsetY.
Also, if your canvas extends beyond the innerHeight, and uses the scroll, add the window.pageYOffset to the e.clientY. Goes the same way, if your canvas extends beyond the width.
Another example is at my github: https://github.com/michaelBenin/fi-test
Here is another link that explains this: http://eli.thegreenplace.net/2010/02/13/finding-out-the-mouse-click-position-on-a-canvas-with-javascript/
Please use below function if you want to support more than one rectangle in canvas and handle its click event
<canvas id="myCanvas" width="1125" height="668" style="border: 3px solid #ccc; margin:0;padding:0;" />
var elem = document.getElementById('myCanvas'),
elemLeft = elem.offsetLeft,
elemTop = elem.offsetTop,
context = elem.getContext('2d'),
elements = [];
// Add event listener for `click` events.
elem.addEventListener('click', function (event) {
// var leftWidth = $("#leftPane").css("width")
// var x = event.pageX - (elemLeft + parseInt(leftWidth) + 220),
// y = event.pageY - (elemTop + 15);
var x = event.pageX - elemLeft,
y = event.pageY - elemTop;
elements.forEach(function (element) {
if (y > element.top && y < element.top + element.height && x > element.left && x < element.left + element.width) {
alert(element.text);
}
});
}, false);
// Set the value content (x,y) axis
var x = 15, y = 20, maxWidth = elem.getAttribute("width"),
maxHeight = elem.getAttribute("height"), type = 'TL',
width = 50, height = 60, text = "", topy = 0, leftx = 0;
for (i = 1; i <= 15; i++) {
y = 10;
for (j = 1; j <= 6; j++) {
width = 50, height = 60
switch (j) {
case 1:
type = 'TL'; // Trailer
height = 60;
width = 85;
text = i + 'E';
break;
case 2:
type = 'DR'; // Door
height = 35;
width = 85;
text = i;
break;
case 3:
type = 'FL'; // Floor
height = 30;
width = 40;
break;
case 4:
type = 'FL'; // Floor
height = 30;
width = 40;
y -= 10;
break;
case 5:
type = 'DR'; // Door
height = 35;
width = 85;
text = i*10 + 1;
y = topy;
break;
case 6:
type = 'TL'; // Trailer
height = 60;
width = 85;
text = i + 'F';
y += 5;
break;
}
topy = y;
leftx = x;
if (type == 'FL') {
for (k = 1; k <= 12; k++) {
elements.push({
colour: '#05EFFF',
width: width,
height: height,
top: topy,
left: leftx,
text: k,
textColour: '#fff',
type: type
});
if (k % 2 == 0) {
topy = y + elements[j - 1].height + 5;
leftx = x;
y = topy;
}
else {
topy = y;
leftx = x + elements[j - 1].width + 5;
}
}
x = leftx;
y = topy;
}
else {
elements.push({
colour: '#05EFFF',
width: width,
height: height,
top: y,
left: x,
text: text,
textColour: '#fff',
type: type
});
}
//get the y axis for next content
y = y + elements[j-1].height + 6
if (y >= maxHeight - elements[j-1].height) {
break;
}
}
//get the x axis for next content
x = x + elements[0].width + 15
if (x >= maxWidth - elements[0].width) {
break;
}
}
// Render elements.
elements.forEach(function (element) {
context.font = "14pt Arial";
context.strokeStyle = "#000";
context.rect(element.left, element.top, element.width, element.height);
if (element.type == 'FL') {
context.fillText(element.text, element.left + element.width / 4, element.top + element.height / 1.5);
}
else {
context.fillText(element.text, element.left + element.width / 2.5, element.top + element.height / 1.5);
}
context.lineWidth = 1;
context.stroke()
});
Here's an example of doing just that: http://jsfiddle.net/BmeKr/1291/
Please use below function if you want to support more than one rectangle in canvas and handle its click event..... modified logic given by Matt King.
function collides(myRect, x, y) {
var isCollision = false;
for (var i = 0, len = myRect.length; i < len; i++) {
var left = myRect[i].x, right = myRect[i].x+myRect[i].w;
var top = myRect[i].y, bottom = myRect[i].y+myRect[i].h;
if ((left + right) >= x
&& left <= x
&& (top +bottom) >= y
&& top <= y) {
isCollision = json.Major[i];
}
}
}
return isCollision;
}

Tooltip moves further from mouseover location as I scroll HTML page

I'm using JQuery mouseover and CSS with "position: absolute;" to position the tooltip next to a particular matrix (x, y) location where the mouse is pointing to.
But as I scroll down, the tooltip moves further from the mouse pointer! :(
Here are the two images:
Before scrolling down
After scrolling down
My fiddle: http://jsfiddle.net/kjx9z1uy/22/
My code snippet:
var colours = ['#F5F5F5', '#F5F5F5', '#F5F5F5', '#f4cd42', '#F5F5F5'];
var n = colours.length;
var w = 15;
var h = w * 4;
// create an offsreen canvas containing the desired coloured circles
var off = document.createElement('canvas');
off.width = n * w;
off.height = h;
var ctx = off.getContext('2d');
// request mousemove events
var tipCanvas = document.getElementById("tip");
var tipCtx = tipCanvas.getContext("2d");
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
for (var i = 0; i < n; ++i) {
ctx.beginPath();
ctx.rect(i*w, 0, w, h)
// Fill
ctx.globalAlpha = "0.8"; // Opacity
ctx.fillStyle = colours[i];
ctx.fill();
// Stroke
ctx.globalAlpha = "1.0"; // Opacity reset
//ctx.lineWidth = "1";
ctx.strokeStyle = "black";
ctx.stroke();
}
// get handles to the on-screen canvas
var canvas = document.getElementById('canvas');
var ctx2 = canvas.getContext('2d');
// create 3000 random points
var points = [];
var num_boxes = Math.floor(canvas.width/w)
* Math.floor(canvas.height/h)
console.log("# boxes: " + num_boxes)
for (var i = 0; i < Math.floor(canvas.width/w); ++i) {
for (var j = 0; j < Math.floor(canvas.height/h); ++j) {
var point_left = Math.floor(i*w);
var point_top = Math.floor(j*h);
var point_right = point_left + w;
var point_bottom = point_top + h;
points.push({
c: i % n,
x: point_left,
y: point_top,
w: w,
h: h,
r: point_right,
b: point_bottom,
text: "Data: (" + i + ", " + j + ") at (" + point_left + ", " + point_top + ", " + point_right + ", " + point_bottom + ")"
});
}
}
// copy points from the offscreen canvas into the on-page canvas
var t0 = Date.now();
ctx2.font = "bold 15px sans-serif";
ctx2.fillStyle = "black";
for (var i = 0; i < points.length; ++i) {
var p = points[i];
ctx2.drawImage(off, p.c * p.w, 0, p.w, p.h, p.x, p.y, p.w, p.h);
ctx2.fillText(p.c, p.x, p.y+p.h);
//console.log("RECT:", p.x, p.y, p.w, p.h)
}
var t1 = Date.now();
console.log("Elapsed time: " + (t1 - t0) + "ms");
// show tooltip when mouse hovers over dot
function handleMouseMove(e) {
mouseX = parseInt(e.clientX);
mouseY = parseInt(e.clientY);
var hit = false;
var adjustY = 15 + tipCanvas.height
for (var i = 0; i < points.length; ++i) {
var p = points[i];
var withinX = (mouseX >= p.x) && (mouseX < p.r);
var withinY = (mouseY >= p.y) && (mouseY < p.b);
if (withinX && withinY) {
if ((mouseX + tipCanvas.width) < canvas.width) {
tipCanvas.style.left = mouseX + "px";
}
else {
if (tipCanvas.width < canvas.width) {
tipCanvas.style.left = (mouseX - tipCanvas.width) + "px";
}
else {
tipCanvas.style.left = mouseX + "px";
}
}
if (mouseY > adjustY) {
tipCanvas.style.top = (mouseY - adjustY) + "px";
}
else {
tipCanvas.style.top = (mouseY + adjustY) + "px";
}
tipCtx.clearRect(0, 0, tipCanvas.width, tipCanvas.height);
tipCtx.fillText(p.text, 5, 15);
tipCtx.fillText('Mouse: (' + mouseX + ', ' + mouseY + ")", 5, 30);
hit = true;
}
}
if (!hit) {
tipCanvas.style.left = 0 - tipCanvas.width - 40 + "px";
}
}
#tip {
background-color: white;
border: 1px solid blue;
position: absolute;
left: -200px;
top: -100px;
}
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<canvas id="tip" width=200 height=40></canvas>
<canvas id="canvas" width="1000" height="15000"> </canvas>
Because you didn't add the scrolled height.
In handleMouseMove() function, I added window.scrollY to Y axis value.
var colours = ['#F5F5F5', '#F5F5F5', '#F5F5F5', '#f4cd42', '#F5F5F5'];
var n = colours.length;
var w = 15;
var h = w * 4;
// create an offsreen canvas containing the desired coloured circles
var off = document.createElement('canvas');
off.width = n * w;
off.height = h;
var ctx = off.getContext('2d');
// request mousemove events
var tipCanvas = document.getElementById("tip");
var tipCtx = tipCanvas.getContext("2d");
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
for (var i = 0; i < n; ++i) {
ctx.beginPath();
ctx.rect(i*w, 0, w, h)
// Fill
ctx.globalAlpha = "0.8"; // Opacity
ctx.fillStyle = colours[i];
ctx.fill();
// Stroke
ctx.globalAlpha = "1.0"; // Opacity reset
//ctx.lineWidth = "1";
ctx.strokeStyle = "black";
ctx.stroke();
}
// get handles to the on-screen canvas
var canvas = document.getElementById('canvas');
var ctx2 = canvas.getContext('2d');
// create 3000 random points
var points = [];
var num_boxes = Math.floor(canvas.width/w)
* Math.floor(canvas.height/h)
console.log("# boxes: " + num_boxes)
for (var i = 0; i < Math.floor(canvas.width/w); ++i) {
for (var j = 0; j < Math.floor(canvas.height/h); ++j) {
var point_left = Math.floor(i*w);
var point_top = Math.floor(j*h);
var point_right = point_left + w;
var point_bottom = point_top + h;
points.push({
c: i % n,
x: point_left,
y: point_top,
w: w,
h: h,
r: point_right,
b: point_bottom,
text: "Data: (" + i + ", " + j + ") at (" + point_left + ", " + point_top + ", " + point_right + ", " + point_bottom + ")"
});
}
}
// copy points from the offscreen canvas into the on-page canvas
var t0 = Date.now();
ctx2.font = "bold 15px sans-serif";
ctx2.fillStyle = "black";
for (var i = 0; i < points.length; ++i) {
var p = points[i];
ctx2.drawImage(off, p.c * p.w, 0, p.w, p.h, p.x, p.y, p.w, p.h);
ctx2.fillText(p.c, p.x, p.y+p.h);
//console.log("RECT:", p.x, p.y, p.w, p.h)
}
var t1 = Date.now();
console.log("Elapsed time: " + (t1 - t0) + "ms");
// show tooltip when mouse hovers over dot
function handleMouseMove(e) {
mouseX = parseInt(e.clientX);
mouseY = parseInt(e.clientY);
var hit = false;
var adjustY = 15 + tipCanvas.height;
var scrollY = window.scrollY;
for (var i = 0; i < points.length; ++i) {
var p = points[i];
var withinX = (mouseX >= p.x) && (mouseX < p.r);
var withinY = (mouseY >= p.y) && (mouseY < p.b);
if (withinX && withinY) {
if ((mouseX + tipCanvas.width) < canvas.width) {
tipCanvas.style.left = mouseX + "px";
}
else {
if (tipCanvas.width < canvas.width) {
tipCanvas.style.left = (mouseX - tipCanvas.width) + "px";
}
else {
tipCanvas.style.left = mouseX + "px";
}
}
if (mouseY > adjustY) {
tipCanvas.style.top = (mouseY - adjustY + scrollY) + "px";
}
else {
tipCanvas.style.top = (mouseY + adjustY + scrollY) + "px";
}
tipCtx.clearRect(0, 0, tipCanvas.width, tipCanvas.height);
tipCtx.fillText(p.text, 5, 15);
tipCtx.fillText('Mouse: (' + mouseX + ', ' + mouseY + ")", 5, 30);
hit = true;
}
}
if (!hit) {
tipCanvas.style.left = 0 - tipCanvas.width - 40 + "px";
}
}
#tip {
background-color: white;
border: 1px solid blue;
position: absolute;
left: -200px;
top: -100px;
}
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<canvas id="tip" width=200 height=40></canvas>
<canvas id="canvas" width="1000" height="15000"> </canvas>

JS chart > data to outer space

I am using this chart, with the data in the original var oData it works fantastic. But when I add my values the cart is drawn anywhere but not is the chart it self. The strange thing is, when I change this original data the chart is updated correctly! Any suggestions?
Original data with no issues:
var oData = {
"2008": 10,
"2009": 39.9,
"2010": 17,
"2011": 30.0,
"2012": 5.3,
"2013": 38.4,
"2014": 15.7,
"2015": 9.0
};
When I change this data to there are no issues:
var oData = {
"2008": 100,
"2009": 390.9,
"2010": 170,
"2011": 300.0,
"2012": 50.3,
"2013": 380.4,
"2014": 150.7,
"2015": 90.0
};
My data:
var oData = {
"1220": 262,
"1120": 338,
"1020": 244,
"0920": 314,
"0820": 311,
"0720": 302,
"0620": 300,
"0520": 269,
"0420": 232,
"0320": 347
};
var label = document.querySelector(".label");
var c = document.getElementById("c");
var ctx = c.getContext("2d");
var cw = c.width = 700;
var ch = c.height = 350;
var cx = cw / 2,
cy = ch / 2;
var rad = Math.PI / 180;
var frames = 0;
ctx.lineWidth = 1;
ctx.strokeStyle = "#999";
ctx.fillStyle = "#ccc";
ctx.font = "14px monospace";
var grd = ctx.createLinearGradient(0, 0, 0, cy);
grd.addColorStop(0, "hsla(167,72%,60%,1)");
grd.addColorStop(1, "hsla(167,72%,60%,0)");
var oData = {
"2008": 10,
"2009": 39.9,
"2010": 17,
"2011": 30.0,
"2012": 5.3,
"2013": 38.4,
"2014": 15.7,
"2015": 9.0
};
var valuesRy = [];
var propsRy = [];
for (var prop in oData) {
valuesRy.push(oData[prop]);
propsRy.push(prop);
}
var vData = 4;
var hData = valuesRy.length;
var offset = 50.5; //offset chart axis
var chartHeight = ch - 2 * offset;
var chartWidth = cw - 2 * offset;
var t = 1 / 7; // curvature : 0 = no curvature
var speed = 2; // for the animation
var A = {
x: offset,
y: offset
}
var B = {
x: offset,
y: offset + chartHeight
}
var C = {
x: offset + chartWidth,
y: offset + chartHeight
}
/*
A ^
| |
+ 25
|
|
|
+ 25
|__|_________________________________ C
B
*/
// CHART AXIS -------------------------
ctx.beginPath();
ctx.moveTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
ctx.lineTo(C.x, C.y);
ctx.stroke();
// vertical ( A - B )
var aStep = (chartHeight - 50) / (vData);
var Max = Math.ceil(arrayMax(valuesRy) / 10) * 10;
var Min = Math.floor(arrayMin(valuesRy) / 10) * 10;
var aStepValue = (Max - Min) / (vData);
console.log("aStepValue: " + aStepValue); //8 units
var verticalUnit = aStep / aStepValue;
var a = [];
ctx.textAlign = "right";
ctx.textBaseline = "middle";
for (var i = 0; i <= vData; i++) {
if (i == 0) {
a[i] = {
x: A.x,
y: A.y + 25,
val: Max
}
} else {
a[i] = {}
a[i].x = a[i - 1].x;
a[i].y = a[i - 1].y + aStep;
a[i].val = a[i - 1].val - aStepValue;
}
drawCoords(a[i], 3, 0);
}
//horizontal ( B - C )
var b = [];
ctx.textAlign = "center";
ctx.textBaseline = "hanging";
var bStep = chartWidth / (hData + 1);
for (var i = 0; i < hData; i++) {
if (i == 0) {
b[i] = {
x: B.x + bStep,
y: B.y,
val: propsRy[0]
};
} else {
b[i] = {}
b[i].x = b[i - 1].x + bStep;
b[i].y = b[i - 1].y;
b[i].val = propsRy[i]
}
drawCoords(b[i], 0, 3)
}
function drawCoords(o, offX, offY) {
ctx.beginPath();
ctx.moveTo(o.x - offX, o.y - offY);
ctx.lineTo(o.x + offX, o.y + offY);
ctx.stroke();
ctx.fillText(o.val, o.x - 2 * offX, o.y + 2 * offY);
}
//----------------------------------------------------------
// DATA
var oDots = [];
var oFlat = [];
var i = 0;
for (var prop in oData) {
oDots[i] = {}
oFlat[i] = {}
oDots[i].x = b[i].x;
oFlat[i].x = b[i].x;
oDots[i].y = b[i].y - oData[prop] * verticalUnit - 25;
oFlat[i].y = b[i].y - 25;
oDots[i].val = oData[b[i].val];
i++
}
///// Animation Chart ///////////////////////////
//var speed = 3;
function animateChart() {
requestId = window.requestAnimationFrame(animateChart);
frames += speed; //console.log(frames)
ctx.clearRect(60, 0, cw, ch - 60);
for (var i = 0; i < oFlat.length; i++) {
if (oFlat[i].y > oDots[i].y) {
oFlat[i].y -= speed;
}
}
drawCurve(oFlat);
for (var i = 0; i < oFlat.length; i++) {
ctx.fillText(oDots[i].val, oFlat[i].x, oFlat[i].y - 25);
ctx.beginPath();
ctx.arc(oFlat[i].x, oFlat[i].y, 3, 0, 2 * Math.PI);
ctx.fill();
}
if (frames >= Max * verticalUnit) {
window.cancelAnimationFrame(requestId);
}
}
requestId = window.requestAnimationFrame(animateChart);
/////// EVENTS //////////////////////
c.addEventListener("mousemove", function(e) {
label.innerHTML = "";
label.style.display = "none";
this.style.cursor = "default";
var m = oMousePos(this, e);
for (var i = 0; i < oDots.length; i++) {
output(m, i);
}
}, false);
function output(m, i) {
ctx.beginPath();
ctx.arc(oDots[i].x, oDots[i].y, 20, 0, 2 * Math.PI);
if (ctx.isPointInPath(m.x, m.y)) {
//console.log(i);
label.style.display = "block";
label.style.top = (m.y + 10) + "px";
label.style.left = (m.x + 10) + "px";
label.innerHTML = "<strong>" + propsRy[i] + "</strong>: " + valuesRy[i] + "%";
c.style.cursor = "pointer";
}
}
// CURVATURE
function controlPoints(p) {
// given the points array p calculate the control points
var pc = [];
for (var i = 1; i < p.length - 1; i++) {
var dx = p[i - 1].x - p[i + 1].x; // difference x
var dy = p[i - 1].y - p[i + 1].y; // difference y
// the first control point
var x1 = p[i].x - dx * t;
var y1 = p[i].y - dy * t;
var o1 = {
x: x1,
y: y1
};
// the second control point
var x2 = p[i].x + dx * t;
var y2 = p[i].y + dy * t;
var o2 = {
x: x2,
y: y2
};
// building the control points array
pc[i] = [];
pc[i].push(o1);
pc[i].push(o2);
}
return pc;
}
function drawCurve(p) {
var pc = controlPoints(p); // the control points array
ctx.beginPath();
//ctx.moveTo(p[0].x, B.y- 25);
ctx.lineTo(p[0].x, p[0].y);
// the first & the last curve are quadratic Bezier
// because I'm using push(), pc[i][1] comes before pc[i][0]
ctx.quadraticCurveTo(pc[1][1].x, pc[1][1].y, p[1].x, p[1].y);
if (p.length > 2) {
// central curves are cubic Bezier
for (var i = 1; i < p.length - 2; i++) {
ctx.bezierCurveTo(pc[i][0].x, pc[i][0].y, pc[i + 1][1].x, pc[i + 1][1].y, p[i + 1].x, p[i + 1].y);
}
// the first & the last curve are quadratic Bezier
var n = p.length - 1;
ctx.quadraticCurveTo(pc[n - 1][0].x, pc[n - 1][0].y, p[n].x, p[n].y);
}
//ctx.lineTo(p[p.length-1].x, B.y- 25);
ctx.stroke();
ctx.save();
ctx.fillStyle = grd;
ctx.fill();
ctx.restore();
}
function arrayMax(array) {
return Math.max.apply(Math, array);
};
function arrayMin(array) {
return Math.min.apply(Math, array);
};
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
body {
margin: 0;
overflow: hidden;
background: #152B39;
font-family: Courier, monospace;
font-size: 14px;
color:#ccc;
}
.wrapper {
display: block;
margin: 5em auto;
border: 1px solid #555;
width: 700px;
height: 350px;
position: relative;
}
p{text-align:center;}
.label {
height: 1em;
padding: .3em;
background: rgba(255, 255, 255, .8);
position: absolute;
display: none;
color:#333;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<canvas id='c'></canvas>
<div class="label">text</div>
</div>
<p>Please mouse over the dots</p>
var label = document.querySelector(".label");
var c = document.getElementById("c");
var ctx = c.getContext("2d");
var cw = c.width = 700;
var ch = c.height = 350;
var cx = cw / 2,
cy = ch / 2;
var rad = Math.PI / 180;
var frames = 0;
ctx.lineWidth = 1;
ctx.strokeStyle = "#999";
ctx.fillStyle = "#ccc";
ctx.font = "14px monospace";
var grd = ctx.createLinearGradient(0, 0, 0, cy);
grd.addColorStop(0, "hsla(167,72%,60%,1)");
grd.addColorStop(1, "hsla(167,72%,60%,0)");
var oData = {
"2008": 100,
"2009": 390.9,
"2010": 170,
"2011": 300.0,
"2012": 50.3,
"2013": 380.4,
"2014": 150.7,
"2015": 90.0
};
var valuesRy = [];
var propsRy = [];
for (var prop in oData) {
valuesRy.push(oData[prop]);
propsRy.push(prop);
}
var vData = 4;
var hData = valuesRy.length;
var offset = 50.5; //offset chart axis
var chartHeight = ch - 2 * offset;
var chartWidth = cw - 2 * offset;
var t = 1 / 7; // curvature : 0 = no curvature
var speed = 2; // for the animation
var A = {
x: offset,
y: offset
}
var B = {
x: offset,
y: offset + chartHeight
}
var C = {
x: offset + chartWidth,
y: offset + chartHeight
}
/*
A ^
| |
+ 25
|
|
|
+ 25
|__|_________________________________ C
B
*/
// CHART AXIS -------------------------
ctx.beginPath();
ctx.moveTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
ctx.lineTo(C.x, C.y);
ctx.stroke();
// vertical ( A - B )
var aStep = (chartHeight - 50) / (vData);
var Max = Math.ceil(arrayMax(valuesRy) / 10) * 10;
var Min = Math.floor(arrayMin(valuesRy) / 10) * 10;
var aStepValue = (Max - Min) / (vData);
console.log("aStepValue: " + aStepValue); //8 units
var verticalUnit = aStep / aStepValue;
var a = [];
ctx.textAlign = "right";
ctx.textBaseline = "middle";
for (var i = 0; i <= vData; i++) {
if (i == 0) {
a[i] = {
x: A.x,
y: A.y + 25,
val: Max
}
} else {
a[i] = {}
a[i].x = a[i - 1].x;
a[i].y = a[i - 1].y + aStep;
a[i].val = a[i - 1].val - aStepValue;
}
drawCoords(a[i], 3, 0);
}
//horizontal ( B - C )
var b = [];
ctx.textAlign = "center";
ctx.textBaseline = "hanging";
var bStep = chartWidth / (hData + 1);
for (var i = 0; i < hData; i++) {
if (i == 0) {
b[i] = {
x: B.x + bStep,
y: B.y,
val: propsRy[0]
};
} else {
b[i] = {}
b[i].x = b[i - 1].x + bStep;
b[i].y = b[i - 1].y;
b[i].val = propsRy[i]
}
drawCoords(b[i], 0, 3)
}
function drawCoords(o, offX, offY) {
ctx.beginPath();
ctx.moveTo(o.x - offX, o.y - offY);
ctx.lineTo(o.x + offX, o.y + offY);
ctx.stroke();
ctx.fillText(o.val, o.x - 2 * offX, o.y + 2 * offY);
}
//----------------------------------------------------------
// DATA
var oDots = [];
var oFlat = [];
var i = 0;
for (var prop in oData) {
oDots[i] = {}
oFlat[i] = {}
oDots[i].x = b[i].x;
oFlat[i].x = b[i].x;
oDots[i].y = b[i].y - oData[prop] * verticalUnit - 25;
oFlat[i].y = b[i].y - 25;
oDots[i].val = oData[b[i].val];
i++
}
///// Animation Chart ///////////////////////////
//var speed = 3;
function animateChart() {
requestId = window.requestAnimationFrame(animateChart);
frames += speed; //console.log(frames)
ctx.clearRect(60, 0, cw, ch - 60);
for (var i = 0; i < oFlat.length; i++) {
if (oFlat[i].y > oDots[i].y) {
oFlat[i].y -= speed;
}
}
drawCurve(oFlat);
for (var i = 0; i < oFlat.length; i++) {
ctx.fillText(oDots[i].val, oFlat[i].x, oFlat[i].y - 25);
ctx.beginPath();
ctx.arc(oFlat[i].x, oFlat[i].y, 3, 0, 2 * Math.PI);
ctx.fill();
}
if (frames >= Max * verticalUnit) {
window.cancelAnimationFrame(requestId);
}
}
requestId = window.requestAnimationFrame(animateChart);
/////// EVENTS //////////////////////
c.addEventListener("mousemove", function(e) {
label.innerHTML = "";
label.style.display = "none";
this.style.cursor = "default";
var m = oMousePos(this, e);
for (var i = 0; i < oDots.length; i++) {
output(m, i);
}
}, false);
function output(m, i) {
ctx.beginPath();
ctx.arc(oDots[i].x, oDots[i].y, 20, 0, 2 * Math.PI);
if (ctx.isPointInPath(m.x, m.y)) {
//console.log(i);
label.style.display = "block";
label.style.top = (m.y + 10) + "px";
label.style.left = (m.x + 10) + "px";
label.innerHTML = "<strong>" + propsRy[i] + "</strong>: " + valuesRy[i] + "%";
c.style.cursor = "pointer";
}
}
// CURVATURE
function controlPoints(p) {
// given the points array p calculate the control points
var pc = [];
for (var i = 1; i < p.length - 1; i++) {
var dx = p[i - 1].x - p[i + 1].x; // difference x
var dy = p[i - 1].y - p[i + 1].y; // difference y
// the first control point
var x1 = p[i].x - dx * t;
var y1 = p[i].y - dy * t;
var o1 = {
x: x1,
y: y1
};
// the second control point
var x2 = p[i].x + dx * t;
var y2 = p[i].y + dy * t;
var o2 = {
x: x2,
y: y2
};
// building the control points array
pc[i] = [];
pc[i].push(o1);
pc[i].push(o2);
}
return pc;
}
function drawCurve(p) {
var pc = controlPoints(p); // the control points array
ctx.beginPath();
//ctx.moveTo(p[0].x, B.y- 25);
ctx.lineTo(p[0].x, p[0].y);
// the first & the last curve are quadratic Bezier
// because I'm using push(), pc[i][1] comes before pc[i][0]
ctx.quadraticCurveTo(pc[1][1].x, pc[1][1].y, p[1].x, p[1].y);
if (p.length > 2) {
// central curves are cubic Bezier
for (var i = 1; i < p.length - 2; i++) {
ctx.bezierCurveTo(pc[i][0].x, pc[i][0].y, pc[i + 1][1].x, pc[i + 1][1].y, p[i + 1].x, p[i + 1].y);
}
// the first & the last curve are quadratic Bezier
var n = p.length - 1;
ctx.quadraticCurveTo(pc[n - 1][0].x, pc[n - 1][0].y, p[n].x, p[n].y);
}
//ctx.lineTo(p[p.length-1].x, B.y- 25);
ctx.stroke();
ctx.save();
ctx.fillStyle = grd;
ctx.fill();
ctx.restore();
}
function arrayMax(array) {
return Math.max.apply(Math, array);
};
function arrayMin(array) {
return Math.min.apply(Math, array);
};
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
body {
margin: 0;
overflow: hidden;
background: #152B39;
font-family: Courier, monospace;
font-size: 14px;
color:#ccc;
}
.wrapper {
display: block;
margin: 5em auto;
border: 1px solid #555;
width: 700px;
height: 350px;
position: relative;
}
p{text-align:center;}
.label {
height: 1em;
padding: .3em;
background: rgba(255, 255, 255, .8);
position: absolute;
display: none;
color:#333;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<canvas id='c'></canvas>
<div class="label">text</div>
</div>
<p>Please mouse over the dots</p>
var label = document.querySelector(".label");
var c = document.getElementById("c");
var ctx = c.getContext("2d");
var cw = c.width = 700;
var ch = c.height = 350;
var cx = cw / 2,
cy = ch / 2;
var rad = Math.PI / 180;
var frames = 0;
ctx.lineWidth = 1;
ctx.strokeStyle = "#999";
ctx.fillStyle = "#ccc";
ctx.font = "14px monospace";
var grd = ctx.createLinearGradient(0, 0, 0, cy);
grd.addColorStop(0, "hsla(167,72%,60%,1)");
grd.addColorStop(1, "hsla(167,72%,60%,0)");
var oData = {
"1220": 262,
"1120": 338,
"1020": 244,
"0920": 314,
"0820": 311,
"0720": 302,
"0620": 300,
"0520": 269,
"0420": 232,
"0320": 347
};
var valuesRy = [];
var propsRy = [];
for (var prop in oData) {
valuesRy.push(oData[prop]);
propsRy.push(prop);
}
var vData = 4;
var hData = valuesRy.length;
var offset = 50.5; //offset chart axis
var chartHeight = ch - 2 * offset;
var chartWidth = cw - 2 * offset;
var t = 1 / 7; // curvature : 0 = no curvature
var speed = 2; // for the animation
var A = {
x: offset,
y: offset
}
var B = {
x: offset,
y: offset + chartHeight
}
var C = {
x: offset + chartWidth,
y: offset + chartHeight
}
/*
A ^
| |
+ 25
|
|
|
+ 25
|__|_________________________________ C
B
*/
// CHART AXIS -------------------------
ctx.beginPath();
ctx.moveTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
ctx.lineTo(C.x, C.y);
ctx.stroke();
// vertical ( A - B )
var aStep = (chartHeight - 50) / (vData);
var Max = Math.ceil(arrayMax(valuesRy) / 10) * 10;
var Min = Math.floor(arrayMin(valuesRy) / 10) * 10;
var aStepValue = (Max - Min) / (vData);
console.log("aStepValue: " + aStepValue); //8 units
var verticalUnit = aStep / aStepValue;
var a = [];
ctx.textAlign = "right";
ctx.textBaseline = "middle";
for (var i = 0; i <= vData; i++) {
if (i == 0) {
a[i] = {
x: A.x,
y: A.y + 25,
val: Max
}
} else {
a[i] = {}
a[i].x = a[i - 1].x;
a[i].y = a[i - 1].y + aStep;
a[i].val = a[i - 1].val - aStepValue;
}
drawCoords(a[i], 3, 0);
}
//horizontal ( B - C )
var b = [];
ctx.textAlign = "center";
ctx.textBaseline = "hanging";
var bStep = chartWidth / (hData + 1);
for (var i = 0; i < hData; i++) {
if (i == 0) {
b[i] = {
x: B.x + bStep,
y: B.y,
val: propsRy[0]
};
} else {
b[i] = {}
b[i].x = b[i - 1].x + bStep;
b[i].y = b[i - 1].y;
b[i].val = propsRy[i]
}
drawCoords(b[i], 0, 3)
}
function drawCoords(o, offX, offY) {
ctx.beginPath();
ctx.moveTo(o.x - offX, o.y - offY);
ctx.lineTo(o.x + offX, o.y + offY);
ctx.stroke();
ctx.fillText(o.val, o.x - 2 * offX, o.y + 2 * offY);
}
//----------------------------------------------------------
// DATA
var oDots = [];
var oFlat = [];
var i = 0;
for (var prop in oData) {
oDots[i] = {}
oFlat[i] = {}
oDots[i].x = b[i].x;
oFlat[i].x = b[i].x;
oDots[i].y = b[i].y - oData[prop] * verticalUnit - 25;
oFlat[i].y = b[i].y - 25;
oDots[i].val = oData[b[i].val];
i++
}
///// Animation Chart ///////////////////////////
//var speed = 3;
function animateChart() {
requestId = window.requestAnimationFrame(animateChart);
frames += speed; //console.log(frames)
ctx.clearRect(60, 0, cw, ch - 60);
for (var i = 0; i < oFlat.length; i++) {
if (oFlat[i].y > oDots[i].y) {
oFlat[i].y -= speed;
}
}
drawCurve(oFlat);
for (var i = 0; i < oFlat.length; i++) {
ctx.fillText(oDots[i].val, oFlat[i].x, oFlat[i].y - 25);
ctx.beginPath();
ctx.arc(oFlat[i].x, oFlat[i].y, 3, 0, 2 * Math.PI);
ctx.fill();
}
if (frames >= Max * verticalUnit) {
window.cancelAnimationFrame(requestId);
}
}
requestId = window.requestAnimationFrame(animateChart);
/////// EVENTS //////////////////////
c.addEventListener("mousemove", function(e) {
label.innerHTML = "";
label.style.display = "none";
this.style.cursor = "default";
var m = oMousePos(this, e);
for (var i = 0; i < oDots.length; i++) {
output(m, i);
}
}, false);
function output(m, i) {
ctx.beginPath();
ctx.arc(oDots[i].x, oDots[i].y, 20, 0, 2 * Math.PI);
if (ctx.isPointInPath(m.x, m.y)) {
//console.log(i);
label.style.display = "block";
label.style.top = (m.y + 10) + "px";
label.style.left = (m.x + 10) + "px";
label.innerHTML = "<strong>" + propsRy[i] + "</strong>: " + valuesRy[i] + "%";
c.style.cursor = "pointer";
}
}
// CURVATURE
function controlPoints(p) {
// given the points array p calculate the control points
var pc = [];
for (var i = 1; i < p.length - 1; i++) {
var dx = p[i - 1].x - p[i + 1].x; // difference x
var dy = p[i - 1].y - p[i + 1].y; // difference y
// the first control point
var x1 = p[i].x - dx * t;
var y1 = p[i].y - dy * t;
var o1 = {
x: x1,
y: y1
};
// the second control point
var x2 = p[i].x + dx * t;
var y2 = p[i].y + dy * t;
var o2 = {
x: x2,
y: y2
};
// building the control points array
pc[i] = [];
pc[i].push(o1);
pc[i].push(o2);
}
return pc;
}
function drawCurve(p) {
var pc = controlPoints(p); // the control points array
ctx.beginPath();
//ctx.moveTo(p[0].x, B.y- 25);
ctx.lineTo(p[0].x, p[0].y);
// the first & the last curve are quadratic Bezier
// because I'm using push(), pc[i][1] comes before pc[i][0]
ctx.quadraticCurveTo(pc[1][1].x, pc[1][1].y, p[1].x, p[1].y);
if (p.length > 2) {
// central curves are cubic Bezier
for (var i = 1; i < p.length - 2; i++) {
ctx.bezierCurveTo(pc[i][0].x, pc[i][0].y, pc[i + 1][1].x, pc[i + 1][1].y, p[i + 1].x, p[i + 1].y);
}
// the first & the last curve are quadratic Bezier
var n = p.length - 1;
ctx.quadraticCurveTo(pc[n - 1][0].x, pc[n - 1][0].y, p[n].x, p[n].y);
}
//ctx.lineTo(p[p.length-1].x, B.y- 25);
ctx.stroke();
ctx.save();
ctx.fillStyle = grd;
ctx.fill();
ctx.restore();
}
function arrayMax(array) {
return Math.max.apply(Math, array);
};
function arrayMin(array) {
return Math.min.apply(Math, array);
};
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
body {
margin: 0;
overflow: hidden;
background: #152B39;
font-family: Courier, monospace;
font-size: 14px;
color:#ccc;
}
.wrapper {
display: block;
margin: 5em auto;
border: 1px solid #555;
width: 700px;
height: 350px;
position: relative;
}
p{text-align:center;}
.label {
height: 1em;
padding: .3em;
background: rgba(255, 255, 255, .8);
position: absolute;
display: none;
color:#333;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<canvas id='c'></canvas>
<div class="label">text</div>
</div>
<p>Please mouse over the dots</p>

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>

Categories