Destroying Path clockwise in SVG - javascript

So the first function creates a circle and it works really good but I have a problem with destroyCircle function. Is there any way to make to destroy the circle clockwise?
Demo: http://jsfiddle.net/mkn9t627/
HTML Code:
<div style="width:160px;height:160px;">
<svg width="160" height="160" viewBox="0 0 160 160">
<path id="arc" transform="translate(80, 80) scale(0.85)" fill="rgba(0,0,0,0.73)" d="M 0, 0 V -80 A 0 0 1 1 1 -0.001 -80 Z"></path>
</svg>
</div>
Javascript Codes:
function destroyCircle(id) {
var circle = document.getElementById(id);
var angle = 270;
var radius = 80;
window.timerx = window.setInterval(
function () {
angle = angle + 3;
var radians = (angle / 180) * Math.PI;
var x = -Math.cos(radians) * radius;
var y = Math.sin(radians) * radius;
var e = circle.getAttribute("d");
var d = e + " L " + x + ", " + y;
if (angle > (270 + 356)) {
window.clearInterval(window.timerx);
}
circle.setAttribute("d", d);
}, 20);
}
function drawCircle(id) {
var circle = document.getElementById(id);
var angle = 90;
var radius = 80;
window.timer = window.setInterval(
function () {
angle = angle + 3;
var radians = (angle / 180) * Math.PI;
var x = -Math.cos(radians) * radius;
var y = -Math.sin(radians) * radius;
var e = circle.getAttribute("d");
var d = e + " L " + x + " " + y;
circle.setAttribute("d", d);
if (angle > 449) {
window.clearInterval(window.timer);
destroyCircle("arc"); // DESTROY THE CIRCLE
}
}, 10);
}
// draw the circle
drawCircle("arc");

I think this is the easiest way:
You need to remove all fragments added in your drawCircle function. To do that you could transform string into array and simple use shift or pop (pop will do it anti-clockwise).
function destroyCircle(id) {
var circle = document.getElementById(id);
var angle = 0;
var radius = 80;
var e = circle.getAttribute("d");
var x = e.split('Z');
var y = x[1].split('L');
window.timerx = window.setInterval(function () {
y.shift()
var d = x[0] + 'Z L' + y.join('L');
if (!y.length) {
d = x[0] + 'Z';
clearTimeout(window.timerx)
}
circle.setAttribute("d", d);
}, 10);
}
I've updated your jsFiddle: http://jsfiddle.net/mkn9t627/1/

Related

Uncaught TypeError: (((item.value + 1) / total) * 100).floor is not a function

This code is running good here: https://jsfiddle.net/petersirka/fycx4kr1/
but not on other code editor like codepen or vscode getting error on line 33
//HTML
<svg id="svg" width="600" height="600"></svg>
//Javascript
function Donut(cx, cy, radius, data) {
function arcradius(cx, cy, radius, degrees) {`enter code here`
var radians = (degrees - 90) * Math.PI / 180.0;
return {
x: cx + (radius * Math.cos(radians)),
y: cy + (radius * Math.sin(radians))
};
}
var decimals = 4;
var total = 0;
var arr = [];
var beg = 0;
var end = 0;
var count = 0;
var half = radius / 2;
var midpoint = radius / 2.4;
for (var i = 0; i < data.length; i++)
total += data[i].value;
for (var i = 0; i < data.length; i++) {
var item = data[i];
var tmp = {};
var p = (((item.value + 1) / total) * 100).floor(2);
count += p;
if (i === length - 1 && count < 100)
p = p + (100 - count);
end = beg + ((360 / 100) * p);
tmp.index = i;
tmp.value = item.value;
tmp.data = item;
var b = arcradius(cx, cy, radius, end);
var e = arcradius(cx, cy, radius, beg);
var la = (end - beg) <= 180 ? 0 : 1;
tmp.d = ['M', b.x.floor(decimals), b.y.floor(decimals), 'A', radius, radius, 0, la, 0, e.x.floor(decimals), e.y.floor(decimals)].join(' ');
arr.push(tmp);
beg = end;
}
return arr;
}
// USAGE
(function(svg) {
var data = [{
value: 45
}, {
value: 25
}, {
value: 30
}];
var centerX = 300;
var centerY = 300;
var radius = 250;
var color = ['#7400b8', '#ff006e', '#3a86ff'];
var arr = Donut(centerX, centerY, radius, data);
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
svg.asvg('<g><path d="{0}" stroke="{1}" fill="none" stroke-width="50" /></g>'.format(item.d, color[i]));
}
})(document.getElementById('#svg'));
I know Math.Floor is a function because I've used it before and I looked it up. Did I mess something up in the code? Thanks.
Sorry if this question is a little vague, I'm still learning (as you can see :p).
If you want to convert the line
var p = (((item.value + 1) / total) * 100).floor(2);
into an integer with the floor function, you must write it like this:
var p = Math.floor(((item.value + 1) / total) * 100);
So the term you want to apply the Math.floor() function is inside the brackets.
I don't know what that (2) at the end means though...

