2016-04-28 54 views
2

我有一個基於邁克·博斯托克的'Focus + Context via brushing' d3時間表,我試圖做出迴應。響應d3刷牙

我已經能夠實現它的大部分,但我努力刷的程度。作爲一種解決方法,我試圖將它作爲上下文的新寬度,但它表現得非常不正常。我嘗試過的其他東西似乎都沒有影響 - 範圍矩形不會改變寬度。

我需要一種方法來查找範圍矩形的x和寬度,並將其應用於調整大小的我的x-scale(名爲xContext)。有一個'工作'版本here和完整的代碼如下。調整大小功能正在朝向底部。

非常感謝提前。

var marginTimeline = {top: 0, right: 18, bottom: 260, left: 0}, 
    marginContext = {top: 400, right: 18, bottom: 80, left: 0}, 
    w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right, 
    hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom, 
    hContext = parseInt(d3.select("#chart").style("height")) - marginContext.top - marginContext.bottom; 

//Height of the bars drawn. Context bars are half this. 
var barHeight = hTimeline * 0.04;  

var formatDate = d3.time.format("%Y%m%d"), 
    parseDate = formatDate.parse; 

var xTimeline = d3.time.scale().range([0, w]), 
    xContext = d3.time.scale().range([0, w]), 
    yTimeline = d3.scale.linear().domain([0, 6]).range([hTimeline, 0]).nice(), 
    yContext = d3.scale.linear().range([hContext, 0]); 

var thous = d3.format(","); 
var displayDate = d3.time.format("%d %b %Y"); 
var displayMonthYear = d3.time.format("%b %Y"); 
var displayYear = d3.time.format("%Y"); 

var xAxisTimeline = d3.svg.axis().scale(xTimeline).orient("bottom"), 
    xAxisContext = d3.svg.axis().scale(xContext).orient("bottom"), 
    yAxisTimeline = d3.svg.axis().scale(yTimeline).orient("left").outerTickSize(0).ticks(0), 
    yAxisContext = d3.svg.axis().scale(yContext).orient("left").outerTickSize(0).ticks(0); 

var svg = d3.select("#chart") 
    .attr("width", w + marginTimeline.left + marginTimeline.right) 
    .attr("height", hTimeline + marginTimeline.top + marginTimeline.bottom) 
    .append("g"); 

svg.append("defs").append("clipPath") 
    .attr("id", "clip") 
    .append("rect") 
    .attr("width", w) 
    .attr("height", hTimeline); 

var opTimeline = svg.append("g") 
    .attr("class", "timeline") 
    .attr("width", w) 
    .attr("height", hTimeline) 
    .attr("transform", "translate(10,0)"); 

var opContext = svg.append("g") 
    .attr("class", "context") 
    .attr("transform", "translate(10," + marginContext.top + ")"); 

var brush = d3.svg.brush() 
    .x(xContext) 
    .extent([0, 1]) 
    .on("brush", brushed); 

queue() 
    .defer(d3.json, "http://pasi.com.au/omarpasha/api/get_category_posts/?slug=shows&include=title,url,content,custom_fields") 
    .defer(d3.json, "http://pasi.com.au/omarpasha/api/get_category_posts/?slug=timeline&include=title,url,content,custom_fields") 
    .await(ready); 

