gpt4 book ai didi

javascript - splitter - 调整特定节点的大小

转载 作者:行者123 更新时间:2023-12-04 04:47:57 25 4
gpt4 key购买 nike

拖动拆分器时,如何调整 xul 窗口中特定节点的大小?
由于 xul 窗口的复杂性,无法使用 resizebefore/resizeafter 属性。

我试过使用 ondrag分离器上的事件,但它根本没有触发。 ondragstart事件触发正常,我可以使用 event.offsetY捕获分离器移动了多少像素。
使用该值,我可以将其添加到需要元素的高度,这很好用,但不幸的是,每个拖动 session 仅触发一次此事件。

有任何想法吗?

谢谢你。

一个例子来测试它。由于我原来的 xul 的复杂性,我无法改变 xul 结构(用户可以隐藏和更改行的顺序),所以可能只有 javascript 解决方案是可行的):

<?xml version="1.0"?>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window id="testWindow"
title="testing resizing element by splitter"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="color: white;"
>
<vbox id="resizeme" flex="1" style="background-color: yellow; color: black;">
<hbox flex="1">
<label value="#1"/>
<hbox flex="1" align="center" pack="center">
<label value="Resizable by top and bottom splitter"/>
</hbox>
</hbox>
</vbox>
<splitter tooltiptext="Top splitter"/>
<grid flex="1">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row style="background-color: black;">
<label value="#2"/>
<vbox flex="1" pack="center" align="center">
<label value="Must stay constant size at all times"/>
</vbox>
</row>
<row flex="1" style="background-color: blue;">
<label value="#3"/>
<vbox flex="1" pack="center" align="center">
<label value="Resizable by top splitter only"/>
</vbox>
</row>
<row style="background-color: black;">
<label value="#4"/>
<hbox flex="1" pack="center" align="center">
<label value="Must stay constant size at all times, content must fit"/>
<button label="blah"/>
</hbox>
</row>
<splitter tooltiptext="Bottom splitter"/>
<row flex="1" style="background-color: green;">
<label value="#5"/>
<vbox flex="1" pack="center" align="center">
<label value="Resizable by bottom splitter only"/>
</vbox>
</row>
<row style="background-color: black;">
<label value="#6"/>
<vbox flex="1" pack="center" align="center">
<label value="Must stay constant size at all times"/>
</vbox>
</row>
</rows>
</grid>
</window>

最佳答案

没有为 <splitter> 指定特定节点的常用方法。调整大小。

与所有在 XUL 中调整大小一样,目的是您应该能够对 XUL 进行编码,以便您可以使用 <splitter> 调整 UI 布局或其内部部分的大小。元素,自动,无需让您的 JavaScript 监听事件并执行调整大小。但是,您当然可以让 JavaScript 执行 <splitter>调整大小。当您正在做一些复杂的事情时,您通常会这样做,您遇到了 <splitter> 中的错误之一。实现,您只是发现它比微调您的 XUL 以使用库存功能更容易,或者如果您只是想要编写您自己的代码提供的完整控制。重点是<splitter>并且底层系统应该为您执行整个调整大小。