change color of each line in a vertex

I'm drawing each point of a vector as a vertex in P5.js.
I would like to change the color of each vertex in a loop separately like a rainbow. The thing is that it changes the color of all the vertex and I don't know why.
var x = 0.01
var y = 0
var z = 0
var sigma = 10
var prandtl = 28
var rayleigh = 8 / 3
var dt = 0.01
var positionPoint = []
var colorChange = 0;
function setup() {
createCanvas(800, 800, WEBGL)
colorMode(HSB)
}
function draw() {
background("grey")
formula()
noFill()
scale(5)
strokeWeight(3)
beginShape()
rotateZ(frameCount * 0.01);
rotateY(frameCount * 0.01);
for (var i = 0; i < positionPoint.length; i++) {
stroke(colorChange, 255, 255)
vertex(positionPoint[i].x, positionPoint[i].y, positionPoint[i].z)
colorChange += 0.001
if (colorChange > 255) {
colorChange = 0
}
}
endShape()
}
const formula = () => {
var dx = (sigma * (y - x)) * dt
var dy = (x * (prandtl - z) - y) * dt
var dz = (x * y - rayleigh * z) * dt
x = x + dx
y = y + dy
z = z + dz
positionPoint.push(createVector(x, y, z))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
Shapes drawn from corner points can only be colored uniformly. You need to draw separate line segments.
vertex(positionPoint[i].x, positionPoint[i].y, positionPoint[i].z)
line(positionPoint[i].x, positionPoint[i].y, positionPoint[i].z,
positionPoint[i+1].x, positionPoint[i+1].y, positionPoint[i+1].z)
Color channels are in range [0, 255]. Set colorChange = 0 before the loop and inclrement it by 1 in the loop:
colorChange = 0;
for (var i = 0; i < positionPoint.length-1; i++) {
// [...]
colorChange += 1
if (colorChange > 255) {
colorChange = 0
}
}
var x = 0.01
var y = 0
var z = 0
var sigma = 10
var prandtl = 28
var rayleigh = 8 / 3
var dt = 0.01
var positionPoint = []
var colorChange = 0;
function setup() {
createCanvas(800, 800, WEBGL)
colorMode(HSB)
}
function draw() {
background("grey")
formula()
noFill()
scale(5)
strokeWeight(3)
//beginShape()
rotateZ(frameCount * 0.01);
rotateY(frameCount * 0.01);
colorChange = 0;
for (var i = 0; i < positionPoint.length-1; i++) {
stroke(colorChange, 255, 255)
//vertex(positionPoint[i].x, positionPoint[i].y, positionPoint[i].z)
line(positionPoint[i].x, positionPoint[i].y, positionPoint[i].z, positionPoint[i+1].x, positionPoint[i+1].y, positionPoint[i+1].z)
colorChange += 1
if (colorChange > 255) {
colorChange = 0
}
}
//endShape()
}
const formula = () => {
var dx = (sigma * (y - x)) * dt
var dy = (x * (prandtl - z) - y) * dt
var dz = (x * y - rayleigh * z) * dt
x = x + dx
y = y + dy
z = z + dz
positionPoint.push(createVector(x, y, z))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

Draw regular polygons inscribed in a circle

I'm trying to draw regular polygons(square and equilateral triangle) inscribed in a circle of a given centre (x,y) and a radius (r). I'm using raphael.js.
Here's my function to draw a inscribed square:
function draw_square(x,y,radius){
var side= radius*(Math.sqrt(2));
var x = x - (side/2);
var y = y - (side/2);
var square= paper.rect(x, y, side, side);
}
Can anyone shed some light on how I could draw an equilateral triangle(inscribed in a given circle)?
First time I've used raphael, so you'll have to extract what you need from the following:
<html>
<body>
<div id="paper"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
<script>
var paper = new Raphael(document.getElementById('paper'), 256, 256);
var x = 128, y = 128, r = 64, n = 9;
paper.circle(x, y, r);
var xx, yy, i, a, pathString = "";
for (i = 0; i <= n; ++i) {
a = ((4 * Math.PI * i) + (Math.PI * n) + (2 * Math.PI)) / (2 * n);
xx = x + r * Math.cos(a);
yy = y + r * Math.sin(a);
pathString += (i == 0 ? "M " : " L ") + xx + " " + yy;
}
pathString += " z";
paper.path(pathString);
</script>
</body>
</html>
EDIT: Refactored to use var a, and to always have a horizontal base.
function draw_triangle(x, y, radius){
var x_offset =radius*(Math.cos(Math.PI/6));
var y_offset =radius*(Math.sin(Math.PI/6));
var x1 = x;
var y1 = y - radius;
var x2 = x + x_offset;
var y2 = y + y_offset;
var x3 = x - x_offset;
var y3 = y + y_offset;
var triangle = "M"+x1+","+y1+"L"+x2+","+y2+"L"+x3+","+y3+"Z";
var triangle= paper.path(triangle);
}
With a little help of trigo and raphael paper.path().

How do I draw x number of circles around a central circle, starting at the top of the center circle?

I'm trying to create a UI that has a lot of items in circles. Sometimes these circles will have related circles that should be displayed around them.
I was able to cobble together something that works, here.
The problem is that the outer circles start near 0 degrees, and I'd like them to start at an angle supplied by the consumer of the function/library. I was never a star at trigonometry, or geometry, so I could use a little help.
As you can see in the consuming code, there is a setting: startingDegree: 270 that the function getPosition should honor, but I haven't been able to figure out how.
Update 04/02/2014:
as I mentioned in my comment to Salix alba, I wasn't clear above, but what I needed was to be able to specify the radius of the satellite circles, and to have them go only partly all the way around. Salix gave a solution that calculates the size the satellites need to be to fit around the center circle uniformly.
Using some of the hints in Salix's answer, I was able to achieve the desired result... and have an extra "mode," thanks to Salix, in the future.
The working, though still rough, solution is here: http://jsfiddle.net/RD4RZ/11/. Here is the entire code (just so it's all on SO):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.js"></script>
<style type="text/css">
.circle
{
position: absolute;
width: 100px;
height: 100px;
background-repeat: no-repeat;background-position: center center;
border: 80px solid #a19084;
border-radius: 50%;
-moz-border-radius: 50%;
}
.sm
{
border: 2px solid #a19084;
}
</style>
<script type="text/javascript">//<![CDATA[
$(function () {
function sind(x) {
return Math.sin(x * Math.PI / 180);
}
/*the law of cosines:
cc = aa + bb - 2ab cos(C), where c is the satellite diameter a and b are the legs
solving for cos C, cos C = ( aa + bb - cc ) / 2ab
Math.acos((a * a + b * b - c * c) / (2 * a * b)) = C
*/
function solveAngle(a, b, c) { // Returns angle C using law of cosines
var temp = (a * a + b * b - c * c) / (2 * a * b);
if (temp >= -1 && temp <= 1)
return radToDeg(Math.acos(temp));
else
throw "No solution";
}
function radToDeg(x) {
return x / Math.PI * 180;
}
function degToRad(x) {
return x * (Math.PI / 180);
}
var satellite = {
//settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
//optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
getPosition: function (settings) {
//backwards compat
settings.centerPadding = settings.centerPadding || settings.itemPadding;
settings.noOverLap = typeof settings.noOverLap == 'undefined' ? true : settings.noOverLap;
settings.startingDegree = settings.startingDegree || 270;
settings.startSatellitesOnEdge = typeof settings.startSatellitesOnEdge == 'undefined' ? true : settings.startSatellitesOnEdge;
var itemIndex = $.inArray(settings.item, settings.collection);
var itemCnt = settings.collection.length;
var satelliteSide = settings.itemDiameter + (settings.itemSeparation || 0) + (settings.itemPadding || 0);
var evenDistribution = typeof settings.evenDistribution == 'undefined' ? true : settings.evenDistribution;
var degreeOfSeparation = (360 / itemCnt);
/*
we know all three sides:
one side is the diameter of the satellite itself (plus any padding). the other two
are the parent radius + the radius of the satellite itself (plus any padding).
given that, we need to find the angle of separation using the law of cosines (solveAngle)
*/
//if (!evenDistribution) {
var side1 = ((satelliteSide / 2)) + ((settings.minCenterDiameter + (2 * settings.centerPadding)) / 2);
var side2 = satelliteSide;;
var degreeOfSeparationBasedOnSatellite = solveAngle(side1, side1, side2); //Math.acos(((((side1 * side1) + (side2 * side2)) - (side2 * side2)) / (side2 * side2 * 2)) / 180 * Math.PI) * Math.PI;
degreeOfSeparation = evenDistribution? degreeOfSeparation: settings.noOverLap ? Math.min(degreeOfSeparation, degreeOfSeparationBasedOnSatellite) : degreeOfSeparationBasedOnSatellite;
//}
//angle-angle-side
//a-A-B
var a = satelliteSide;
var A = degreeOfSeparation;
/*
the three angles of any triangle add up to 180. We know one angle (degreeOfSeparation)
and we know the other two are equivalent to each other, so...
*/
var B = (180 - A) / 2;
//b is length necessary to fit all satellites, might be too short to be outside of base circle
var b = a * sind(B) / sind(A);
var offset = (settings.itemDiameter / 2) + (settings.itemPadding || 0); // 1; //
var onBaseCircleLegLength = ((settings.minCenterDiameter / 2) + settings.centerPadding) + offset;
var offBase = false;
if (b > onBaseCircleLegLength) {
offBase = true;
}
b = settings.noOverLap ? Math.max(b, onBaseCircleLegLength) : onBaseCircleLegLength;
var radianDegree = degToRad(degreeOfSeparation);
//log('b=' + b);
//log('settings.center.x=' + settings.center.x);
//log('settings.center.y=' + settings.center.y);
var degreeOffset = settings.startingDegree;
if (settings.startSatellitesOnEdge) {
degreeOffset += ((offBase ? degreeOfSeparation : degreeOfSeparationBasedOnSatellite) / 2);
}
var i = ((Math.PI * degreeOffset) / 180) + (radianDegree * itemIndex);// + (degToRad(degreeOfSeparationBasedOnSatellite) / 2); //(radianDegree) * (itemIndex);
var x = (Math.cos(i) * b) + (settings.center.x - offset);
var y = (Math.sin(i) * b) + (settings.center.y - offset);
return { 'x': Math.round(x), 'y': Math.round(y) };
}
,
/* if we ever want to size satellite by how many need to fit tight around the base circle:
x: function calcCircles(n) {
circles.splice(0); // clear out old circles
var angle = Math.PI / n;
var s = Math.sin(angle);
var r = baseRadius * s / (1 - s);
console.log(angle);
console.log(s);
console.log(r);
console.log(startAngle);
console.log(startAngle / (Math.PI * 2));
for (var i = 0; i < n; ++i) {
var phi = ((Math.PI * startAngle) / 180) + (angle * i * 2);
var cx = 150 + (baseRadius + r) * Math.cos(phi);
var cy = 150 + (baseRadius + r) * Math.sin(phi);
circles.push(new Circle(cx, cy, r));
}
},
*/
//settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
//optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
getAllPositions: function (settings) {
var point;
var points = [];
var collection = settings.collection;
for (var i = 0; i < collection.length; i++) {
settings.item = collection[i]
points.push(satellite.getPosition(settings));
}
return points;
}
};
var el = $("#center"), cnt = 10, arr = [], itemDiameter= 100;
for (var c = 0; c < cnt; c++) {
arr.push(c);
}
var settings = {
collection: arr,
itemDiameter: itemDiameter,
minCenterDiameter: el.width(),
center: { x: el.width() / 2, y: el.width() / 2 },
itemPadding: 2,
evenDistribution: false,
centerPadding: parseInt(el.css("border-width")),
noOverLap: false,
startingDegree: 270
};
var points = satellite.getAllPositions(settings);
for (var i = 0; i < points.length; i++) {
var $newdiv1 = $("<div></div>");
var div = el.append($newdiv1);
$newdiv1.addClass("circle").addClass("sm");
$newdiv1.text(i);
$newdiv1.css({ left: points[i].x, top: points[i].y, width: itemDiameter +'px', height: itemDiameter +'px' });
}
});//]]>
</script>
</head>
<body>
<div id="center" class="circle" style="left:250px;top:250px" >
</div>
</body>
</html>
The central bit you need to work out is radius of the small circles. If you have R for radius of the central circle and you want to fit n smaller circles around it. Let the as yet unknown radius of the small circle be r. We can construct a right angle triangle with one corner in the center of the big circle one in the center of the small circle and one which is where a line from the center is tangent to the small circle. This will be a right angle. The angle at the center is a the hypotenuse has length R+r the opposite is r and we don't need the adjacent. Using trig
sin(a) = op / hyp = r / (R + r)
rearrange
(R+r) sin(a) = r
R sin(a) + r sin(a) = r
R sin(a) = r - r sin(a)
R sin(a) = (1 - sin(a)) r
r = R sin(a) / ( 1 - sin(a))
once we have r we are pretty much done.
You can see this as a fiddle http://jsfiddle.net/SalixAlba/7mAAS/
// canvas and mousedown related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// save canvas size to vars b/ they're used often
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var baseRadius = 50;
var baseCircle = new Circle(150,150,50);
var nCircles = 7;
var startAngle = 15.0;
function Circle(x,y,r) {
this.x = x;
this.y = y;
this.r = r;
}
Circle.prototype.draw = function() {
ctx.beginPath();
ctx.arc(this.x,this.y,this.r, 0, 2 * Math.PI, false);
ctx.stroke();
}
var circles = new Array();
function calcCircles(n) {
circles.splice(0); // clear out old circles
var angle = Math.PI / n;
var s = Math.sin(angle);
var r = baseRadius * s / (1-s);
console.log(angle);
console.log(s);
console.log(r);
for(var i=0;i<n;++i) {
var phi = startAngle + angle * i * 2;
var cx = 150+(baseRadius + r) * Math.cos(phi);
var cy = 150+(baseRadius + r) * Math.sin(phi);
circles.push(new Circle(cx,cy,r));
}
}
function draw() {
baseCircle.draw();
circles.forEach(function(ele){ele.draw()});
}
calcCircles(7);
draw();

A relative rotation in an animation

I have a problem with Raphael.js. I want to rotate the "compassScale" - set in the following code - in a relative manner.
This works for the paths, but all the texts "animate" to the absolute rotation of 30 degree. I want them to rotate to the 30 degrees relative from their actual positions.
var compassScale = paper.set();
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
compassScale.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
Like you said, the problem is that you're animating all elements to 30 degrees, not their current rotation + 30 degrees. It's actually quite simple once you think of it that way. Here is your revised code that works:
var compassScale = paper.set();
var texts = []; // array to hold the text elements
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
texts.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
// loop through the text elements, adjusting their rotation by adding 30 to their individual rotation
for (var i = 0, l = texts.length; i < l; i += 1) {
// node.attr("rotation") returns something like 50 200 200, so we have to split the string and grab the first number with shift
texts[i].animate({rotation: (30 + +texts[i].attr("rotation").split(" ").shift()) + " " + centerX + " " + centerY}, 5000);
}
Just a quick observation:
Looks like "Rotation" isn't part of the Atrr anymore since ver 2, so you can't use it in "animate", but you can replace that with "transform: "r" + (some degree)"..
eg:
element.animate( {transform: "r" + (-90)}, 2000, 'bounce');

Categories

Resources