function ready(error, shows, history) { 
        shows.posts.forEach(function(d) { 
        d.id = d.id; 
        d.title = d.title; 
        d.showpage = d.url; 
        d.startDate = parseDate(d.custom_fields.starting_date[0]); 
        d.endDate = parseDate(d.custom_fields.finishing_date[0]); 
}) 
        history.posts.forEach(function(d) { 
        d.id = d.id; 
        d.title = d.title; 
        d.startDate = parseDate(d.custom_fields.starting_date[0]); 
        d.endDate = parseDate(d.custom_fields.finishing_date[0]); 
        d.line = d.custom_fields.line; 
        d.dateFormat = d.custom_fields.date_format; 
}); 


var minDateShows = d3.min(shows.posts.map(function(d) { return d.startDate; })); 

var minDateHistory = d3.min(history.posts.map(function(d) { return d.startDate; })); 

var minDate = (minDateShows < minDateHistory ? minDateShows : minDateHistory); 

var leftDate = new Date(minDate.getTime()); 
    leftDate.setDate(leftDate.getDate()-40); 

var maxDateShows = d3.max(shows.posts.map(function(d) { return d.endDate; })); 

var maxDateHistory = d3.max(history.posts.map(function(d) { return d.endDate; })); 

var maxDate = (maxDateShows > maxDateHistory ? maxDateShows : maxDateHistory); 

var rightDate = new Date(maxDate.getTime()); 
    rightDate.setDate(rightDate.getDate()+1400); 


    xTimeline.domain([leftDate, rightDate]); 
    xContext.domain(xTimeline.domain()); 
    yContext.domain(yTimeline.domain()); 

var tip = d3.tip() 
    .attr('class', 'd3-tip') 
    .offset(function(d) { if (xTimeline(d.endDate) > 800) { return [-10, 8] } else { return [-10, -8] } }) 
    .direction(function(d) { if (xTimeline(d.endDate) > 800) { return 'nw' } else { return 'ne' } }) 
    .html(function(d) { 
    if (displayMonthYear(d.startDate) == displayMonthYear(d.endDate)) { 
      return d.title + "<br/><p class='yellow'>" + displayMonthYear(d.startDate) + "</p>"; } 
     else { 
      return d.title + "<br/><p class='yellow'>"+ displayMonthYear(d.startDate) + " to " + displayMonthYear(d.endDate) + "</p>"; } 
    }); 

var tip2 = d3.tip() 
    .attr('class', 'd3-tip') 
    .direction(function(d) { if (xTimeline(d.endDate) > 800) { return 'nw' } else { return 'ne' } }) 
    .offset(function(d) { 
     if (xTimeline(d.endDate) > 800) { 
     return [-10, 8]; 
     } else { 
     return [-10, -8]; 
     } 
    }) 
    .html(function(d) { 
    var toolTipContent = ""; 
    if ((xTimeline(d.endDate) - xTimeline(d.startDate) == 0)) { 
     toolTipContent = getToolTipContent(d, true); 
    } else { 
     toolTipContent = getToolTipContent(d, false); 
    } 
    return toolTipContent; 
    }); 

function getToolTipContent(d, sameDates) { 
    var toolTipContent = d.title + "<br/><p class='yellow'>"; 
    if (d.dateFormat == "Year only") { 
    toolTipContent += (sameDates) 
     ? displayYear(d.startDate) + "</p>" + d.content 
     : displayYear(d.startDate) + " to " + displayYear(d.endDate); 
    } else if (d.dateFormat == "Month and year") { 
    toolTipContent += (sameDates) 
     ? displayMonthYear(d.startDate) + "</p>" + d.content 
     : displayMonthYear(d.startDate) + " to " + displayMonthYear(d.endDate); 
    } else { 
    toolTipContent += (sameDates) 
     ? displayDate(d.startDate) + "</p>" + d.content 
     : displayDate(d.startDate) + " to " + displayDate(d.endDate); 
    } 
    toolTipContent += "</p>" + d.content; 
    return toolTipContent; 
} 

svg.call(tip); 
svg.call(tip2); 

opTimeline.append("line") 
    .attr("class", "show show-line") 
    .attr("x1", 0) 
    .attr("x2", w) 
    .attr("y1", yTimeline(5)) 
    .attr("y2", yTimeline(5)); 

opTimeline.append("line") 
    .attr("class", "ost ost-line") 
    .attr("x1", 0) 
    .attr("x2", w) 
    .attr("y1", yTimeline(3)) 
    .attr("y2", yTimeline(3)); 

opTimeline.append("line") 
    .attr("class", "blackart blackart-line") 
    .attr("x1", 0) 
    .attr("x2", w) 
    .attr("y1", yTimeline(1)) 
    .attr("y2", yTimeline(1)); 

opContext.append("line") 
    .attr("class", "context show context-show-line") 
    .attr("x1", 0) 
    .attr("x2", w) 
    .attr("y1", yContext(5)) 
    .attr("y2", yContext(5)); 

opContext.append("line") 
    .attr("class", "context ost context-ost-line") 
    .attr("x1", 0) 
    .attr("x2", w) 
    .attr("y1", yContext(3)) 
    .attr("y2", yContext(3)); 

opContext.append("line") 
    .attr("class", "context blackart context-blackart-line") 
    .attr("x1", 0) 
    .attr("x2", w) 
    .attr("y1", yContext(1)) 
    .attr("y2", yContext(1)); 

opTimeline.append("text") 
    .attr("class", "show show-text") 
    .attr("x", 10) 
    .attr("y", yTimeline(5) + 26) 
    .text("Shows"); 

opTimeline.append("text") 
    .attr("class", "ost ost-text") 
    .attr("x", 10) 
    .attr("y", yTimeline(3) + 26) 
    .text("Ostrowsky Family"); 

opTimeline.append("text") 
    .attr("class", "blackart blackart-text") 
    .attr("x", 10) 
    .attr("y", yTimeline(1) + 26) 
    .text("Black Art"); 

svg.append("text") 
    .attr("class", "explanation") 
    .attr("x", 10) 
    .attr("y", 380) 
    .text("Move the handles below to adjust the time period"); 

opTimeline.append("g") 
    .selectAll("rect") 
    .data(shows.posts) 
    .enter() 
    .append("svg:a") 
    .attr("xlink:href", function(d){return d.showpage;}) 
    .append("rect") 
    .attr("class", "event show-event show") 
    .attr("clip-path", "url(#clip)") 
    .attr("x", (function(d) { return xTimeline(d.startDate); })) 
    .attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) { 
    return (xTimeline(d.endDate) - xTimeline(d.startDate));} 
    else { 
     return 12 
    } })) 
    .attr("y", yTimeline(5) - (barHeight * 0.5)) 
    .attr("height", barHeight) 
    .attr("rx", 10) 
    .attr("ry", 10); 

