首页 > Vue >

vue-grid-layout的边距margin改变组件高度突变化大的问题

时间: 作者:admin 浏览:

在使用vue-grid-layout栅格布局组件的过程中,设置好的布局,要修改纵向外边距margin[1]时,发现每个组件的高度变化很大,但是修改横向外边距margin[0]时,组件的宽度却基本不变化,研究了很久差点没看明白官方计算高度、位置的思路,来看一下官方对组件新位置和新宽高的计算方法:

calcPosition: function (x, y, w, h) {
    const colWidth = this.calcColWidth();
    // add rtl support
    let out;
    if (this.renderRtl) {
        out = {
            right: Math.round(colWidth * x + (x + 1) * this.margin[0]),
            top: Math.round(this.rowHeight * y + (y + 1) * this.margin[1]),
            // 0 * Infinity === NaN, which causes problems with resize constriants;
            // Fix this if it occurs.
            // Note we do it here rather than later because Math.round(Infinity) causes deopt
            width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
            height: h === Infinity ? h : Math.round(this.rowHeight * h + Math.max(0, h - 1) * this.margin[1])
        };
    } else {
        out = {
            left: Math.round(colWidth * x + (x + 1) * this.margin[0]),
            top: Math.round(this.rowHeight * y + (y + 1) * this.margin[1]),
            // 0 * Infinity === NaN, which causes problems with resize constriants;
            // Fix this if it occurs.
            // Note we do it here rather than later because Math.round(Infinity) causes deopt
            width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
            height: h === Infinity ? h : Math.round(this.rowHeight * h + Math.max(0, h - 1) * this.margin[1])
        };
    }
    return out;
}

可以看到,这里通过margin外边距的值计算组件的top、height


一步步来,首先,把计算height值的那一行后半部分拿出来分析下:

height: Math.round(this.rowHeight * h + Math.max(0, h - 1) * this.margin[1])

分割开来看:

this.rowHeight * h:是行高乘以h高度,计算的是组件高度的实际像素值
h-1:也看不明白
Math.max(0, h - 1):就是这里最小值是0
Math.max(0, h - 1) * this.margin[1]:这里看不懂了,乘以外边距是什么意思?

从最后一段代码可以看到组件高度height变化大的原因,margin[1]从1px增加到10px,高度基本增加了10倍左右了

接下来,将这段逻辑写成式子:设r是行高,h是组件的格子高度值,margin为m,列成公式如下:

r x h + (h - 1) x m

展开,去掉乘号,整理下:

rh + mh - m
//合并h
(r+m)h - m

上面的公式,假如将r+m看作新的行高,就能看出点思路了吧;


我们再来看top的计算公式:

Math.round(this.rowHeight * y + (y + 1) * this.margin[1])

同样,按上面的方法整理后,得到的公式是:

r x y + m x y + m
//合并y去掉乘号
(r+m)y + m

可以看到,同样是行高发生了变化,从r增加到r+m,后面多加的m就是纵外边距了;


综合两条式子一起看:

//height
 (r+m)h - m
//top
 (r+m)y + m

可以看到,就是行高发生了变化,top这里加多一个纵外边距m,在高度那里减少一个纵外边距m;

上面的公式导致m就算改变很小,整个画布的高度以及组件的高度都发生巨大的变化,这样是有点问题的;


从另一个方向看,我们改变横外边距时,却发现组件的宽度不会发生突变,且画布总宽度也不变,于是小编尝试打印了一下上述calcPosition方法中列宽colWidth的值,发现横向外边距margin[0]每增加1px,colwidth就减少1px,到这我立刻明白了,原来列宽是不断变小的,变化的值刚好是横向外边距margin[0]的值;

既然这样,那行高是不是也可以用同样的方法呢,所以我们将计算的top和height的那几段代码稍微修改下,最后方法如下(请注意top和height前后计算的变化):

calcPosition: function (x, y, w, h) {
    const colWidth = this.calcColWidth();
    // add rtl support
    let out;
    if (this.renderRtl) {
        out = {
            right: Math.round(colWidth * x + (x + 1) * this.margin[0]),
            top: Math.round((this.rowHeight - this.margin[1]) * y + (y + 1) * this.margin[1]),
            // 0 * Infinity === NaN, which causes problems with resize constriants;
            // Fix this if it occurs.
            // Note we do it here rather than later because Math.round(Infinity) causes deopt
            width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
            height: h === Infinity ? h : Math.round((this.rowHeight - this.margin[1]) * h + Math.max(0, h - 1) * this.margin[1])
        };
    } else {
        out = {
            left: Math.round(colWidth * x + (x + 1) * this.margin[0]),
            top: Math.round((this.rowHeight - this.margin[1]) * y + (y + 1) * this.margin[1]),
            // 0 * Infinity === NaN, which causes problems with resize constriants;
            // Fix this if it occurs.
            // Note we do it here rather than later because Math.round(Infinity) causes deopt
            width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
            height: h === Infinity ? h : Math.round((this.rowHeight - this.margin[1]) * h + Math.max(0, h - 1) * this.margin[1])
        };
    }
    return out;
}

可以看到,其实就是将原来的行高this.rowHeight换成了this.rowHeight - this..margin[1],这样改一下之后,随意改变外边距margin横纵向的值,组件和画布的高度都不会突变了;

其实可以看到height改了之后的代码:

height:Math.round((this.rowHeight - this.margin[1]) * h + Math.max(0, h - 1) * this.margin[1])

简化之后变成:

(r - m) x h + (h - 1) x m
rh - mh + mh - m
rh - m

