D3.js How to rotate Text on a path

爱⌒轻易说出口 提交于 2019-12-12 16:27:42

问题


This is my first D3 project (I'm not a programmer, so learning everything at once).

I'm trying to make a circular calendar that I've been drawing by hand for several years, which is a huge pain.

Most of the bits are now working, but I can't figure out how to get the text turned 90 degrees on this arc.

Once I get the text turned, then I'll need to figure out how to get it spaced equally around the circle.

My goal is to get it to look like this hand drawn example:

Here's what it looks like currently:

I may have gone down a dead end by placing the text on a path, so any and all suggestions welcome.

Edit: I created a jsFiddle here jsfiddle.net/LMF9h/1/

Here is the code I have so far:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body { font: 12px Arial;}
path {
    stroke: red;
    stroke-width: 1;
    fill: none;
}
.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}
</style>

<body>
    <script type="text/javascript" src="d3/d3.v3.js"></script>

    <script type="text/javascript">

var margin = {top: 25, right: 25, bottom: 25, left: 25},
width = 950 - margin.left - margin.right,
height = 950 - margin.top - margin.bottom;

// Build Day Numbers
function pad(n) {
    return (n < 10) ? ("0" + n) : n;
}

function getDaysArray(year) {
    var numDaysInMonth, daysInWeek, daysIndex, index, i, l, daysArray;

    numDaysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    daysInWeek = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
    daysIndex = { 'Sun': 0, 'Mon': 1, 'Tue': 2, 'Wed': 3, 'Thu': 4, 'Fri': 5, 'Sat': 6 };

    daysArray = [];

for (m=0; m < 12; m++) {
        index = daysIndex[(new Date(year, m, 1)).toString().split(' ')[0]];
    for (i = 0, l = numDaysInMonth[m]; i < l; i++) {
        daysArray.push(pad((i + 1)) + ' ' + daysInWeek[index++]);
        if (index == 7) index = 0;
    }
}
    return daysArray;
}


var year = 2014

var days = getDaysArray(year)

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g"),
    pi = Math.PI;


var arc = d3.svg.arc()
    .innerRadius(850)
    .outerRadius(851)
    .startAngle(0)
    .endAngle(-pi*1.99999)

var path = svg.append("path")
    .attr("d", arc)
    .attr("id", "path1")
    .attr("transform", "translate(900,900)")
    .attr("fill","#f00")


// Draw lines
function daysInYear(year) {
    if(year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) {
        // Leap year
        return 366;
    } else {
        // Not a leap year
        return 365;
    }
}

var dt = new Date(year+"/01/01");
var day = dt.getDay();
var startrads = (2*pi / daysInYear(year)) * day;
    //---center point(width,height)---
        var cx=width
        var cy=height
        var r=850 //--radius--
        var radialLines=52 //---create 52 lines---
        var angle=2*Math.PI/radialLines //---angle between each line---
        var radialPoints=[]
        for(var k=startrads;k<radialLines;k++)
        {
                var x2=r*Math.cos(angle*k)+cx
                var y2=r*Math.sin(angle*k)+cy
                radialPoints.push([x2,y2])
        }
    //---your d3 SVG parent element---
        svg.selectAll("line")//---an empty selection---
        .data(radialPoints)
        .enter().append("svg:line")
        .attr("x1",cx)
        .attr("y1",cy)
        .attr("x2",function(p){return p[0]})
        .attr("y2",function(p){return p[1]})
        .attr("stroke","red");



// Add day labels.
var text1 = svg.append("text");


text1
    .selectAll("body")
    .data(days)
    .enter()
    .append("textPath")
    .attr("xlink:href","#path1")
    .text(function(d) { return d; });



    </script>

</body>

回答1:


To get the text right, you simply need to calculate its position and rotation in the same way that you're calculating the end of the radial lines. The code would be something like

svg.selectAll("text").data(days).enter()
.append("text")
.attr("transform", function(d, i) {
    return "translate(" + (850*Math.cos(i*2*Math.PI/365)+width) +
           "," + (850*Math.sin(i*2*Math.PI/365)+height) + ")" +
           "rotate(" + (i*360/365) + ")";
})
.text(function(d) { return d; });

Note that on the side of the circle that is visible in your jsfiddle, the text would be upside down. You can fix this by adding 180 to the rotate value. Modified jsfiddle here.



来源:https://stackoverflow.com/questions/17226206/d3-js-how-to-rotate-text-on-a-path

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!