opTimeline.append("g") 
    .selectAll("rect") 
    .data(history.posts) 
    .enter() 
    .append("rect") 
    .attr("class", (function(d) { if (d.line == "Ostrowsky family") { return "event ost-event ost" } else { return "event blackart-event blackart" } })) 
    .attr("clip-path", "url(#clip)") 
    .attr("x", (function(d) { return xTimeline(d.startDate); })) 
    .attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) { 
    return (xTimeline(d.endDate) - xTimeline(d.startDate));} 
    else { 
     return 12 
    } })) 
    .attr("y", (function(d) { if (d.line == "Ostrowsky family") { return yTimeline(3) - (barHeight * 0.5) } else { return yTimeline(1) - (barHeight * 0.5) } })) 
    .attr("height", barHeight) 
    .attr("rx", 10) 
    .attr("ry", 10); 

opContext.append("g") 
    .selectAll("rect") 
    .data(shows.posts) 
    .enter() 
    .append("rect") 
    .attr("class", "event show-event show") 
    .attr("x", (function(d) { return xContext(d.startDate); })) 
    .attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 6)) { 
    return (xTimeline(d.endDate) - xTimeline(d.startDate));} 
    else { 
     return 6 
    } })) 
    .attr("y", yContext(5) - (barHeight * 0.25)) 
    .attr("height", barHeight/2) 
    .attr("rx", 5) 
    .attr("ry", 5); 

