Сделайте JSVGCanvas внутри JSVGScrollPane соответствовать размеру SVGDocument
У меня проблема с отображением моего рисунка 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
внутри JSVGScrollPane
родительский элемент полосы прокрутки меньше, чем документ, скажем, 500x500 пикселей.
Ожидаемое поведение
При запуске приложения видна та часть чертежа, которая умещается в 500x500. Базовый документ больше, поэтому есть полосы прокрутки, которые позволяют мне прокрутить 121px влево и вниз.
При увеличении размера рамки все больше и больше рисунка становится видимым. Когда вы увеличите его до 620x620 пикселей, полосы прокрутки могут полностью исчезнуть.
Если вы сделаете панель меньше, чем документ, снова появятся полосы прокрутки.
Встреченное поведение
При запуске приложения видна та часть чертежа, которая умещается в 500x500. Полосы прокрутки не видны.
Когда вы увеличиваете панель, холст увеличивается (его цвет фона), но рисунок не расширяется до той части, которая находится за пределами первоначальных 500x500px.
Когда вы уменьшаете размер панели, скажем, до 400x400, полосы прокрутки появляются, но позволяют только прокрутить 100px. Таким образом, холст все еще является исходным 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
, Предполагается, что в коде Batik есть запасные варианты, когда viewBox не задан, поэтому я надеюсь, что это необязательно. Кроме того, когда я где-то перемещаюсь, это должно сохраняться, если я изменю документ.
Надеюсь, я разъясню свою проблему. Ожидаю ли я слишком многого от JSVG-классов, когда ожидаю, что они обеспечат желаемое поведение из коробки, или я что-то здесь упускаю? Может ли кто-нибудь, пожалуйста, направить меня к решению?
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
это только обновило первое изменение. Вероятно, есть исправление для этого с правильной инициализацией очереди обновлений, но, поскольку я изменяю большую часть документа, я с таким же успехом могу начинать каждый раз заново.
Я остался с одной незначительной проблемой. Всякий раз, когда я устанавливаю новый документ, позиция прокрутки сбрасывается. Я пытался получить преобразование до манипуляции и применить его позже, но это, похоже, не работает.
Изменить +: мне удалось исправить эту незначительную проблему, заменив корневой узел документа, а не заменив весь документ. Холст устанавливается с состоянием документа ALWAYS_DYNAMIC. Изменения в документе применяются немедленно, включая новый размер корневого узла (установите, как описано выше). Но поскольку документ не полностью заменен, состояние прокрутки сохраняется.
Холст готов к модификации dom только после того, как закончил рендеринг документа. Я использовал SVGLoad-слушатель, чтобы обнаружить это.
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;
}
// ...
}