我有一个svg元素,其中包含此图像上的地图和标记等图像。标记有圆圈和下降。使用d3.zoom我可以缩放地图,并且在悬停在标记上时,它们必须按x缩放到1.1并且按y缩放到1,但是保持圆形。我的问题是在圆圈下方定位标记,它们总是在圆圈上方左/右移动。
使用d3.js我在此代码中添加了新标记(this.map.layout是svg上的g元素,在缩放后移动和缩放):
let map = {
element: null,
layout: null,
currentZoomLevel: 1.0
};
const consts = {
markersScales: {
small: {
x: 1,
y: 1
},
hovered: {
x: 1,
y: 1.1
},
extended: {
x: 7,
y: 7
}
}
};
const markers = [
{x: 1, y: 10, color: 'red'},
{x: 5, y: 16, color: 'green'},
{x: 45, y: 30, color: 'blue'},
{x: 20, y: 50, color: 'yellow'},
{x: 0, y: 0, color: 'black'},
]
map.element = d3.select("#map");
let mapWidth = map.element.node().parentNode.getBoundingClientRect().width;
let mapHeight = mapWidth * 2/3;
map.layout = map.element.append('g');
map.layout
.append('svg:image')
.attr('xlink:href', 'http://filipinofreethinkers.org/wp-content/uploads/2009/03/map-to-ateneo.png')
.attr('width', 400)
.attr('height', 202);
map.element.call(d3.zoom().on('zoom', _zoomed));
function _zoomed() {
let {x, y, k} = d3.event.transform;
map.layout.attr("transform", `translate(${x}, ${y}) scale(${k})`);
map.currentZoomLevel = k;
map.layout.selectAll('.marker')
.each(function(d) {
_setScale(d3.select(this));
});
}
function _setScale(element, scale = null) {
let markerData = element.datum();
if (!scale) {
scale = markerData.scale;
}
const yOffset = scale.x == consts.markersScales.extended.x ? -45 : 0;
const strokeWidth = scale.x == consts.markersScales.extended.x ? 0.2 : 1;
const duration = scale.x == consts.markersScales.extended.x ? 500 : 200;
const currentZoom = map.currentZoomLevel;
const totScale = scale.x / currentZoom;
const xOffset = 25.5/totScale;
element.select("use.drop").transition()
.attr('stroke-width', strokeWidth)
.attr('transform', `translate(${25.5*(1-totScale) + 3/currentZoom} ) scale(${ scale.x / currentZoom} ${scale.y / currentZoom}) `)
.duration(duration);
}
function _renderMarker(x, y, color, data, type, scale = {x: 1, y: 1}) {
const offset = 25;
const yOffset = 0;
let g = map.layout.append('g');
g
.datum({
color: color,
x: x,
y: y,
type: type,
scale: scale,
data: data,
open: false
})
.attr('class', 'marker')
.attr('style', `transform: translate(${x-offset}px, ${y-yOffset}px)`);
const currentZoom = map.currentZoomLevel;
const totScale = scale.x / currentZoom;
g.append("use")
.attr('xlink:href', '#path-1')
.attr('fill', color)
.attr('class', 'drop')
.attr('transform', `translate(${25.5*(1-totScale) + 3/currentZoom}) scale(${ scale.x * currentZoom} ${scale.y * currentZoom})`)
.attr('stroke', 'white')
.attr('stroke-width', 1)
.attr('fill-rule', 'evenodd');
g.append('circle')
.attr('cx', offset)
.attr('cy', yOffset)
.attr('r', 3)
.attr('stroke', 'white')
.attr('stroke-width', 1)
.attr('fill', color);
g.on('mouseenter', function(d) {
d3.select(this).raise();
_setScale(d3.select(this), consts.markersScales.hovered);
});
g.on('mouseleave', function(d) {
_setScale(d3.select(this), consts.markersScales.small);
});
g.on('click', function(d) {
_setScale(d3.select(this), consts.markersScales.extended);
});
return g;
}
markers.forEach(marker => _renderMarker(marker.x, marker.y, marker.color, null, null, consts.markersScales.small));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
<svg id="map" width=400 height=300>
<defs>
<path d="
M 43, 39
C 37, 25 25.5, 21 24, 8
C 24, 21 11.35, 25 5, 39
C -1.5, 52 8, 68 24, 68
C 39, 68 49.5, 52 43, 39
Z" id="path-1"></path>
</defs>
</svg>