如何在更改后的Flexbox网格中获取Vue元素的更新CSS宽度?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在更改后的Flexbox网格中获取Vue元素的更新CSS宽度?相关的知识,希望对你有一定的参考价值。

这是我的头擦。也许我在Vue生命周期中没有看到应该考虑的东西。我将尝试使其尽可能简单。

我有一个自定义按钮组件,该组件正在由flex网格容器调整大小。网格看起来像这样:

<template>
    <div class="component d-flex flex-column flex-grow-1" style="min-height: 0">
        <div class="justify-center align-content-center" 
        style="display: grid; width: 100%; height: 100%; grid-gap: 1rem;
        grid-template-columns: repeat(12, minmax(0, 1fr));
        grid-template-rows: repeat(8, minmax(0, 1fr));">
            <slot/>
        </div>
    </div>
</template>

我有一个看起来像这样的网格面板:

<template> 
    <v-card :style="cardStyle" v-bind="$attrs">
        <slot></slot>
    </v-card>
</template>

<script>
export default 
    computed: 
        cardStyle: function() 
            return 'grid-column: span ' + (this.columns || 2) +
            '; grid-row: span ' + (this.rows || 2) + ';';
        
    ,
    props: 
        columns: [Number, String],
        rows: [Number, String],
    

</script>

“我的按钮”使用面板来像这样调整大小:

<template>
    <grid-panel ref="elm" :columns='columns || 2' :rows='rows || 2' elevation="0">
        <v-btn class="ma-0 align-stretch flex-column py-4" 
        width="100%" max-width="100%" height="100%" :color="color || 'rgba(0,0,0,0)'">

            <div class="d-flex fill-height py-2" style="width:100%;">
                <div ref="iconTest" class="fill-height">
                    <v-icon ref="icon" class="mb-0 flex-shrink-1 notransition py-0 pr-2" :color="iconColor" style="width:auto; height:auto"> icon </v-icon>
                </div>
                <div ref="text" class="flex-grow-1 d-flex align-center text-center">
                    <span ref="innerSpan" style="font-size: 1px" class="textFitted text-center d-inline-block notransition">
                         label 
                    </span>
                </div>
            </div>
        </v-btn>
    </grid-panel>
</template>

我知道这看起来很烂,但是请耐心等待一秒钟。

我想要一种方法,以确保我放在此按钮中的任何文本或标签都不会被剪切。并不是说我会有大量的文本作为标签,但是与网络上的传统按钮不同,根据按钮在网格中的放置方式,这些按钮将具有固定的大小;网格将根据其显示的屏幕调整自身大小。为了解决此问题,我想自动调整文本大小以填充可用空间。因此,使用JS我想到了:

// Calculate height without padding.
innerHeight(el)
    var style = window.getComputedStyle(el, null);
    return el.clientHeight -
    parseInt(style.getPropertyValue('padding-top'), 10) -
    parseInt(style.getPropertyValue('padding-bottom'), 10);
,

// Calculate width without padding.
innerWidth(el)
    var style = window.getComputedStyle(el, null);
    return el.clientWidth -
    parseInt(style.getPropertyValue('padding-left'), 10) -
    parseInt(style.getPropertyValue('padding-right'), 10);
,

    updateFontSize() 

        if (!this.isMounted || this.$refs.text == undefined) 
            return;

        // Everything below here is taken from the textFit JS library
        // https://github.com/STRML/textFit/blob/master/textFit.js
        // By default - the library does not work well with Vue, and I had to modify it some

        let el = this.$refs.text;
        let innerSpan = this.$refs.innerSpan;

        innerSpan.style.fontSize = '1px';

        let originalWidth = this.innerWidth(el);
        let originalHeight = this.innerHeight(el);

        console.log(el);
        console.log(originalWidth);

        // Don't process if we can't find box dimensions
        if (!originalWidth || !originalHeight) 
            throw new Error('Set a static width on the target element ' + el.outerhtml +
                ' before using textFit!');
        


        let low = 4;
        let high = 36;
        let mid;

        // Binary search for highest best fit
        let size = low;
        while (low <= high) 
            mid = (high + low) >> 1;
            innerSpan.style.fontSize = mid + 'px';
            if(innerSpan.scrollWidth <= originalWidth && innerSpan.clientHeight <= originalHeight)
                size = mid;
                low = mid + 1;
             else 
                high = mid - 1;
            
        
        // found, updating font if differs:
        innerSpan.style.fontSize = size + 'px';

    ,

它相对容易理解,并且效果很好。所有这些操作都是查看div中包含文本的可用空间,由于使用了flexbox,该空间被设置为填充该空间,并增大文本直到溢出为止。

然后我想,我也可以对图标做同样的事情....?

好的,这就是我开始困惑的地方。

图标调整大小本身就可以了。编辑::实际上,我想我可能错了...我只是在玩这个,我发现了一些有趣的东西。请参阅下面的编辑。

我认为首先需要确定图标的大小,这将允许CSS增大和缩小2个flex项目,然后调整文本大小。这是用于调整图标大小的代码,您将看到一堆console.logs并注释掉了一些残骸,这些残骸是我拔掉头发的残余。