opContext.append("g") 
    .selectAll("rect") 
    .data(history.posts) 
    .enter() 
    .append("rect") 
    .attr("class", (function(d) { if (d.line == "Ostrowsky family") { return "event ost-event ost" } else { return "event blackart-event blackart" } })) 
    .attr("x", (function(d) { return xContext(d.startDate); })) 
    .attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 6)) { 
    return (xTimeline(d.endDate) - xTimeline(d.startDate));} 
    else { 
     return 6 
    } })) 
    .attr("y", (function(d) { if (d.line == "Ostrowsky family") { return yContext(3) - (barHeight * 0.25) } else { return yContext(1) - (barHeight * 0.25) } })) 
    .attr("height", barHeight/2) 
    .attr("rx", 5) 
    .attr("ry", 5); 

opTimeline.append("g") 
    .attr("class", "x axis") 
    .attr("transform", "translate(0," + hTimeline + ")") 
    .call(xAxisTimeline); 


opContext.append("g") 
     .attr("class", "x axis") 
     .attr("transform", "translate(0," + hContext + ")") 
     .call(xAxisContext); 

var brushg = opContext.append("g") 
     .attr("class", "x brush") 
     .call(brush) 
     .selectAll("rect") 
     .attr("y", -6) 
     .attr("height", hContext + 7); 

opContext.selectAll(".e") 
     .append("image") 
     .attr("xlink:href",'../wp-content/themes/omarpasha/img/right-handle.png') 
     .attr("width", 10) 
     .attr("height", 70) 
     .attr("y", -6); 

opContext.selectAll(".w") 
     .append("image") 
     .attr("xlink:href",'../wp-content/themes/omarpasha/img/left-handle.png') 
     .attr("width", 10) 
     .attr("height", 70) 
     .attr("x", -10) 
     .attr("y", -6); 

opTimeline.selectAll(".show-event") 
    .on('mouseover', tip.show) 
    .on('mouseout', tip.hide); 

opTimeline.selectAll(".ost-event, .blackart-event") 
    .on('mouseover', tip2.show) 
    .on('mouseout', tip2.hide); 


function resize() { 
    marginContext = {top: 400, right: 18, bottom: 80, left: 0}, 
    w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right, 
    hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom, 
    hContext = parseInt(d3.select("#chart").style("height")) - marginContext.top - marginContext.bottom; 

    var barHeight = hTimeline * 0.04;  

     xTimeline.range([0, w]), 
     xContext.range([0, w]), 
     yTimeline.range([hTimeline, 0]).nice(), 
     yContext.range([hContext, 0]); 

    svg 
     .attr("width", w + marginTimeline.left + marginTimeline.right) 
     .attr("height", hTimeline + marginTimeline.top + marginTimeline.bottom); 

    svg.select("#clip rect") 
     .attr("width", w) 
     .attr("height", hTimeline); 

    d3.select(".background") 
     .attr("width", w); 

    opTimeline 
     .attr("width", w) 
     .attr("height", hTimeline) 
     .attr("transform", "translate(10,0)"); 

    opContext 
     .attr("transform", "translate(10," + marginContext.top + ")"); 

    opTimeline.select('.x.axis') 
     .attr("transform", "translate(0," + hTimeline + ")") 
     .call(xAxisTimeline); 

    opContext.select('.x.axis') 
     .attr("transform", "translate(0," + hContext + ")") 
     .call(xAxisContext); 

    opTimeline.select(".show-line") 
     .attr("x2", w); 

    opTimeline.select(".ost-line") 
     .attr("x2", w); 

    opTimeline.select(".blackart-line") 
     .attr("x2", w); 

    opContext.select(".context-show-line") 
     .attr("x2", w); 

    opContext.select(".context-ost-line") 
     .attr("x2", w); 

    opContext.select(".context-blackart-line") 
     .attr("x2", w); 

    opTimeline.selectAll(".event") 
     .attr("x", (function(d) { return xTimeline(d.startDate); })) 
     .attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) { 
     return (xTimeline(d.endDate) - xTimeline(d.startDate));} 
     else { 
      return 12 
     } })); 

    opContext.selectAll(".event") 
     .attr("x", (function(d) { return xContext(d.startDate); })) 
     .attr("width", (function(d) { if ((xContext(d.endDate) - xContext(d.startDate) > 6)) { 
     return (xContext(d.endDate) - xContext(d.startDate));} 
     else { 
      return 6 
     } })); 

    brush 
     .x(xContext) 
     .extent([0, 1]) 
     .on("brush", brushed); 
} 

