SVG - moving a dot along a fixed circle following the mouse position - javascript

I'm trying to animate a point to follow the potion of the mouse.
It's like an eye looking at the arrow while I'm not on the eye.
The point should move along a circle if I mouve around the mouse.
If the mouse is on the eye, the eye should follow the arrow.
That's what I'm currently trying to do.
I use snap.svg library.
I currently have a point following the mobility of the mouse but I cant make it stay in a circle.
It looks like this so far :
var s = Snap(400,400);
var c1 = s.circle(0,0,10).attr({ fill: "red" });
function OnMouseMove(evt) {
c1.attr({ cx: evt.clientX , cy: evt.clientY });
}
document.onmousemove = OnMouseMove;
Any idea community ?

Here's my visual solution, it uses Snap's built in functions:-
var s = Snap(400,400);
var circleX = 150, circleY = 150, circleRadius = 100;
var bigCircle = s.circle(circleX, circleY, circleRadius);
var L1 = s.path("M "+circleX+" "+circleY +"L 0 0").attr({stroke: "blue"});
// BigCircle default its black, lets change its attributes
bigCircle.attr({
fill: "#bada55",
stroke: "#000",
strokeWidth: 5
});
var c1 = s.circle(0,0,10).attr({ fill: "red" });
function OnMouseMove(evt) {
L1.attr({ d: "M "+circleX+" "+circleY +"L "+evt.clientX+" "+evt.clientY });
var totalLength = L1.getTotalLength();
if (totalLength < circleRadius) {
c1.attr({ cx: evt.clientX , cy: evt.clientY });
} else {
var PAL = L1.getPointAtLength(circleRadius);
c1.attr({ cx: PAL.x , cy: PAL.y });
}
}
document.onmousemove = OnMouseMove;
Update: Here's the fiddle demo. Challenge for readers: Try var bigCircle = s.ellipse(150, 150, 100, 50);.

You have to test how far away your mouse coordinates are from the centre of the circle, and stop them if they reach the edge.
Something like this should work.
function OnMouseMove(evt) {
// Get the mouse position relative to the centre of the circle (circleX,circleY)
var dx = evt.clientX - circleX;
var dy = evt.clientY - circleY;
// Calculate distance from centre of circle to mouse (Pythagoras' theorem)
var distance = Math.sqrt(dx * dx + dy *dy);
// Test against radius
if (distance > circleRadius) {
// Scale the dx,dy coords back so they are on the circumference
dx = dx * circleRadius / distance;
dy = dy * circleRadius / distance;
}
c1.attr({ cx: dx, cy: dy });
}
If this doesn't work for you, make a jsfiddle so we can see what you have so far.

Just strayling slightly, but as an extension to Alvin Ks answer (for the readers challenge!), if you can make sure the the object is a path, you could use Snap.path.intersection which would work for many other shapes. It would need an extra bit of code for multiple intersections though possibly.
Relevant amendment to Alvins code...
function OnMouseMove(evt) {
L1.attr({ d: "M "+circleX+" "+circleY +"L "+evt.clientX+" "+evt.clientY });
var intersect = Snap.path.intersection( path, L1 )
if (intersect.length == 0) {
c1.attr({ cx: evt.clientX , cy: evt.clientY });
} else {
c1.attr({ cx: intersect[0].x , cy: intersect[0].y });
}
}
jsfiddle

Related

Fabric.js moving the points of a polygon shape fails when flipped

