使用CSS缩放子级以适合父级

osh3o9ms  于 2023-02-06  发布在  其他
关注(0)|答案(1)|浏览(235)

是否可以***缩放***子节点(大小已知)以匹配其父节点(宽度未知)的宽度?
子对象的大小将比父对象的大小更宽。我需要找到一种方法来缩小它,以便它在父对象的边界内显示整个子对象。

我希望尽可能避免使用javascript。
下面是代码:

.a-very-wide-child {
  /* the exact size of the child is known */
  width: 3000px;
  height: 500px;

  /* I am trying to scale this element to match the width of its parent. */
  /* I am thinking probably what I need is a value for [HERE]. I am open 
     to other suggestions though. */
  transform: scale(calc(/*  [HERE] */));
  transform-origin: top left;

/* I am NOT wanting to use width rules to resize the child. */
/* no: width: 100%; */
/* no: max-width: 100%; */
}

.a-small-parent {
  /* has no style rules of note */
  /* the width of the parent is not known */
  /* the height of the parent is not known exactly, but known to be tall 
     enough to not be of concern */
}

/* Below is just styling and is safe to ignore. */

.a-small-parent {
  border: 5px solid teal;
}

.a-very-wide-child {
  background-image: linear-gradient(45deg, #888, transparent);
  border: 5px solid gold;
}

.container{
  width: 40vw;
}
<div class="container">
  <div class=a-small-parent>
    <pre class=a-very-wide-child>
      <img src="https://picsum.photos/400/500" alt="">
    </pre>
  </div>
</div>
0pizxfdo

0pizxfdo1#

很晚才下班,我一直在摆弄各种元素大小和字体缩放以实现响应行为,我知道我已经创建了一些东西来解决您的问题(请参阅末尾的Codepen链接)。
先从不利的一面说起:你不能避免使用一些Javascript,因为你需要一个 * 无单位的比例因子 * 来使CSS transform: scale(..)正常工作。为了响应的目的,你需要知道父容器的 * 当前clientWidth。这对于 * 容器查询 * 和它们各自的单位也是如此。 使用CSS你不能从属性值中去掉单位 *。

比例因子公式:parent.clientWidth / child.Width(无单位)。

使用transform: scale(..)缩小时,HTML将继续使用元素在文档中占用的原始空间,从而导致元素周围的(巨大)空间。修改transform-origin不能解决这个问题,但是如果margin为负,我们可以“删除”多余的空间,并将元素放入其父元素中。该演示假定默认为transform-origin: center,简单地意味着下面的等式对于所有四个边缘属性都成立。

边距空间偏移的公式:offset = -1px * (childSize - scaled childSize) / 2(使用“px”单位转换)。由于当前问题,我们仅能处理 width 值,因此childSize读取childWidth

这个代码片段在所有的公式中使用了CSS自定义变量,以方便操作和测试。代码中有大量的注解,所以我希望不会有什么惊喜。如果我需要详细说明的话,请告诉我。

请注意当 * 缓慢 * 调整浏览器大小时,您有时会在缩放元素的上方/旁边看到空白的单像素线。这不是上面公式的结果,而是由于舍入问题,我没有考虑到。在公式中除法,您将得到十进制值,最终导致部分像素。经验法则:* 部分像素是完整像素 *。这将需要额外的Javascript,因为CSS不支持round(..)ceil(..)floor(..)

我创建了一些带有缩放变化的Codepen:

// Assign event listeners to show scaling works on resize
window.addEventListener('load'  , setVariables); // Initial run
window.addEventListener('resize', setVariables); // Reponsiveness scaling

// parent elements involved
const parents = document.querySelectorAll('.a-small-parent');

function setVariables() {
    // Modify CSS custom variables to hold current element
    // width/height per parent
    parents.forEach((el) => {
        el.style.setProperty('--parentClientWidth' , el.clientWidth);
        el.style.setProperty('--parentClientHeight', el.clientHeight);
    });
};
.a-small-parent {
    /* has no style rules of note. CHECK! */
    /* the width of the parent is not known. CHECK! */
    /* the height of the parent is not known exactly, but known to be tall 
       enough to not be of concern. CHECK! */

    /* [OPTIONAL] defined here, just for clarity */
    --parentClientWidth : 0; /* Modified by JS */
    --parentClientHeight: 0;
}

.a-very-wide-child {
    /* the exact size of the child is known. CHECK! */
    
    /* Define the size of the child element, unitless  */
    --childWidth : 3000; /* We need unitless values to */
    --childHeight:  500; /* calculate scale and offset */

    width : calc(var(--childWidth)  * 1px); /* Could be hardcoded units */
    height: calc(var(--childHeight) * 1px); /* like the original code   */

    /* Define the parent to child scale factors for the transform, unitless */
    --scaleH: calc(var(--parentClientWidth) / var(--childWidth)); /* Horizontal */
    /*
        When in the below 'transform' functions scaleX(..) and scaleY(..)
        are being used, two transform-scale variables must be declared and
        used for scaling and margin offset calculation.
    */
    --scaleV: var(--scaleH); /* For now, Horizontal/Vertical scales are equal */

    /* I am trying to scale this element to match the width of its parent. */
    /* I am thinking probably what I need is a value for [HERE]. I am open 
       to other suggestions though. CHECK! */

    /* Scale the child element relative to current parent clientWidth and clientHeight */
    transform: scale(var(--scaleH), var(--scaleV));

    /* I am NOT wanting to use width rules to resize the child. CHECK! */
    /* no: width: 100%; */
    /* no: max-width: 100%; */

    /*
        'transform: scale(..)' scales an element but leaves the original space the element
        occupied intact. Essentially, leaving a big open space after scale down, or a burst out
        of the parent when scaling up.
    
        Assuming the element is scaled down and uses default 'transform-origin: center'
        we need to calculate margin offsets to correct the gaps:

        T/B: offset = -1 * (childHeight - scaled childHeight) / 2
        R/L: offset = -1 * (childWidth  - scaled childWidth)  / 2

        - childHeight/childWidth offset as their values differ
        - multiplied by -1 as we need a negative offset to shrink the gap
        - divided    by 2  as we must shrink either side (Top and Bottom, Right and Left)

        The sign of below 'offset-unit-multiplier' depends on the current scale factor
        - negative when scale factor < 1
        - positive when scale factor > 1

        This requires additional Javascript to determine (not implemented).
    */

    /* Assumed to be 'negative' in this demo: always scaling down */
    --offset-unit-multiplier: -1px;
    
    --offsetTB: calc(var(--offset-unit-multiplier) * (var(--childHeight) - var(--scaleV) * var(--childHeight)) / 2);
    --offsetRL: calc(var(--offset-unit-multiplier) * (var(--childWidth)  - var(--scaleH) * var(--childWidth))  / 2);

    margin: var(--offsetTB) var(--offsetRL);

    /*
       Moved down as it is not needed. However, when assigned here,
       the calculations for the margin offset must be changed accordingly.
       This would require some testing/fiddling to find out what works best.
    */    
    /*  transform-origin: top left;/* Obsolete */
}

/* Below is just DEMO styling and is safe to ignore. */
* { 
    box-sizing: border-box;
    outline: 1px dashed; /* for debugging */
}
pre { margin: 0 }
img { height: 100%; object-fit: cover }

.a-small-parent    { border: 5px solid teal }
.a-very-wide-child { border: 5px solid red; background-image: linear-gradient(45deg, #888, transparent) }
.container         { width : 40vw }
<div class="container">
    <div class=a-small-parent>
        <pre class=a-very-wide-child>
            <img src="https://picsum.photos/400/500" alt="">
        </pre>
    </div>
</div>

相关问题