然而,<splitter>元素确实有很大的限制和一些错误,这可能会导致您需要编写自己的调整大小代码。这些限制包括:

  • flex属性重载。它用于控制对象最初的放置方式、窗口大小调整时对象的大小调整方式以及所有 <splitters> 对象的大小调整方式。 .您很可能希望在每种情况下都发生不同的事情。
  • 存货有bug <splitter>代码。我观察到至少有几个不同的,包括一些明确声明为不灵活的元素仍然调整大小的地方。 IIRC,这些似乎主要是在尝试使用 <splitter> 时即在容器内更改超出该容器的对象的大小。
  • 无法明确指定(例如通过 ID)<splitter> 的元素是调整大小。

  • [我在考虑更多限制,但我现在不记得它们。]

    如果您打算使用 JavaScript 进行自己的处理,似乎您需要通过听 mouse events 来完全实现该功能。 . <splitter>的机芯似乎不会触发 drag events .我认为这是因为移动了 <splitter>不被视为“拖放”操作的一部分(即您实际上并未将其捡起并将其放在放置目标上)。虽然我希望能够听到拖动事件,但很明显它们没有触发。

    对我来说, <splitters> 中缺少的最重要的功能是缺乏通过 ID 指定要调整大小的两个元素的能力。显然,从您的问题的标题来看,很明显,您也发现这是非常缺乏的。

    添加指定 ID 到 <splitter> :

    以下代码实现并提供了使用示例 <splitter>元素指定要在 resizebefore 中调整大小的元素的 ID和 resizeafter XUL 中的属性。

    为了在特定的 <splitter> 上使用它,您将需要调用公共(public)函数之一来注册 <splitter>使用 <splitter>的 ID 或 <splitter>元素。例如,两个 <spliter>示例 XUL 中的元素(从您问题中的代码稍作修改)注册为:
    splitterById.registerSplitterById("firstSplitter");
    splitterById.registerSplitterById("secondSplitter");

    splitterById.js:
     /******************************************************************************
    * splitterById *
    * *
    * XUL <splitter> elements which are registered will resize only the two *
    * specific elements for which the ID is contained in the <splitter>'s *
    * resizebefore and resizeafter attributes. The orient attribute is used to *
    * specify if the <splitter> is resizing in the "vertical" or "horizontal" *
    * orientation. "vertical" is the default. *
    * *
    * For a particular <splitter> this is an all or nothing choice. In other *
    * words, you _must_ specify both a before and after element (e.g. You can not *
    * mix using an ID on the resizebefore and not on resizeafter with the *
    * expectation that the after will be resized with the normal <splitter> *
    * functionality. *
    * *
    * On both elements, the attributes minheight, maxheight, minwidth, and *
    * maxwidth will be obeyed. It may be necessary to explicitly set these *
    * attributes in order to prevent one or the other element from growing or *
    * shrinking when the other element is prevented from changing size by other *
    * XUL UI constraints. For example, an element can not be reduced in size *
    * beyond the minimum needed to display it. This code does not check for these *
    * other constraints. Thus, setting these attributes, at least the ones *
    * specifying the minimum height or minimum width will almost always be *
    * desirable. *
    * *
    * Public methods: *
    * registerSplitterById(id) : registers the <splitter> with that ID *
    * registerSplitterByElement(element) : registers the <splitter> element *
    * unregisterSplitterById(id) : unregisters the <splitter> with that ID *
    * unregisterSplitterByElement(element) : unregisters the <splitter> element *
    * *
    ******************************************************************************/

    var splitterById = (function(){

    let beforeER = {};
    let afterER = {};
    let splitIsVertical = true;
    let origClientY = -1;
    let origClientX = -1;

    function ElementRec(_el) {
    this.element = _el;
    this.origHeight = getElementHeight(_el);
    this.origWidth = getElementWidth(_el);
    //The .minHeight and .maxHeight attributes/properties
    // do not appear to be valid when first starting, so don't
    // get them here.
    //this.minHeight = getMinHeightAsValue(_el);
    //this.maxHeight = getMaxHeightAsValue(_el);
    }
    function getElementHeight(el) {
    //.height can be invalid and does not indicate the actual
    // height displayed, only the desired height.
    let boundingRec = el.getBoundingClientRect();
    return boundingRec.bottom - boundingRec.top;
    }
    function getElementWidth(el) {
    //.width can be invalid and does not indicate the actual
    // width displayed, only the desired width.
    let boundingRec = el.getBoundingClientRect();
    return boundingRec.right - boundingRec.left;
    }
    function getMaxHeightAsValue(el) {
    return asValueWithDefault(el.maxHeight,99999999);
    }
    function getMinHeightAsValue(el) {
    return asValueWithDefault(el.minHeight,0);
    }
    function getMaxWidthAsValue(el) {
    return asValueWithDefault(el.maxHeight,99999999);
    }
    function getMinWidthAsValue(el) {
    return asValueWithDefault(el.minHeight,0);
    }
    function asValueWithDefault(value,myDefault) {
    if(value === null || value === "" || value === undefined) {
    value = myDefault;
    }
    //What is returned by the various attributes/properties is
    // usually text, but not always.
    value++;
    value--;
    return value;
    }
    function storeSplitterStartingValues(el) {
    //Remember if the splitter is vertical or horizontal,
    // references to the elements being resized and their initial sizes.
    splitIsVertical = true;
    if(el.getAttribute("orient") === "horizontal") {
    splitIsVertical = false;
    }
    beforeER=new ElementRec(document.getElementById(el.getAttribute("resizebefore")));
    afterER=new ElementRec(document.getElementById(el.getAttribute("resizeafter")));
    if(beforeER.element === undefined || afterER.element === undefined) {
    //Did not find one or the other element. We must have both.
    return false;
    }
    return true;
    }
    function mousedownOnSplitter(event) {
    if(event.button != 0) {
    //Only drag with the left button.
    return;
    }
    //Remember the mouse position at the start of the resize.
    origClientY = event.clientY;
    origClientX = event.clientX;
    //Remember what we are acting upon
    if(storeSplitterStartingValues(event.target)) {
    //Start listening to mousemove and mouse up events on the whole document.
    document.addEventListener("mousemove",resizeSplitter,true);
    document.addEventListener("mouseup",endResizeSplitter,true);
    }
    }
    function endResizeSplitter(event) {
    if(event.button != 0) {
    //Only drag with the left button.
    return;
    }
    removeResizeListeners();
    }
    function removeResizeListeners() {
    //Don't listen to document mousemove, mouseup events when not
    // actively resizing.
    document.removeEventListener("mousemove",resizeSplitter,true);
    document.removeEventListener("mouseup",endResizeSplitter,true);
    }
    function resizeSplitter(event) {
    //Prevent the splitter from acting normally:
    event.preventDefault();
    event.stopPropagation();

    //Get the new size for the before and after elements based on the
    // mouse position relative to where it was when the mousedown event fired.
    let newBeforeSize = -1;
    let newAfterSize = -1;
    if(splitIsVertical) {
    newBeforeSize = beforeER.origHeight + (event.clientY - origClientY);
    newAfterSize = afterER.origHeight - (event.clientY - origClientY);
    } else {
    newBeforeSize = beforeER.origWidth + (event.clientX - origClientX);
    newAfterSize = afterER.origWidth - (event.clientX - origClientX);
    }

    //Get any maximum and minimum sizes defined for the elements we are changing.
    //Get these here because they may not have been populated/valid
    // when the drag was first initiated (i.e. we should have been able
    // to do this only once when the mousedown event fired, but testing showed
    // the values are not necessarily valid at that time.
    let beforeMinSize;
    let beforeMaxSize;
    let afterMinSize;
    let afterMaxSize;
    if(splitIsVertical) {
    beforeMinSize = getMinHeightAsValue(beforeER.element);
    beforeMaxSize = getMaxHeightAsValue(beforeER.element);
    afterMinSize = getMinHeightAsValue(afterER.element);
    afterMaxSize = getMaxHeightAsValue(afterER.element);
    } else {
    beforeMinSize = getMinWidthAsValue(beforeER.element);
    beforeMaxSize = getMaxWidthAsValue(beforeER.element);
    afterMinSize = getMinWidthAsValue(afterER.element);
    afterMaxSize = getMaxWidthAsValue(afterER.element);
    }

    //Apply the limits to sizes we want to change to.
    //These do appear to work better sequentially rather than optimized.
    if(newBeforeSize < beforeMinSize) {
    //Set to beforeMinSize limit if have passed.
    let diff = beforeMinSize - newBeforeSize;
    newBeforeSize += diff;
    newAfterSize -= diff;
    }
    if(newBeforeSize > beforeMaxSize) {
    //Set to beforeMaxSize limit if have passed.
    let diff = beforeMaxSize - newBeforeSize;
    newBeforeSize += diff;
    newAfterSize -= diff;
    }
    if(newAfterSize < afterMinSize) {
    //Set to afterMinSize limit if have passed.
    let diff = afterMinSize - newAfterSize;
    newAfterSize += diff;
    newBeforeSize -= diff;
    }
    if(newAfterSize > afterMaxSize) {
    //Set to afterMaxSize limit if have passed.
    let diff = afterMaxSize - newAfterSize;
    newAfterSize += diff;
    newBeforeSize -= diff;
    }

    //Don't make any changes if we are still violating the limits.
    //There are some pathological cases where we could still be violating
    // a limit (where limits are set such that it is not possible to have
    // a valid height).
    if(newBeforeSize < beforeMinSize || newBeforeSize > beforeMaxSize
    || newAfterSize < afterMinSize || newAfterSize > afterMaxSize) {
    return;
    }

    //Make the size changes
    if(splitIsVertical) {
    beforeER.element.height = newBeforeSize;
    afterER.element.height = newAfterSize;
    } else {
    beforeER.element.width = newBeforeSize;
    afterER.element.width = newAfterSize;
    }
    }
    function _registerSplitterById(id) {
    _registerSplitterByElement(document.getElementById(id));
    }
    function _registerSplitterByElement(el) {
    el.addEventListener("mousedown",mousedownOnSplitter,false);
    }
    function _unregisterSplitterById(id) {
    _unregisterSplitterByElement(document.getElementById(id));
    }
    function _unregisterSplitterByElement(el) {
    el.removeEventListener("mousedown",mousedownOnSplitter,false);
    removeResizeListeners();
    }

    return {
    registerSplitterById : function(id) {
    _registerSplitterById(id);
    },
    registerSplitterByElement : function(el) {
    _registerSplitterByElement(el);
    },
    unregisterSplitterById : function(id) {
    _unregisterSplitterById(id);
    },
    unregisterSplitterByElement : function(el) {
    _unregisterSplitterByElement(el);
    }
    };
    })();

    示例 XUL(从问题修改):
    <?xml version="1.0"?>
    <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
    <window id="testWindow"
    title="testing resizing element by splitter"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    style="color: white;"
    >
    <vbox id="resizeme" height="120" minheight="30" maxheight="250"
    style="background-color: yellow; color: black;">
    <hbox flex="1">
    <label value="#1"/>
    <hbox flex="1" align="center" pack="center">
    <label id="yellowLabel" value="Resizable by top and bottom splitter"/>
    </hbox>
    </hbox>
    </vbox>
    <splitter id="firstSplitter" tooltiptext="Top splitter" orient="vertical"
    resizebefore="resizeme" resizeafter="blueVbox"/>
    <grid>
    <columns>
    <column/>
    <column flex="1"/>
    </columns>
    <rows>
    <row style="background-color: black;">
    <label value="#2"/>
    <vbox pack="center" align="center">
    <label value="Must stay constant size at all times"/>
    </vbox>
    </row>
    <row id="blueRow" style="background-color: blue;">
    <label value="#3"/>
    <vbox id="blueVbox" height="120" minheight="30" pack="center" align="center">
    <label id="blueLabel" value="Resizable by top splitter only"/>
    </vbox>
    </row>
    <row style="background-color: black;">
    <label value="#4"/>
    <hbox pack="center" align="center">
    <label value="Must stay constant size at all times, content must fit"/>
    <button label="blah"/>
    </hbox>
    </row>
    <splitter id="secondSplitter" tooltiptext="Bottom splitter" orient="vertical"
    resizebefore="resizeme" resizeafter="greenVbox"/>
    <row id="greenRow" style="background-color: green;">
    <label value="#5"/>
    <vbox id="greenVbox" height="120" minheight="30" pack="center" align="center">
    <label id="greenLabel" value="Resizable by bottom splitter only"/>
    </vbox>
    </row>
    <row style="background-color: black;">
    <label value="#6"/>
    <vbox pack="center" align="center">
    <label value="Must stay constant size at all times"/>
    </vbox>
    </row>
    </rows>
    </grid>
    <script type="application/x-javascript" src="file://b:/SplitterById.js"/>
    <script type="application/javascript">
    <![CDATA[
    splitterById.registerSplitterById("firstSplitter");
    splitterById.registerSplitterById("secondSplitter");
    ]]>
    </script>
    </window>

    这个例子看起来像:
    Resizing using splitterById

    [注意:虽然编写的代码同时适用于垂直和水平 <splitters> ,我只测试过垂直 <splitters>在上面的例子中。]

    使用 <splitter>通常(不监听事件):
    您最初在问题中使用的示例远没有您现在使用的示例复杂。完全有可能使用严​​格的 XUL 对其进行编码以启用 <splitter>以您要求的方式运作。

    有多种方式(其中许多以各种组合交互)可用于控制通过 <splitter> 调整哪个对象或对象的大小。元素,或整体布局的一般调整。其中包括使用 resizebefore resizeafter <splitter> 的属性结合 flex 的适当值XUL 中元素的属性,并可能包括 box 中的那些元素, hbox , 或 vbox 仅用于分发“flex”的元素。此外,可能需要使用各种 attributes available to an XUL element 为正在调整大小的区域内的每个元素指定各种约束。 (其他 MDN 文档: 123 )。

    您似乎错过的一件事是 flex属性可以是其他值,而不仅仅是 10 .该数值用于指定在特定元素上相对于受调整大小影响的其他元素(由于 <splitter> 或容器元素的调整大小(例如 <window> <box><vbox><hbox> 等),其中包含您感兴趣的元素)。

    试错:
    为了在特定布局中准确获得您想要的功能,您可能需要进行一些反复试验。您可能会找到 XUL 原型(prototype)设计工具 XUL Explorer有助于这样做,具体取决于您在做什么。例如,如果您的代码动态构建您的 XUL,那么 XUL Explorer 就没有那么大的帮助。然而,即使在动态构建我的 XUL 布局时,我也使用 XUL Explorer 来快速查看我正在构建的 XUL 变化的外观/行为。

    您的(原始)具体示例:
    [注意:以下内容基于问题中包含的第一个示例。那个例子比现在的问题要简单得多。特别是,它没有 <splitter>容器内(在新示例中为 <grid> ),该容器需要在该容器外调整大小。]

    对于您的具体示例,您描述的行为可以通过设置 flex 来实现。绿色值 <vbox>相对于其他元素的较大值。

    与许多 UI 问题一样,很难用语言表达您希望发生的一切。例如,在本例中,您没有为其他 <vbox> 指定起始大小。元素。为了展示更多关于 <splitter> 发生的事情并为 flex 使用不同的值在绿色 <vbox> , 我已经包含了一个起始/默认 height 其他 <vbox>元素。这将导致这些元素从那个高度开始,然后只有在绿色 <vbox> 时才从那个高度缩小到它们的最小高度。已经缩小到最低高度。

    注意:您正在使用 style 属性的一部分是 min-height: 30px; .除非您打算将它放在 CSS 类中,否则使用 XUL 属性 minheight 可能更好/更容易.如果您愿意,这样做将使以编程方式进行更改变得更容易。鉴于这是示例代码,您可能只是内联了它,以便不必还包含 CSS 文件。

    编码:
    <?xml version="1.0"?>

    <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

    <window id="testWindow"
    title="testing resizing element by splitter"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    >
    <hbox flex="1">
    <vbox flex="1">
    <vbox flex="1" height="80" pack="center" align="center"
    style="background-color: blue; min-height: 30px; color: white;">
    <label value="this should stay constant size until green element reached its minimum size"/>
    </vbox>
    <vbox id="resizeme" flex="10000" height="80" pack="center" align="center"
    style="background-color: green; min-height: 30px; color: white;">
    <label value="only this should be resized until it reached minimum size of 30px"/>
    </vbox>
    <vbox flex="1" height="80" pack="center" align="center"
    style="background-color: red; min-height: 30px; color: white;">
    <label value="this should stay constant size until green element reached its minimum size"/>
    </vbox>
    </vbox>
    </hbox>

    <splitter/>
    <vbox flex="1"/>

    </window>

    使用 <splitter> 时的样子调整大小:
    Resizing <vbox> elements with flex

    关于javascript - splitter - 调整特定节点的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38162895/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com