Wasted weeks - Need solution to edit Polygons using either Fabric.js and Konva.js - Both have no way to actually update the poly points and transformer when the poly or it's points are MOVED, FLIPPED or MIRRORED. I'll assume the array points need to be reversed and the end the starting index switch depending on the quadrant the poly has been flipped.
If anyone have a solution please post. Fabric.js code in CodePen: https://codepen.io/Rstewart/pen/LYbJwQE
/* CODE FROM POLY DEMO ON FABRIC WEBSITE - CODE FAILS WHEN FLIPPED OR MIRRORED */
function polygonPositionHandler(dim, finalMatrix, fabricObject) {
var x = (fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x),
y = (fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y);
return fabric.util.transformPoint( { x: x, y: y },
fabric.util.multiplyTransformMatrices(
fabricObject.canvas.viewportTransform,
fabricObject.calcTransformMatrix()
)
);
}
function actionHandler(eventData, transform, x, y) {
var polygon = transform.target, currentControl = polygon.controls[polygon.__corner],
mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center'),
polygonBaseSize = polygon._getNonTransformedDimensions(), size = polygon._getTransformedDimensions(0, 0),
finalPointPosition = {
x: mouseLocalPosition.x * polygonBaseSize.x / size.x + polygon.pathOffset.x,
y: mouseLocalPosition.y * polygonBaseSize.y / size.y + polygon.pathOffset.y
};
polygon.points[currentControl.pointIndex] = finalPointPosition; return true;
}
function anchorWrapper(anchorIndex, fn) {
return function(eventData, transform, x, y) {
var fabricObject = transform.target,
absolutePoint = fabric.util.transformPoint({
x: (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x),
y: (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y),
}, fabricObject.calcTransformMatrix()),
actionPerformed = fn(eventData, transform, x, y),
newDim = fabricObject._setPositionDimensions({}),
polygonBaseSize = fabricObject._getNonTransformedDimensions(),
newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x,
newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
return actionPerformed;
}
}

p5.js How would I prevent a small circle from crossing a diameter of a bigger circle

Im working of a program that has a circle moving around inside another bigger circle.
var offX;
function setup() {
createCanvas(400, 400);
offX = 0.00;
}
function draw() {
background(220);
var bigBoy = {
x: 200,
y: 200,
r: 150,
};
var smallBoy = {
x: noise(offX) * 400,
y: map(sin(offX),-1,1,10,380),
r: 10,
};
offX += 0.005;
ellipse(bigBoy.x,bigBoy.y,bigBoy.r * 2);
ellipse(smallBoy.x,smallBoy.y,smallBoy.r * 2);
var d = dist(bigBoy.x,bigBoy.y,smallBoy.x,smallBoy.y);
text(round(d,0),30,20);
//-------------------------------//
if(d > bigBoy.r - smallBoy.r){
//move away from / dont cross over bigBoy's diameter//
} else{
//crack on mate//
}
}
could anyone advise me how I would keep the smaller circle within the bigger circles diameter. I would need it to look natural, like the small circle has a force preventing it from leaving the bigger one?
Thanks in advance!
P

find position of a point with origin, angle and radius

im stuck with a trigonometry problem in a javascript game im trying to make.
with a origin point(xa,ya) a radius and destination point (ya,yb) I need to find the position of a new point.
//calculate a angle in degree
function angle(xa, ya, xb, yb)
{
var a= Math.atan2(yb - ya, xb - xa);
a*= 180 / Math.PI;
return a;
}
function FindNewPointPosition()
{
//radius origine(xa,xb) destination(ya,yb)
var radius=30;
var a = angle(xa, xb, ya, yb);
newpoint.x = xa + radius * Math.cos(a);
newpoint.y = ya + radius * Math.sin(a);
return newpoint;
}
Imagine a image because I dont have enough reputation to post one :
blue square is the map (5000x5000), black square (500x500) what players see (hud).
Cross(400,400) is the origin and sun(4200,4200) the destination.
The red dot (?,?) indicate to player which direction take to find the sun ..
But sun and cross position can be reverse or in different corner or anywhere !
At the moment the red dot do not do that at all ..
Tks for your help.
Why did you use ATAN2? Change to Math.atan() - you will get angle in var A
Where you have to place your red dot? inside hud?
Corrected code
https://jsfiddle.net/ka9xr07j/embedded/result/
var obj = FindNewPointPosition(400,4200,400,4200); - new position 417. 425
Finally I find a solution without using angle.
function newpointposition(origin, destination)
{
// radius distance between cross and red dot
var r=30;
// calculate a vector
var xDistance = destination.x - origin.x;
var yDistance = destination.y - origin.y;
// normalize vector
var length = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
xDistance /= length;
yDistance /= length;
// add the radius
xDistance = xDistance * r;
yDistance = yDistance * r;
var newpoint = { x: 0, y: 0 };
newpoint.x = origin.x + xDistance;
newpoint.y = origin.y + yDistance;
return newpoint;
}
var radar = newpointposition({
x: 500,
y: 800
}, {
x: 3600,
y: 2850
});
alert(radar.x + ' ' + radar.y);
ty Trike, using jsfiddle really help me.

