我使用d3.js组件timeknots。该组件由line和circle元素组成。我稍微修改了这个组件。还有div
用作标签的包装。如果用户将鼠标悬停在圈子元素上,则会显示带有标签的div
元素。
如果用户悬停在圈子上,则会触发mouseover
。然后自动触发mouseout
圆圈处理程序和mouseenter
标签,我不知道原因。
HTML
<div id="timeline1" style="width:500px;"></div>
的javascript
var TimeKnots = {
draw: function(id, events, options){
var cfg = {
width: 600,
height: 200,
radius: 10,
lineWidth: 4,
color: "#999",
background: "#FFF",
dateFormat: "%Y/%m/%d %H:%M:%S",
horizontalLayout: true,
showLabels: false,
labelFormat: "%H:%M:%S",
addNow: false,
seriesColor: d3.scale.category20(),
dateDimension: true,
initTime: "0",
duration: "0"
};
//default configuration overrid
if(options != undefined){
for(var i in options){
cfg[i] = options[i];
}
}
if(cfg.addNow != false){
events.push({date: new Date(), name: cfg.addNowLabel || "Today"});
}
// sort events according to dates
events.sort(function(a,b){
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
if (cfg.dateFormat.indexOf("%Y") === -1){
return new Date("1971-01-01T" + a.date) - new Date("1971-01-01T" + b.date);
}
return new Date(a.date) - new Date(b.date);
});
var idIndex = "";
var svg = d3.select(id).append('svg').attr("class", "timeline")
.attr("width", cfg.width).attr("height", cfg.height).on("mouseover", function(d){
if ($("#poi-"+idIndex).length > 0){
console.log(idIndex + "hover", $("#poi-"+idIndex).is(":hover"));
}
});
svg.append('defs')
.append("linearGradient")
.attr("id", "gradient").attr("gradientUnits", "userSpaceOnUse")
.attr("y1", "0px").attr("y2", "0px")
.attr("x1", "0px").attr("x2", "0px");
svg.select('linearGradient').append("stop").attr("stop-color", "#1689fb");
svg.select('linearGradient').append("stop").attr("stop-color", "rgb(126, 126, 126)");
var tip = d3.select(id)
.append('div')
.attr("class","label-wrapper")
.style("position", "absolute")
.style("font-weight", "300")
.style("color", "white");
svg.append('g')
.attr("stroke", "url(#gradient)")
.attr("class", "wrapper-timeline");
//Calculate times in terms of timestamps
if(!cfg.dateDimension){
var timestamps = events.map(function(d){
if (cfg.dateFormat.indexOf("%Y") !== -1){
return Date.parse(d.date);
}
return Date.parse("1971-01-01T" + d.date);
});//new Date(d.date).getTime()});
var maxValue = d3.max(timestamps);
var minValue = d3.min(timestamps);
}else{
var timestamps = events.map(function(d){
if (cfg.dateFormat.indexOf("%Y") !== -1){
return Date.parse(d.date);
}
return Date.parse("1971-01-01T" + d.date);
});//new Date(d.date).getTime()});
var maxValue = d3.max(timestamps);
var minValue = d3.min(timestamps);
}
var margin = (d3.max(events.map(function(d){return d.radius})) || cfg.radius)*1.5+cfg.lineWidth;
var step = (cfg.horizontalLayout)?((cfg.width-2*margin)/(maxValue - minValue)):((cfg.height-2*margin)/(maxValue - minValue));
var series = [];
if(maxValue == minValue){step = 0;if(cfg.horizontalLayout){margin=cfg.width/2}else{margin=cfg.height/2}}
linePrevious = {
x1 : null,
x2 : null,
y1 : null,
y2 : null
}
var minX = 0;
var maxX = 0;
var yPos = 0;
var cxPoints = new Array();
d3.select(id + " svg g")
.append('g')
.attr("clip-path", "url(#clipLine)")
.attr("class", "wrapper-lines");
svg.select('.wrapper-lines').selectAll("line")
.data(events).enter().append("line")
.attr("class", "timeline-line")
.attr("x1", function(d, i){
var ret;
if(cfg.horizontalLayout){
var timeInMil = new Date();
if (cfg.dateFormat.indexOf("%Y") !== -1){
timeInMil = new Date(d.date).getTime();
} else {
timeInMil = new Date("1971-01-01T" + d.date).getTime();
}
var datum = (cfg.dateDimension) ? timeInMil : d.value;
ret = Math.floor(step*(datum - minValue) + margin)
}
else{
ret = Math.floor(cfg.width/2)
}
linePrevious.x1 = ret
if (i == 0){
minX= ret;
}
return ret;
})
.attr("x2", function(d, i){
if (linePrevious.x1 != null){
if (i == 0){
maxX = linePrevious.x1;
}
return linePrevious.x1
}
if(cfg.horizontalLayout){
var timeInMil = new Date();
if (cfg.dateFormat.indexOf("%Y") !== -1){
timeInMil = new Date(d.date).getTime();
} else {
timeInMil = new Date("1971-01-01T" + d.date).getTime();
}
var datum = (cfg.dateDimension) ? timeInMil : d.value;
ret = Math.floor(step*(datum - minValue ))
}
return Math.floor(cfg.width/2);
})
.attr("y1", function(d, i){
var ret;
if(cfg.horizontalLayout){
ret = Math.floor(cfg.height/2)
}
else{
var timeInMil = new Date();
if (cfg.dateFormat.indexOf("%Y") !== -1){
timeInMil = new Date(d.date).getTime();
} else {
timeInMil = new Date("1971-01-01T" + d.date).getTime();
}
var datum = (cfg.dateDimension) ? timeInMil : d.value;
ret = Math.floor(step*(datum - minValue)) + margin
}
linePrevious.y1 = ret
if (i === 0){
yPos = ret;
}
return ret
})
.attr("y2", function(d){
if (linePrevious.y1 != null){
return linePrevious.y1
}
if(cfg.horizontalLayout){
return Math.floor(cfg.height/2)
}
var timeInMil = new Date();
if (cfg.dateFormat.indexOf("%Y") !== -1){
timeInMil = new Date(d.date).getTime();
} else {
timeInMil = new Date("1971-01-01T" + d.date).getTime();
}
var datum = (cfg.dateDimension) ? timeInMil : d.value;
return Math.floor(step*(datum - minValue))
})
.style("stroke-width", cfg.lineWidth);
var lastClipPathVal = "";
var idIndexInOut = "";
var timeout;
var mouseOutTimer;
svg.select(".wrapper-timeline").selectAll("circle")
.data(events).enter()
.append("circle")
.attr("class", function (d, i) {
return "timeline-event";
})
.attr("id", function (d, i) {
return "knot-" + i;
})
.attr("r", function(d){if(d.radius != undefined){return d.radius} return cfg.radius})
.style("stroke-width", function(d){if(d.lineWidth != undefined){return d.lineWidth} return cfg.lineWidth})
.style("fill", "rgba(220,220,220, 0.01)")
.attr("cy", function(d){
if(cfg.horizontalLayout){
return Math.floor(cfg.height/2)
}
var timeInMil = new Date();
if (cfg.dateFormat.indexOf("%Y") !== -1){
timeInMil = new Date(d.date).getTime();
} else {
timeInMil = new Date("1971-01-01T" + d.date).getTime();
}
var datum = (cfg.dateDimension) ? timeInMil : d.value;
return Math.floor(step*(datum - minValue) + margin)
})
.attr("cx", function(d, i){
var radius = this.getAttribute("r") != undefined ? this.getAttribute("r") : cfg.radius;
var x = 0;
if(cfg.horizontalLayout){
var timeInMil = new Date();
if (cfg.dateFormat.indexOf("%Y") !== -1){
timeInMil = new Date(d.date).getTime();
} else {
timeInMil = new Date("1971-01-01T" + d.date).getTime();
}
var datum = (cfg.dateDimension) ? timeInMil : d.value;
x = Math.floor(step*(datum - minValue) + margin);
cxPoints.push(x);
return x;
}
x = Math.floor(cfg.width/2);
cxPoints.push(x);
return x;
}).on("mouseover", function(d){
if(cfg.dateDimension){
var format = d3.time.format(cfg.dateFormat);
var datetime = format(new Date(d.date));
var dateValue = (datetime != "")?(d.name +" <small>("+datetime+")</small>"):d.name;
}else{
var format = function(d){return d}; // TODO fix date formatting
var datetime = d.value;
var dateValue = d.name +" <small>("+d.value+")</small>";
}
idIndex = d3.select(this).attr("id").replace('knot-','');
timeout = setTimeout(function(){
$("#poi-"+idIndex).css({display: "flex"})
.hide()
.fadeIn()
.addClass("hovered");
}, 400);
lastClipPathVal = $(this).attr("clip-path");
$(this).removeAttr("clip-path");
d3.select(this)
.style("fill", "white").transition()
.duration(100).attr("r", function(d){if(d.radius != undefined){return Math.floor(d.radius*1.8)} return Math.floor(cfg.radius*1.8)})
})
.on("mouseout", function(){
idIndexInOut = d3.select(this).attr("id").replace('knot-','');
clearTimeout(timeout);
console.log("mouseout " + idIndexInOut);
mouseOutTimer = setTimeout(function(){
console.log("fadeoutTriggered " + idIndexInOut);
$("#poi-"+idIndexInOut).fadeOut("fast");
}, 400);
d3.select(this)
.style("fill", "rgba(220,220,220, 0.01)").transition()
.duration(100).attr("r", function(d){if(d.radius != undefined){return d.radius} return cfg.radius})
.attr("clip-path", lastClipPathVal);;
});
// svg.append("use").attr("xlink:href", '');
var labelWrapper = d3.select('.label-wrapper')
.style("width", cfg.width + "px")
.style("left", "112px");
var topOffset = -50;
console.log(topOffset)
var leftToRight = true;
// Append POI's
for (var idx = 0; idx < events.length; idx++) {
if (cxPoints[idx] <= d3.select(id).node().getBoundingClientRect().width/2){
leftToRight = true;
} else {
leftToRight = false;
}
var poiWrapper = labelWrapper.append('div')
.attr("id", "poi-" + idx)
.attr("class",function() {
var className = "poi-wrapper";
if (events[idx].type && events[idx].type === 'link'){
return className += " poi-link";
} else {
return className;
}
});
var poiImg = '';
if (events[idx].type && events[idx].type === 'link'){
poiImg = poiWrapper.append("div").attr("class", "background-img");
var linkContent = poiWrapper.append("div").attr("class", "link-content")
linkContent.append("span").attr("class", "poi-header").html(events[idx].heading);
linkContent.append("span").attr("class", "poi-subheading").html(events[idx].subHeading);
linkContent.append("ul")
.attr("class", "poi-details")
.append("li").html("<i class=\"fa fa-comment\" aria-hidden=\"true\"></i> " + events[idx].details);
}
if (events[idx].type !== 'topic'){
if (leftToRight){
if (poiImg.length > 0){
poiWrapper.style("height", "100px");
var ImgSvg = poiImg.append("svg").attr("class", "poi-image");
ImgSvg.attr("class", "svg-defs").attr("width", "110px")
.append('defs').append("clipPath").attr("id", "clip-triangle-"+[idx])
.append("polygon").attr("points", "0,0 110,0 110,100 19,100 12,107 5,100 0,100 0,0");
ImgSvg.append("rect").attr("class","svg-background").attr("clip-path", "url(#clip-triangle-"+[idx]+ ")").attr("width", "110px").attr("height", "110px");
ImgSvg.append("image").attr("class","svg-image").attr("clip-path", "url(#clip-triangle-"+[idx]+ ")").attr("width", "110px").attr("height", "110px")
.attr("xlink:href", events[idx].img);
poiImg.style({'width': '110px','height': '110px'});
}
} else {
poiWrapper.append("div").attr("class", "arrow-down-empty-anchor");
if (poiImg.length > 0){
poiWrapper.style("height", "100px");
poiImg.style({'background': "url(" + events[idx].img + ")",
'background-size': "100px 100px",
'background-repeat': "no-repeat",
'width': '100px',
'height': '100px'});
}
}
}
$("#poi-" + idx).on(
{
mouseenter: function()
{
console.log("hover poi - clear timeout");
// poiHovered = true;
clearTimeout(mouseOutTimer);
},
mouseleave: function()
{
console.log("unhover poi");
// poiHovered = false;
mouseOutTimer = setTimeout(function(){
console.log("fadeoutTriggered");
$("#poi-"+idIndex).fadeOut("fast");
// $("#poi-"+idIndex).css("zIndex", 0);
}, 400);
}
});
poiWrapper.style("position", "absolute")
.style("color", "black")
.style("top", function(d) {
return topOffset - this.clientHeight - 30 + "px";
})
if (leftToRight){
if (events[idx].type === 'topic'){
poiWrapper.style("left", (cxPoints[idx]) +"px");
} else {
console.log(cxPoints[idx]);
poiWrapper.style("left", (cxPoints[idx]- 120) +"px");
}
} else {
var mainLineWidth = d3.select(".wrapper-timeline line").node().getBoundingClientRect().width;
var vertOffset = mainLineWidth - cxPoints[idx];
poiWrapper.style("right", vertOffset + "px")
}
poiWrapper.style("display", "none");
}
//Adding start and end labels
if(cfg.showLabels != false){
var measuredTime = new Date(null);
if(cfg.duration){
measuredTime.setSeconds(cfg.duration);
var endString = measuredTime.toISOString().substr(12, 7);
} else {
var endString = duration;
}
svg.append("text")
.text(endString).style({"font-size": "70%", "font-weight": "bold", "fill": "#7e7e7e"})
.attr('class', 'duration')
.attr("x", function(d){if(cfg.horizontalLayout){return cfg.width - d3.max([this.getBBox().width, (margin+this.getBBox().width/2)])} return Math.floor(this.getBBox().width/2)})
.attr("y", function(d){if(cfg.horizontalLayout){return Math.floor(cfg.height/2+(margin+this.getBBox().height))}return cfg.height-margin+this.getBBox().height/2})
}
}
}
var kurbickFilms = [
{
type: "link",
date: "00:00:00",
img:
"http://upload.wikimedia.org/wikipedia/en/thumb/6/6c/Seafarers_title.jpg/225px-Seafarers_title.jpg",
heading: "Revenerace for Putin on the right buys trump cover",
subHeading: "NYTIMES / JEREMY PETERS",
details: "3"
},
{
type: "link",
date: "00:10:25",
img:
"http://upload.wikimedia.org/wikipedia/en/thumb/6/6c/Seafarers_title.jpg/225px-Seafarers_title.jpg",
heading: "Revenerace for Putin on the right buys trump cover",
subHeading: "NYTIMES / JEREMY PETERS",
details: "3"
},
{
type: "link",
date: "00:10:25",
img:
"http://upload.wikimedia.org/wikipedia/en/thumb/6/6c/Seafarers_title.jpg/225px-Seafarers_title.jpg",
heading: "Revenerace for Putin on the right buys trump cover",
subHeading: "NYTIMES / JEREMY PETERS",
details: "3"
},
{
type: "link",
date: "01:11:24",
img:
"http://upload.wikimedia.org/wikipedia/en/thumb/6/6c/Seafarers_title.jpg/225px-Seafarers_title.jpg",
heading: "Revenerace for Putin on the right buys trump cover",
subHeading: "NYTIMES / JEREMY PETERS",
details: "3"
}
];
TimeKnots.draw("#timeline1", kurbickFilms, {
showLabels: true,
dateFormat: "%H:%M:%S",
color: "#7e7e7e",
width: 800,
height: 100,
labelFormat: "%H:%M:%S",
lineWidth: 2,
radius: 6,
duration: "4284.839125"
});
CSS
svg.poi-image {
position: absolute;
height: 110px;
width: 110px;
}
.poi-wrapper{
display: flex;
background: #61b4f7;
z-index: 2;
}
.poi-intro, .poi-twitter, .poi-link, .poi-interview, .poi-add, .poi-comment{
width: 300px;
}
答案 0 :(得分:0)
有一个不可见的svg
元素,它覆盖了circle
元素。