同样,top的计算也一样:

top: Math.round((this.rowHeight - this.margin[1]) * y + (y + 1) * this.margin[1])

简化之后变成:

(r - m) x y + (y + 1) x m
ry - my + my + m
rh + m

其实这样看就很明显了,这样改之后,外边距增加则组件自身高度减少,这样总高度是没有变化的,只是top那边增加了一个margin[1],所以画布高度就不会突变了,这样横向纵向的逻辑便一致了,体验会好很多;


20230105补充:

发现按上面改算法后,还有几个地方要改,上面的改法layout高度还有微小的变化,所以layout的高度与x、y的值都需要重新计算,其实就是看哪里还有margin,关键词为:this.rowHeight + this.margin[1],对应修改;

第一处修改: GridItem.vue文件的calcXY方法:

calcXY(top, left) {
    const colWidth = this.calcColWidth();

    // left = colWidth * x + margin * (x + 1)
    // l = cx + m(x+1)
    // l = cx + mx + m
    // l - m = cx + mx
    // l - m = x(c + m)
    // (l - m) / (c + m) = x
    // x = (left - margin) / (coldWidth + margin)
    let x = Math.round((left - this.margin[0]) / (colWidth + this.margin[0]));
    let y = Math.round((top - this.margin[1]) / (this.rowHeight + this.margin[1]));

    // Capping
    x = Math.max(Math.min(x, this.cols - this.innerW), 0);
    y = Math.max(Math.min(y, this.maxRows - this.innerH), 0);

    return {x, y};
},

改为:

calcXY(top, left) {
    const colWidth = this.calcColWidth();

    // left = colWidth * x + margin * (x + 1)
    // l = cx + m(x+1)
    // l = cx + mx + m
    // l - m = cx + mx
    // l - m = x(c + m)
    // (l - m) / (c + m) = x
    // x = (left - margin) / (coldWidth + margin)
    let x = Math.round((left - this.margin[0]) / (colWidth + this.margin[0]));
    let y = Math.round((top - this.margin[1]) / this.rowHeight );

    // Capping
    x = Math.max(Math.min(x, this.cols - this.innerW), 0);
    y = Math.max(Math.min(y, this.maxRows - this.innerH), 0);

    return {x, y};
},

第二处修改: GridItem.vue文件的calcWH方法:

calcWH(height, width, autoSizeFlag = false) {
    const colWidth = this.calcColWidth();

    // width = colWidth * w - (margin * (w - 1))
    // ...
    // w = (width + margin) / (colWidth + margin)
    let w = Math.round((width + this.margin[0]) / (colWidth + this.margin[0]));
    let h = 0;
    if (!autoSizeFlag) {
        h = Math.round((height + this.margin[1]) / (this.rowHeight + this.margin[1]));
    } else {
        h = Math.ceil((height + this.margin[1]) / (this.rowHeight + this.margin[1]));
    }

    // Capping
    w = Math.max(Math.min(w, this.cols - this.innerX), 0);
    h = Math.max(Math.min(h, this.maxRows - this.innerY), 0);
    return {w, h};
}

改成:

calcWH(height, width, autoSizeFlag = false) {
    const colWidth = this.calcColWidth();

    // width = colWidth * w - (margin * (w - 1))
    // ...
    // w = (width + margin) / (colWidth + margin)
    let w = Math.round((width + this.margin[0]) / (colWidth + this.margin[0]));
    let h = 0;
    if (!autoSizeFlag) {
        h = Math.round((height + this.margin[1]) / this.rowHeight);
    } else {
        h = Math.ceil((height + this.margin[1]) / this.rowHeight);
    }

    // Capping
    w = Math.max(Math.min(w, this.cols - this.innerX), 0);
    h = Math.max(Math.min(h, this.maxRows - this.innerY), 0);
    return {w, h};
}

第三处修改: GridLayout.vue文件的containerHeight方法:

containerHeight: function () {
    if (!this.autoSize) return;
    // console.log("bottom: " + bottom(this.layout))
    // console.log("rowHeight + margins: " + (this.rowHeight + this.margin[1]) + this.margin[1])
    const containerHeight =  bottom(this.layout) * (this.rowHeight + this.margin[1]) + this.margin[1] + 'px';
    return containerHeight;
}

改成:

containerHeight: function () {
    if (!this.autoSize) return;
    // console.log("bottom: " + bottom(this.layout))
    // console.log("rowHeight + margins: " + (this.rowHeight + this.margin[1]) + this.margin[1])
    const containerHeight =  bottom(this.layout) * this.rowHeight + this.margin[1] + 'px';
    return containerHeight;
}

这样改了以后,x、y方向的算法才能一致;

提醒:
这里有个问题要说明一下,小编这里是因为使用的行高r很小,加了m以后,乘积变化大,因此高度突变得厉害,倘若r是默认的150,加10px,突变也不是很大,这样一来默认的方法用起来也没问题,大家根据自己的使用场景选择不同的计算公式即可;

小编测试过这样改之后也不会影响其他逻辑,大家可以多做尝试;

注:小编使用这个组件由于需求比较灵活,所以已经将vue-grid-layout的源码拷进来直接使用了,方便修改和扩展;

好了,今天就写到这,快去试试吧

微信公众号
微信公众号:
  • 前端全栈之路(微信群)
前端QQ交流群
前端QQ交流群:
  • 794324979
  • 734802480(已满)

更多文章

栏目文章


Copyright © 2014-2023 seozhijia.net 版权所有-粤ICP备13087626号-4