How to keep previous offset in rotate animation after dragging a same object with Kinetic?

could any one show my how to drag my object but still keep its previous offset in rotate animation. I try so many times but still not find the solution. When i drag an object, it didn't rotate in orbit :( .
Here is my code
HTML
<!DOCTYPE HTML>
<html>
<body>
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.2.min.js"></script>
</body>
</html>
CSS
body {
margin: 0px;
padding: 0px;
}
canvas {
border: 1px solid #9C9898;
}
JS
window.onload = function() {
var stage = new Kinetic.Stage({
container: 'container',
width: 500,
height: 500
});
var layer = new Kinetic.Layer(),
cx = stage.getWidth() / 2,
cy = stage.getWidth() / 2;
var bigCircle = new Kinetic.Circle({
x: cx ,
y: cy,
radius: 200,
stroke: 'lightgray',
strokeWidth: 8,
});
/*
* move center point outside of the rectangle
* with offset
*/
var smallCirlce = new Kinetic.Circle({
x: cx,
y: cy,
radius: 20,
fill:'rgb(0,102,204)',
strokeWidth: 4,
offset: {x:getRandom(-150,150), y:getRandom(-150,150)}
});
function getRandom(minNum, maxNum){
return Math.random() * (maxNum - minNum) + minNum;
};
layer.add(bigCircle);
layer.add(smallCirlce);
stage.add(layer);
// one revolution per 4 seconds
var angularSpeed = 360 / 4;
var anim = new Kinetic.Animation(function(frame) {
var angleDiff = frame.timeDiff * angularSpeed / 1000;
smallCirlce.rotate(angleDiff);
}, layer);
anim.start();
};
Not sure if this is what you are after. What I took from your description was that you want to be able to drag the blue ball and have it continue orbiting around its initial point but at a greater radius.
Firstly, here is a fiddle:
http://jsfiddle.net/Paul_Smith/206meq0a/
What this solution does is on mousedown, convert the circle's position, offset and rotation into a single coordinate representing the actual position of the circle in space.
It then allows you to drag the circle, and resets its offset to the new one by subtracting its actual position from its original position.
Probably not the best solution out there but I am not a very math oriented programmer :)
The important functions are as below:
//Calculate an angle in radians given degrees
function degreesToRadians(degrees) {
return degrees * Math.PI / 180;
}
//Transform a coordinate by way of a rotation in degrees.
//Rotated about { x: 0, y: 0 }
function translateCoordinate(coordinate, degrees) {
var radians = degreesToRadians(degrees);
return {
x: (coordinate.x * Math.cos(radians)) - (coordinate.y * Math.sin(radians)),
y: (coordinate.x * Math.sin(radians)) + (coordinate.y * Math.cos(radians))
};
}
smallCircle.on("mousedown", function () {
var rotation = smallCircle.rotation(),
offset = smallCircle.offset(),
position = smallCircle.position(),
translatedOffset;
//Calculate how the offset has changed due to the rotation
translatedOffset = translateCoordinate(offset, rotation);
//Set the position to that of the current position - the offset
smallCircle.position({
x: position.x - translatedOffset.x,
y: position.y - translatedOffset.y
});
//set the offset to 0 as it has now been added to the position
smallCircle.offset({
x: 0,
y: 0
});
//Set the rotation to 0 as it is now part of the position due to the
//translation which has taken place
smallCircle.rotation(0);
//Reset the drag position
smallCircle.stopDrag();
smallCircle.startDrag();
});
smallCircle.on("mouseup", function () {
var position = smallCircle.position();
//Calculate the new offset (original position - new position)
smallCircle.offset({
x: cx - position.x,
y: cy - position.y
});
//Set the position back to the original position
smallCircle.position({
x: cx,
y: cy
});
});
Hope this helps

