【问题标题】:d3.js v4 minimap or how to have shared zoom on two elements?d3.js v4 小地图或如何在两个元素上共享缩放?
【发布时间】:2017-08-10 17:08:40
【问题描述】:
我找到了zooming with minimap 这个很好的例子,但它是用旧版本 v3 编写的。我几乎将其转换为 v4,但 d3.event 存在问题。在 v3 中,d3.event 似乎在调用缩放的两个元素之间共享缩放参数。因此,如果我在主画布上缩放,然后在小地图画布上缩放 - d3.event 将具有主画布缩放d3.event 的最后一个缩放值,它将继续缩放。但是在 v4 中,这两个 d3 缩放事件都有不同的缩放或平移值。如文档中所述:
缩放行为将缩放状态存储在应用了缩放行为的元素上,而不是缩放行为本身。这是因为缩放行为可以同时应用于多个元素,并且每个元素都可以独立缩放。
但这留下了一个问题,我如何才能在两个元素上共享一个缩放事件?
【问题讨论】:
标签:
javascript
d3.js
zooming
【解决方案1】:
编辑:
也非常感谢 Bill White,他在同一线程中发布了他的更新文章 - D3 MINIMAP V4 UPDATE。这篇文章实际上是我在问题中发布的示例的更新。
问题的答案可以在here找到。
@mbostock 在问题线程中回答了我的问题。我还找到了另一个解决方案。我成功地将他的代码中的一些逻辑和公式直接实现到我的缩放处理程序中。它就像一个魅力。
这是从 v3 到 v4 更新的缩放处理程序:
注意:不包括翻译边界检查。
// ....
var scale = 1;
var minScale = .5;
var maxScale = 7.5;
var translation = [0, 0];
// ....
// Used for both main canvas and minimap zoom
var zoom = d3.zoom()
.on('zoom', zoomHandler);
function zoomHandler(newScale) {
var prevScale = scale;
var previousTranslation = getXYFromTranslate(panCanvas.attr('transform'));
var isZoomEvent = d3.event && d3.event.sourceEvent.deltaY;
var isDragEvent = d3.event && (d3.event.sourceEvent.movementX || d3.event.sourceEvent.movementY);
if (isZoomEvent) {
scale = calculateNewScale(prevScale, d3.event.sourceEvent);
scale = checkScaleBounderies(scale);
var mousePosition = d3.mouse(this);
// Based on d3.js zoom algorythm
translation[0] = mousePosition[0] - ((mousePosition[0] - previousTranslation[0]) / prevScale) * scale;
translation[1] = mousePosition[1] - ((mousePosition[1] - previousTranslation[1]) / prevScale) * scale;
} else if (isDragEvent) {
translation[0] = previousTranslation[0] + d3.event.sourceEvent.movementX;
translation[1] = previousTranslation[1] + d3.event.sourceEvent.movementY;
} else if (newScale) {
scale = newScale;
}
// Apply the new dimensions to the main canvas
panCanvas.attr('transform', 'translate(' + translation + ') scale(' + scale + ')');
// Apply the new dimensions to the minimap
minimap.scale(scale).render();
}
// Calculate the new scale value based on d3.js zoom formula
function calculateNewScale(prevScale, event) {
return prevScale * Math.pow(2, -event.deltaY * (event.deltaMode ? 120 : 1) / 500);
}
// Check if scale has reached max or min
function checkScaleBounderies(newScale) {
return Math.max(minScale, Math.min(maxScale, newScale));
}
//....
function getXYFromTranslate(transform) {
// Create a dummy g for calculation purposes only. This will never
// be appended to the DOM and will be discarded once this function
// returns.
var g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
// Set the transform attribute to the provided string value.
g.setAttributeNS(null, 'transform', transform);
// consolidate the SVGTransformList containing all transformations
// to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get
// its SVGMatrix.
var matrix = g.transform.baseVal.consolidate().matrix;
// As per definition values e and f are the ones for the translation.
return [matrix.e, matrix.f];
}