Suppose I have an arbitrary path drawn with d3
var points = [0, 4, 4, 4, 8, 8, 4, 4, 4, 8, 4, 4, 4, 4, 0];
var svg = d3.select('svg');
var line = d3.svg.line()
.y(function(d) { return 10*d})
.x(function(d, t) { return t*20 })
.interpolate('cubic');
svg.append('path')
.attr('d', line(points))
.attr('stroke', 'black')
.attr('stroke-width', 2)
.attr('fill', 'none')
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
<svg style="width: 100%; height: 100%; outline: 1px solid green;"></svg>
I want to animate the path drawing out slowly. I understand how to do it from beginning to end but in my case I want to start at an arbitrary point on the line and draw outward at the same rate in both direction.
I can't quite figure out how to do this.
I can utilize attrTween and take slices of the array varying by time, but if I do that, the line shifts since I don't know how to set the horizontal offset correctly at each step.
Hopefully you're working with the second answer on the page you linked to (the one by #duopixel), because that's a much better way. If so, then you can make the line start drawing from the middle if you slightly modify the attributes:
var points = [0, 4, 4, 4, 8, 8, 4, 4, 4, 8, 4, 4, 4, 4, 0];
var svg = d3.select('svg');
var line = d3.svg.line()
.y(function(d) { return 10*d})
.x(function(d, t) { return t*20 })
.interpolate('cubic');
var path = svg.append('path')
.attr('d', line(points))
.attr('stroke', 'black')
.attr('stroke-width', 2)
.attr('fill', 'none');
totalLength = path.node().getTotalLength();
path
.attr("stroke-dasharray", '0 ' + totalLength)
.attr("stroke-dashoffset", totalLength/2)
.transition()
.duration(2000)
.attr("stroke-dasharray", totalLength + ' 0')
.attr("stroke-dashoffset", totalLength);
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
<svg style="width: 100%; height: 100%; outline: 1px solid green;"></svg>
How it works:
First, consider the original, common scenario of animating the drawing of a path from its start to its end. If l is the length of the path (obtained by path.node().getTotalLength()) then setting the path's stroke-dasharray to l + ' ' + l makes the dash and the gap between it and the next dash both equal to l. Under these conditions, the path would appear solid, because the dash length being l "pushes" the gap past the end of the path. By setting stroke-dashoffset to l, the above scenario is reversed — the path is entirely a gap (hence invisible) and the dash falls off the path. By transitioning between these 2 states (from one with offset to one without offset), we get a gradual drawing of a line.
Now, to animate from the middle, the dash is defined with length 0 and the gap has a length l. So now the line is also completely invisible, due to the 0-length dash and the l-length gap . Setting stroke-dashoffset to l/2 moves this 0-length dash halfway down the path. That's the animation starting state. The end state is the reverse dasharray where gap is 0 and dash-length is l, which renders the line completely solid. To properly transition between these states, we also need to set the end state's stroke-dashoffset to l. That was something I figured out experimentally, and I'm not sure exactly how to explain it in words. But it works....
Related
If I have four colours (A, B, C & D) on four points on a line and I want to fill with a gradient that blends nicely between the four colours how would I calculate the colour of the point E?
and A is starting point and D is ending point, before starting point and after ending point fill starting colour and end colour. inside line need to blend colour according to the distance and angle.
The closer E is to any of the other points, the strong that colour should affect the result.
I need like this one.
Any idea how to do that? Speed and simplicity is preferred to accuracy.
Well, in simple terms, take point ß that is halfway between A and B. Assuming the use of RGB colors, if A is red rgb(255, 0, 0) and B is yellow rgb(255, 255, 0), then ß's color will be halfway between these: rgb(255, 128, 0), that is, orange.
As you can see this can be calculated by using a weighted average per color channel - weighted by how close your point is to A and B.
Here's a code example you can run right here:
const slider = document.getElementById("range")
const between = document.getElementById("between")
slider.addEventListener("input", ev => {
const distFromA = ev.target.value
const G = distFromA / 3.92
// Not calculating R and B values, as these don't change in this specific example
const R = 255
const B = 0
between.style.background = `rgb(${R}, ${G}, ${B})`
})
#A { background: red; color: white; }
#B { background: yellow; }
#between { background: gainsboro; }
#between, #A, #B { display: inline-block; width: 50px; height: 50px; }
<aside id=A>A</aside>
<aside id=between>ß</aside>
<aside id=B>B</aside>
<nav><input type=range min=0 max=1000 id=range /></nav>
Do this for each pixel and you get a gradient 👍
I want to create an svg like that:
So, a circumference divided into two parts by a chord. The two sections must have different colors.
I want to draw this object in SVG. I think I need to use the PATH tag, right? Or is there another way to do it?
What points do I need to draw the object? I'm a bit confused..
Yes. It is a <path> element that you will need.
Paths always start with an M (move) command. You'll also need an A (arc) command, and probably an L line command for the line that bisects the circle.
For the arc command, you just need to know the X and Y coordinates of the start and end points (B and C). Plus the radius of the circle. It is important to have accurate coordinates for the start and end points of an arc command. Small discrepancies can cause the position of the circle to move around quite a bit.
In the following demo, I have chosen to calculate the B and C positions based on their angle from the centre. Plus setting the path description attribute from code, allows me to document for you what each of the parameters are for.
// Radius of the circle
var radius = 80;
// Centre coordinate of the circle
var Ox = 100;
var Oy = 100;
// Angles of each point from which we calculate their X and Y coordinates.
// Here, 0 degrees is East, and angle increases in a clockwise direction.
var angleB = 285; // degrees
var angleC = 35;
var B = angleToCoords(angleB, Ox, Oy, radius);
var C = angleToCoords(angleC, Ox, Oy, radius);
// Get the "major segment" path element
var majorPath = document.getElementById("major");
// Set the path description for the "major segment"
majorPath.setAttribute("d", ['M', B.x, B.y, // Move to point B
'L', C.x, C.y, // Line to point C
'A', radius, radius, // X radius and Y radius of the arc
0, // ellipse angle
1, // large arc flag (1 indicates we want the larger of the two possible arcs between the points
1, // clockwise direction flag
B.x, B.y, // arc end point is back at point B
'Z'].join(" ")); // Z command closes the path
// Get the "minor segment" path element
var minorPath = document.getElementById("minor");
// Set the path description for the "minor segment"
minorPath.setAttribute("d", ['M', B.x, B.y, // Move to point B
'A', radius, radius, // X radius and Y radius of the arc
0, // ellipse angle
0, // large arc flag (0 indicates we want the smaller of the two possible arcs between the points
1, // clockwise direction flag
C.x, C.y, // arc end point is at point C
'L', B.x, B.y, // Line to point B
'Z'].join(" ")); // Z command closes the path
// Function to convert from an angle to an X and Y position
function angleToCoords(angleInDegrees, centreX, centreY, radius)
{
var angleInRadians = angleInDegrees * Math.PI / 180;
return {
'x': centreX + (radius * Math.cos(angleInRadians)),
'y': centreY + (radius * Math.sin(angleInRadians))
}
}
path {
stroke: black;
stroke-width: 1;
}
#major {
fill: #78dcdc;
}
#minor {
fill: #aaffaa;
}
<svg width="200" height="200">
<path id="major" d="" />
<path id="minor" d="" />
</svg>
I'm running a sketch with an array of points in 3D space (P3D). I'd like to add an interface to it by drawing text as if it were "onscreen"/2D, only using "X, Y" parameters.
When I tried just adding "text("!##$%", width/2, height/2);" it rendered in 3D space.
Is it possible? I tried "textMode(SCREEN) but doesnt exist in processing 2 anymore.
Here is what I found, I guess on the Processing Forum
You can use:
a PMatrix3D for your 3D content
and code your 2D stuff the plain old way
I wish it helps
PMatrix3D baseMat;
float alpha =0;
void setup() {
size(400, 400, P3D);
// Remember the start model view matrix values
baseMat = getMatrix(baseMat);
}
void draw() {
background(40);
pushMatrix();
camera(0, 0, 400, 0, 0, 0, 0, 1, 0);
directionalLight(255, 255, 255, -100, 150, -100);
ambientLight(40, 40, 40);
// 3D drawing stuff here
rotateY(alpha);
box(100);
alpha += 0.05;
popMatrix();
// Restore the base matrix and lighting ready for 2D
this.setMatrix(baseMat);
ambientLight(255, 255, 255);
// draw 2D stuff here
rect(10, 10, 50, 10);
textSize(25);
text("voila", mouseX, mouseY);
}
A workaround that comes to mind is to create a 2D PGraphic that has the same width/height as your sketch, give it a transparent background, draw your text where you want on it, and then draw the PGraphic onto your real sketch like you would if you were copying over image source data.
I am trying to include a world map in a dynamicaly crreated div using Raphael js. Once the path is defined, I am updating rapahel paper object's viewbox to scale the path. Although this part is working, I am not able to align the path to the center of the div. The following is what I have done so far according to this answer:
var paper = Raphael("map"),
path = paper.path(w_path).attr({fill: "#000", "stroke-width": 0, "stroke-linejoin": "round", opacity: 1}),
box = path.getBBox(),
margin = Math.max(box.width, box.height) * 0.025; // set the viewbox to match the bounding box of the path with 2.5% margins
paper.setViewBox(box.x - margin, box.y - margin, box.width + margin * 2, box.height + margin * 2);
My question is how can I align the path at the center of the div (map div), both vertically and horizontally?
UPDATE:
I have added a jsFiddle for this.
I've just started playing with SVG and Raphael.js and am trying to draw a filled arc but not sure how to start. I'm trying to draw a basic padlock and have the body and two parts of the bolt to fit the arc on top. It's basically a filled 180deg arc, 10 pixels wide. I'm guessing I need to use the .path() but not sure of the syntax or whether I want to be using "curveTo" or "arc" - struggling to find any good SVG or Raphael tutorial sites to be honest.
var padlockBody = paper.rect(100, 100, 100, 100, 5);
padlockBody.attr("fill", "#000000");
var leftBoltPart = paper.rect(120, 70, 10, 30);
leftBoltPart.attr("fill", "#000000");
var rightBoltPart = paper.rect(170, 70, 10, 30);
rightBoltPart.attr("fill", "#000000");
// TODO: filled arc to fit on top of left/right bolt parts
Arcs are notoriously challenging to write by hand (in pure SVG or in Raphael, it's the same).
I usually extend Raphael with these methods (credits belong to the55)
// http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
Raphael.fn.arc = function(startX, startY, endX, endY, radius1, radius2, angle) {
var arcSVG = [radius1, radius2, angle, 0, 1, endX, endY].join(' ');
return this.path('M'+startX+' '+startY + " a " + arcSVG);
};
Raphael.fn.circularArc = function(centerX, centerY, radius, startAngle, endAngle) {
var startX = centerX+radius*Math.cos(startAngle*Math.PI/180);
var startY = centerY+radius*Math.sin(startAngle*Math.PI/180);
var endX = centerX+radius*Math.cos(endAngle*Math.PI/180);
var endY = centerY+radius*Math.sin(endAngle*Math.PI/180);
return this.arc(startX, startY, endX-startX, endY-startY, radius, radius, 0);
};
You can find a demo here: http://jsfiddle.net/cahT9/