How to drag a shape along a given path

I've this simple dummy file that I'm using to do some testing. The intended result is to drag the red circle along the path. The thing is that I can't figure out how to associate both shapes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="raphael-min.js"></script>
</head>
<body>
<script type="text/javascript">
// Creates canvas 320 × 200 at 10, 50
var r = Raphael(10, 50, 320, 200);
var p = r.path("M100,100c0,50 100-50 100,0c0,50 -100-50 -100,0z").attr({stroke: "#ddd"}),
e = r.ellipse(104, 100, 4, 4).attr({stroke: "none", fill: "#f00"}),
/*var c = r.circle(100, 100, 50).attr({
fill: "hsb(.8, 1, 1)",
stroke: "none",
opacity: .5
});*/
var start = function () {
// storing original coordinates
this.ox = this.attr("cx");
this.oy = this.attr("cy");
this.attr({opacity: 1});
},
move = function (dx, dy) {
// move will be called with dx and dy
this.attr({cx: this.ox + dx, cy: this.oy + dy});
},
up = function () {
// restoring state
this.attr({opacity: 1});
};
e.drag(move, start, up);
</script>
</body>
</html>
You didn't specify exactly how you want the interaction to work, so I used what feels most natural to me.
We can assume the dot must remain on the path, so its position must be given by
p.getPointAtLength(l);
for some l. To find l we can search for the local minimum of the distance between the curve and the cursor position. We initialize the search with l0 where l0 is the value of l currently defining the location of the dot.
See the JSfiddle here for a working example:
http://jsfiddle.net/fuzic/kKLtH/
Here is the code:
var searchDl = 1;
var l = 0;
// Creates canvas 320 × 200 at 10, 50
var r = Raphael(10, 50, 320, 200);
var p = r.path("M100,100c0,50 100-50 100,0c0,50 -100-50 -100,0z").attr({stroke: "#ddd"}),
pt = p.getPointAtLength(l);
e = r.ellipse(pt.x, pt.y, 4, 4).attr({stroke: "none", fill: "#f00"}),
totLen = p.getTotalLength(),
start = function () {
// storing original coordinates
this.ox = this.attr("cx");
this.oy = this.attr("cy");
this.attr({opacity: 1});
},
move = function (dx, dy) {
var tmpPt = {
x : this.ox + dx,
y : this.oy + dy
};
l = gradSearch(l, tmpPt);
pt = p.getPointAtLength(l);
this.attr({cx: pt.x, cy: pt.y});
},
up = function () {
this.attr({opacity: 1});
},
gradSearch = function (l0, pt) {
l0 = l0 + totLen;
var l1 = l0,
dist0 = dist(p.getPointAtLength(l0 % totLen), pt),
dist1,
searchDir;
if (dist(p.getPointAtLength((l0 - searchDl) % totLen), pt) >
dist(p.getPointAtLength((l0 + searchDl) % totLen), pt)) {
searchDir = searchDl;
} else {
searchDir = -searchDl;
}
l1 += searchDir;
dist1 = dist(p.getPointAtLength(l1 % totLen), pt);
while (dist1 < dist0) {
dist0 = dist1;
l1 += searchDir;
dist1 = dist(p.getPointAtLength(l1 % totLen), pt);
}
l1 -= searchDir;
return (l1 % totLen);
},
dist = function (pt1, pt2) {
var dx = pt1.x - pt2.x;
var dy = pt1.y - pt2.y;
return Math.sqrt(dx * dx + dy * dy);
};
e.drag(move, start, up);​
A circle object has an x,y coordinate for its center, and a radius. To make sure the circle remains on the line, simply find the intersection of the center of the circle and the line itself.
To do this, you will need to store the start and end coordinates of your line. Then using the equation of a line: y = mx + b, you can find the slope and y-intercept. Once you have a function for the line, you can generate new coordinates for the circle by plugging in different values of x.
Also, by plugging in the x,y coordinates of the circle into your function, you can check to see if the circle is on the line.

Categories

Resources