let widthOnly = this.rows != 1;
let heightOnly = this.rows == 1;

let el = this.$refs.iconTest;
let innerSpan = this.$refs.icon.$el;

let el2 = this.$refs.text;

console.log('text elem before:');            
console.log(el2);
console.log(this.innerWidth(el2));

this.$refs.innerSpan.style.fontSize = '1px';
innerSpan.style.fontSize = '1px';

let originalWidth = this.innerWidth(el);
let originalHeight = this.innerHeight(el);

// Don't process if we can't find box dimensions
if (!originalWidth || !originalHeight) 
    throw new Error('Set a static width on the target element ' + el.outerHTML +
        ' before using textFit!');


console.log('text elem during 1:');            
console.log(el2);
console.log(this.innerWidth(el2));

innerSpan.style.fontSize = originalHeight + 'px';


let low = 4;
let high = 80;
let mid;

// Binary search for highest best fit
let size = low;
while (low <= high) 
    mid = (high + low) >> 1;
    innerSpan.style.fontSize = mid + 'px';
    if((heightOnly || innerSpan.clientWidth <= originalWidth) && (widthOnly || innerSpan.clientHeight <= originalHeight))
        size = mid;
        low = mid + 1;
     else 
        high = mid - 1;
    

// found, updating font if differs:
innerSpan.style.fontSize = size + 'px';
// this.iconSize = size + 'px';


// THIS IS WHERE HELL HAPPENS

console.log('text elem after:');            
console.log(el2);
console.log(" BEFORE DELAY !!! ");
console.log(this.innerWidth(this.$refs.text)); // <-- This should be calculated after the icon has completely resized.

setTimeout(() => 
    console.log(" AFTER DELAY !!!! ");
    console.log(this.innerWidth(this.$refs.text)); // <-- This gets the correct value after a 2 second time out.
    //this.updateFontSize();
, 2000);

this.$nextTick(this.$nextTick(() => 
    console.log('text elem next:');            
    console.log(el2);
    console.log(this.innerWidth(el2)); // <-- This outputs the same as before the delay
));
//this.updateFontSize();

正如我所说,调整图标大小的实际行为很好。上面的代码中的el2是对文本容器的引用。这里发生的事情是,文本容器的宽度花费了大量时间才能从CSS计算中获取更新的正确值。控制台输出如下所示:

text elem before:
60
text elem during 1:
74
text elem after:
BEFORE DELAY !!! 
59
text elem next:
59
AFTER DELAY !!!! 
48

59是修改图标大小后直接获得的值。在这种情况下,48是正确的值,但我只能通过在任意超时后进行设置来获得该值。我已经获得了正确的值,超时值低至121ms,但是在120时它又过时了。这里的问题是,使用正确的值,我的文本标签会正确调整大小,如下所示:

Correct text placement

但是如果我很快运行updateFontSize并返回错误的宽度,则会得到以下信息:

Incorrect Text Placement

使用$ nexttick不能解决它。

我已尽力在codesandbox上重新创建了此文件,但有点奇怪。也许看看是否需要更多背景信息:codesandbocklink

除了增加延迟之外,我没有其他想法。

旁注:这并非旨在作为一种响应式设计或类似的东西。这是针对信息亭类型的应用程序。调整大小的文本仅在安装的组件上发生一次。

编辑::好的,这有点奇怪。在玩了一些之后,尝试制作此按钮的方形版本。它看起来应该像这样:

right

但是相反,它像这样出来:

wrong

所以我添加了一些控制台日志,如下所示:logs

这是输出:

results

因为这是一个方形图标,所以结果应该是宽度与字体大小相同,如下所示:

results2

所以我有了新的理论...页面加载后字体大小是否有可能改变?还是更确切地说,图标的宽度在已经计算之后就改变了?好吧,事实证明..是​​的!它是!我将图标替换为不需要加载任何外部字体的标准文本,并且:

It Works

成功!

Sooooo ......这完全取决于MDI图标包的加载时间...

enter image description here

所以我想这个问题的重点现在转移到了...

如何检测vdit中的mdi图标何时完成加载?

这是我的头擦。也许我在Vue生命周期中没有看到应该考虑的东西。我将尝试使其尽可能简单。我有一个自定义按钮...

答案

也许您需要ResizeObserver。

另一答案

确定这是字体无法快速加载的情况后,答案很简单:

以上是关于如何在更改后的Flexbox网格中获取Vue元素的更新CSS宽度?的主要内容,如果未能解决你的问题,请参考以下文章

如何获取 javascript 中用于 flexbox 和网格布局的行数和列数?

Flexbox 大小均匀的元素,无论内容如何

Flexbox 挑战:如何在整行中使用相同且均匀的边距进行换行

作为 flexbox 的子元素的 CSS 网格未按预期运行

使用 flexbox 具有相同高度元素的多线网格

使用 CSS Grid 和 Flexbox 水平对齐位于不同网格单元格中的元素