我试图将我的Openlayers应用程序表达为基于组件的应用程序。拥有一个<Marker />
组件,其中包含<Map />
这样的子项,我需要从this.map
访问我的<Marker />
组件的<Map center={[-1.81185, 52.44314]} zoom={6}>
<Marker title="This is a marker" coordinate={[-1.81185, 52.44314]} />
</Map>
属性。
从代表性组件中获取此标记:
<Map />
export default class Map extends React.Component {
static propTypes = {
center: React.PropTypes.array.isRequired,
zoom: React.PropTypes.number.isRequired
}
constructor(props) {
super(props);
this.map = null;
}
componentDidMount() {
this.map = new ol.Map(/* code removed for brevity */);
}
renderChildren() {
const { children } = this.props;
if (!children) return;
return React.Children.map(children, c => {
return React.cloneElement(c, {
map: this.map
});
})
}
render() {
return <div id="map">{this.renderChildren()}</div>
}
}
组件:
<Marker />
export default class Marker extends React.Component {
static propTypes = {
map: React.PropTypes.object,
coordinate: React.PropTypes.array.isRequired,
title: React.PropTypes.string
}
componentDidMount() {
const { map, coordinate, title } = this.props;
if (!map) return;
var marker = createMarkerAndPlaceOn(map);
}
render() {
return null;
}
}
组件:
this.map
正如您所看到的,我尝试通过克隆元素并将属性赋予它来传递#map
属性。
但是,因为我需要依赖要呈现的DOM节点new ol.Map()
,所以我可以先在<Map />
componentDidMount()
中初始化我的this.map
方法。这意味着我的子组件在渲染时不会获得<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<script data-require="d3@3.5.3" data-semver="3.5.3" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script>
var myData = "date New York San Francisco Austin\n\
20111001 63.4 62.7 72.2\n\
20111002 58.0 59.9 67.7\n\
20111003 53.3 59.1 69.4\n\
20111004 55.7 58.8 68.0\n\
20111005 64.2 58.7 72.4\n\
20111006 58.8 57.0 77.0\n\
20111007 57.9 56.7 82.3\n\
20111008 61.8 56.8 78.9\n\
20111009 69.3 56.7 68.8\n\
20111010 71.2 60.1 68.7\n\
20111011 68.7 61.1 70.3\n\
20111012 61.8 61.5 75.3\n\
20111013 63.0 64.3 76.6\n\
20111014 66.9 67.1 66.6\n\
20111015 61.7 64.6 68.0\n\
20111016 61.8 61.6 70.6\n\
20111017 62.8 61.1 71.1\n\
20111018 60.8 59.2 70.0\n\
20111019 62.1 58.9 61.6\n\
20111020 65.1 57.2 57.4\n\
20111021 55.6 56.4 64.3\n\
20111022 54.4 60.7 72.4\n";
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category20();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.temperature);
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = d3.tsv.parse(myData);
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "date";
}));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.date,
temperature: +d[name]
};
})
};
});
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([
d3.min(cities, function(c) {
return d3.min(c.values, function(v) {
return v.temperature;
});
}),
d3.max(cities, function(c) {
return d3.max(c.values, function(v) {
return v.temperature;
});
})
]);
var legend = svg.selectAll('g')
.data(cities)
.enter()
.append('g')
.attr('class', 'legend');
legend.append('rect')
.attr('x', width - 20)
.attr('y', function(d, i) {
return i * 20;
})
.attr('width', 10)
.attr('height', 10)
.style('fill', function(d) {
return color(d.name);
});
legend.append('text')
.attr('x', width - 8)
.attr('y', function(d, i) {
return (i * 20) + 9;
})
.text(function(d) {
return d.name;
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
city.append("text")
.datum(function(d) {
return {
name: d.name,
value: d.values[d.values.length - 1]
};
})
.attr("transform", function(d) {
return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")";
})
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
});
// **************************************************************************************** //
var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");
mouseG.append("path") // this is the black vertical line to follow mouse
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data([cities])
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.selectAll('.mouse-per-line') // Rectangle
.data([cities])
.enter()
.append("rect")
.attr("width", width)
.attr("height", 90)
.style("padding", "5px")
.style("stroke", "#272525")
.style("fill", "#272525")
.style("stroke-width", "1px")
.style("opacity", "0")
.attr('x', 10)
.attr('y', -45);
mousePerLine.selectAll('.mouse-per-line') // Circle
.data(cities)
.enter()
.append("circle")
.attr("r", 5)
.style("stroke", function(d) {
return color(d.name);
})
.style("fill", function(d) {
return color(d.name);
})
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.selectAll('.mouse-per-line') // Text
.data(cities)
.enter()
.append("text")
.attr("transform", "translate(15,13)")
.style("fill", function(d) {
return color(d.name);
})
.style("font-weight", "bold")
.style("font-size", "10pt");
mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line rect")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line rect")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
// **************************************************************************************** //
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("foo", function(d, i) {
var xDate = x.invert(mouse[0]);
var bisect;
var heights = [];
var xDateValue = /\w*.\s.\d.\d*.\d*.:\d*.:\d*/.exec(xDate);
// console.log(xDateValue);
d3.selectAll('circle')
.attr("transform", function(d, j) {
bisect = d3.bisector(function(d) {
return d.date;
}).right;
idx = bisect(d.values, xDate);
var beginning = 0,
end = lines[i].getTotalLength(),
target = null;
while (true) {
target = Math.floor((beginning + end) / 2);
pos = lines[j].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
heights[j] = pos.y;
return "translate(" + mouse[0] + "," + pos.y + ")";
});
var avgheight = 0;
for (var z = 0; z < heights.length; z++) {
avgheight = avgheight + heights[z];
}
avgheight = avgheight / d.length;
d3.select(this).selectAll('rect')
.attr("transform", function(d, i) {
return "translate(" + mouse[0] + "," + avgheight + ")";
});
var rectangleText = "";
for (var t = 1; t < heights.length; t++) {
rectangleText = rectangleText + "<br/>" + y.invert(heights[t]).toFixed(2);
}
d3.select(this)
.selectAll('text').text(function(d, i) {
return xDateValue + " " + d.name + " " + y.invert(heights[i]).toFixed(2)
}).attr("transform", function(d, i) {
return "translate(" + mouse[0] + "," + (avgheight + 30 - (i * 25)) + ")";
}).attr("dx", '20px');
return "translate(" + mouse[0] + "," + pos.y + ")";
});
});
</script>
</body>
</html>
的实例。
是否有任何清洁,非反模式,实现这一目标的方式?
答案 0 :(得分:2)
您可以将map
存储在state
中,并在孩子准备好后立即传递给孩子。
constructor(props) {
super(props);
this.state = {
map: null
}
this.renderChildren = this.renderChildren.bind(this);
}
componentDidMount() {
this.setState({map : new ol.Map()});
}
renderChildren() {
const { children } = this.props;
if (!children)
return;
if(!this.state.map)
return <div>Loading markers</div>
return React.Children.map(children, c => {
return React.cloneElement(c, {
map: this.state.map
});
})
}