2

我在图表应用程序中显示我的 SVG 绘图时遇到问题。画布采用其父组件的大小,而不是它包含的文档。大于此大小的文档不会完全呈现。

案子

例如,我有一张 621x621 像素的绘图。

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" contentScriptType="text/ecmascript" zoomAndPan="magnify" contentStyleType="text/css" preserveAspectRatio="xMaxYMax slice" version="1.0">
  <rect x="50" y="50" fill="white" width="20" height="20" stroke="black" stroke-width="1"/>
  <rect x="600" y="600" fill="blue" width="20" height="20" stroke="black" stroke-width="1"/>
</svg>

它没有 viewBox,因为它是动态创建的。当我创建文档时,我没有大小。

我有一个JSVGCanvas内部 a JSVGScrollPane,滚动窗格的父级比文档小,可以说它的 500x500 像素。

预期行为

当您启动应用程序时,可以看到适合 500x500 的绘图部分。底层文档更大,所以有滚动条允许我向左和向下滚动 121px。

当您增加框架的大小时,越来越多的绘图变得可见。当您将其增加到大于 620x620px 时,滚动条会完全消失。

如果再次使面板比文档小,滚动条会重新出现。

遇到的行为

当您启动应用程序时,可以看到适合 500x500 的绘图部分。没有可见的滚动条。

当您使面板变大时,画布会增加(其背景颜色),但绘图不会扩展到初始 500x500px 之外的部分。

当您将面板缩小时,假设为 400x400,滚动条会出现,但只允许我滚动 100 像素。所以画布仍然是最初的 500x500 而不是所需的 621x621。

例子

ScrollExample可以使用Batik 存储库和我在上面添加的 SVG 文件来重现遇到的行为。该示例使用 500x500px 的固定大小。

如果您将背景颜色添加到画布和滚动窗格,则整个框架都是蓝色的。因此,虽然画布确实增加了大小,但它不会绘制超出其初始大小的任何内容。

canvas.setBackground(Color.blue);
scroller.setBackground(Color.red);

重新调整面板大小后,我可以重置文档。这使得它重新绘制绘图的可见部分,但滚动仍然无法按预期工作,因为画布仍然只是可见部分的大小而不是文档的大小。您可以通过将此侦听器添加到框架(或我想的任何其他面板)来测试这一点。

class ResizeListener implements ComponentListener {

    JSVGCanvas canvas;
    URI url;

    public ResizeListener(JSVGCanvas c, URI u) {
        canvas = c;
        url = u;
    }

    @Override
    public void componentResized(ComponentEvent arg0) {
        canvas.setURI(url.toString());
        // Calling invalidate on canvas or scroller does not work
    }

    // ...
};

我已经读过我应该有一个 viewBox,但是我的文档经常重新创建(基于 modelchange 事件),当我创建文档时,我不知道如何获取 viewBox。SVGDocument.getRootElement().getBBox();通常给我null。当没有给出 viewBox 时,蜡染代码中应该有后备,所以我希望这是可选的。此外,当我在某处平移时,如果我更改文档,它应该保持这种平移。

我希望我能把我的问题说清楚。当我期望 JSVG 类提供开箱即用的所需行为时,我是否期望它们太多,或者我在这里遗漏了什么?有人可以指导我找到解决方案吗?

4

1 回答 1

1

我想我可能已经找到了解决办法。我需要设置文档的高度和宽度,而不是弄乱 viewBox。设置大小后,不需要 viewBox 并且滚动条按预期工作。如果我确实设置了一个视图框,画布会不断缩放图像以使其适合。

现在,为了更新大小,每当我更改可能影响大小的 dom 时,我都会使用它。

static BridgeContext ctx = new BridgeContext(new UserAgentAdapter());
static GVTBuilder builder = new GVTBuilder();

private static void calculateSize(SVGDocument doc) {
    GraphicsNode gvtRoot = builder.build(ctx, doc);

    Rectangle2D rect = gvtRoot.getSensitiveBounds();

    doc.getRootElement().setAttributeNS(null,
            SVGConstants.SVG_WIDTH_ATTRIBUTE, rect.getMaxX() + "");
    doc.getRootElement().setAttributeNS(null,
            SVGConstants.SVG_HEIGHT_ATTRIBUTE, rect.getMaxY() + "");
}

我在线程之外UpdateManager并在我使用的 dom 修改之后执行此操作JSVGCanvas.setSVGDocument()。当我尝试通过UpdateManager它执行此操作时,它只更新了第一个更改。正确初始化更新队列可能会解决此问题,但是由于我更改了很多文档,因此每次都可以重新开始。

我剩下一个小问题。每当我设置新文档时,滚动位置都会重置。我尝试在操作之前进行转换并在之后重新应用它,但这似乎不起作用。

Edit+:我设法通过替换文档的根节点而不是替换整个文档来解决这个小问题。画布设置为文档状态 ALWAYS_DYNAMIC。文档中的更改会立即应用,包括根节点的新大小(如上所述设置)。但是因为文档没有被完全替换,所以滚动状态被保留了。

画布仅在完成渲染文档后才准备好修改 dom。我使用 SVGLoad-listener 来检测这一点。

class Canvas extends JSVGCanvas implements SVGLoadEventDispatcherListener {

    public Canvas() {
        super();
        setDocumentState(ALWAYS_DYNAMIC);
        addSVGLoadEventDispatcherListener(this);
    }

    /**
     * Indicatates whether the canvas has finished its first render, the
     * canvas is now ready for modification of the dom
     */
    private boolean isReadyForModification = false;

    /**
     * Renew the document by replacing the root node with the one of the new
     * document
     * 
     * @param doc The new document
     */
    public void renewDocument(final SVGDocument doc) {

        if (isReadyForModification) {
            getUpdateManager().getUpdateRunnableQueue().invokeLater(
                    new Runnable() {
                        @Override
                        public void run() {
                            // Get the root tags of the documents
                            Node oldRoot = getSVGDocument().getFirstChild();
                            Node newRoot = doc.getFirstChild();

                            // Make the new node suitable for the old
                            // document
                            newRoot = getSVGDocument().importNode(newRoot,
                                    true);

                            // Replace the nodes
                            getSVGDocument().replaceChild(newRoot, oldRoot);
                        }
                    });
        } else {
            setSVGDocument(doc);
        }

    }

    @Override
    public void svgLoadEventDispatchCompleted(SVGLoadEventDispatcherEvent e) {
        isReadyForModification = true;
    }

    // ...
}
于 2012-06-01T16:30:20.347 回答