d3.select(window).on('resize', resize); 
    resize(); 

}; 

function brushed() { 
    xTimeline.domain(brush.empty() ? xContext.domain() : brush.extent()); 
    opTimeline.selectAll("rect").attr("x", (function(d) { return xTimeline(d.startDate); })) 
     .attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) { return (xTimeline(d.endDate) - xTimeline(d.startDate));} else { return 12 } })); 
    opTimeline.select(".x.axis").call(xAxisTimeline); 
} 
+0

你有這個想法嗎?我在測試鏈接時沒有發現您的響應式筆刷問題。 – envision

+0

是的,我確實弄明白了。那麼,不是真的我 - 我發現有人的JavaScript知識比我的好得多,他們把它整理出來。它實際上非常簡單 - 在調整大小函數的開始時聲明一個變量,以便在調整大小開始時捕獲範圍的狀態。 您還應該意識到這是D3 v3,並且刷子範圍的方法在v4中已更改。 謝謝你提醒我回答這個問題。 – tgerard

+0

是的,我後來注意到這確實是v3,而隨着v4的功能變化很多。但我幾乎完成了v D3 v4本機響應圖表。雖然會在後面的階段與D3FC進行重構,但在v4更加成熟的時候。 – envision

回答

1

我在Stack Overflow之外有人爲我排序。解決方案很簡單 - 在調整大小函數開始時捕獲畫筆範圍的狀態。沒有其他改變。所以調整大小功能現在看起來像這樣(仍然相當冗長,但工作):

function resize() { 
    var extent = brush.extent(); 

    w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right, 
    hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom; 

    var barHeight = hTimeline * 0.04;  

     xTimeline.range([0, w]), 
     xContext.range([0, w]), 
     yTimeline.range([hTimeline, 0]).nice(), 
     yContext.range([hContext, 0]); 


    svg 
     .attr("width", w + marginTimeline.left + marginTimeline.right); 

    svg.select("#clip rect") 
     .attr("width", w); 

    opTimeline 
     .attr("width", w) 
     .attr("transform", "translate(10,0)"); 

    opContext 
     .attr("transform", "translate(10," + marginContext.top + ")"); 

    opTimeline.select('.x.axis') 
     .attr("transform", "translate(0," + hTimeline + ")") 
     .call(xAxisTimeline); 

    opContext.select('.x.axis') 
     .attr("transform", "translate(0," + hContext + ")") 
     .call(xAxisContext); 

    opTimeline.select(".show-line") 
     .attr("x2", w); 

    opTimeline.select(".ost-line") 
     .attr("x2", w); 

    opTimeline.select(".blackart-line") 
     .attr("x2", w); 

    opContext.select(".context-show-line") 
     .attr("x2", w); 

    opContext.select(".context-ost-line") 
     .attr("x2", w); 

    opContext.select(".context-blackart-line") 
     .attr("x2", w); 

    opTimeline.selectAll(".event") 
     .attr("x", (function(d) { return xTimeline(d.startDate); })) 
     .attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) { 
     return (xTimeline(d.endDate) - xTimeline(d.startDate));} 
     else { 
      return 12 
     } })); 

    opContext.selectAll(".event") 
     .attr("x", (function(d) { return xContext(d.startDate); })) 
     .attr("width", (function(d) { if ((xContext(d.endDate) - xContext(d.startDate) > 6)) { 
     return (xContext(d.endDate) - xContext(d.startDate));} 
     else { 
      return 6 
     } })); 

    brush.extent(extent); 
    // Now just call the methods to update the brush. 
    opContext.select("g.x.brush").call(brush); 
    brushed(); 


}