d3.js v4 minimap或如何对两个元素进行共享缩放?

时间:2017-03-19 12:07:51

标签: javascript d3.js zoom

我找到了这个zooming with minimap的好例子,但它是用旧版本v3编写的。我几乎将其转换为v4但d3.event存在问题。在v3 d3.event似乎共享调用缩放的两个元素之间的缩放参数。因此,如果我在主画布上缩放然后在迷你地图画布上缩放 - d3.event将具有主画布缩放d3.event的最后缩放值,并且它将继续进行缩放。但是在第4版中,两个d3缩放事件都有某种单独的比例或平移值。如在文档中:

  

缩放行为将缩放状态存储在应用缩放行为的元素上,而不是缩放行为本身。这是因为缩放行为可以同时应用于许多元素,并且每个元素都可以独立缩放。

但这就留下了如何在两个元素上创建一个共享缩放事件的问题?

1 个答案:

答案 0 :(得分:3)

编辑:

非常感谢Bill White,他在同一篇帖子中发布了他的最新文章 - D3 MINIMAP V4 UPDATE。这篇文章实际上是我在我的问题中发布的示例的更新。

可以找到问题的答案here

@mbostock在一个问题主题中回答了我的问题。我也发现了另一个解决方案我成功地将他的代码中的一些逻辑和公式直接实现到我的缩放处理程序中。它就像一个魅力。

所以这是从v3到v4的更新缩放处理程序:

注意: 不包含翻译boundery支票。

// ....

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];
}