【问题标题】:What's the math behind CSS's background-size:coverCSS background-size:cover 背后的数学原理是什么
【发布时间】:2012-05-04 08:03:11
【问题描述】:

我正在创建一个“图像生成器”,用户可以在其中上传图像并添加文本和/或在其上绘图。输出的图像是固定尺寸(698x450)。

在客户端,当用户上传他们的图片时,它被设置为 698x450 的 div 的背景,背景大小为:cover。这使它很好地填充了该区域。

最终的组合图像由 PHP 使用 GD 函数生成。我的问题是,如何让图像在 PHP 中像在 CSS 中一样缩放。我希望 PHP 脚本的结果看起来与上面在 CSS 中设置的图像相同。 有谁知道使用 background-size:cover 的浏览器如何计算如何适当地缩放图像?我想把它翻译成 PHP。

谢谢

【问题讨论】:

    标签: css gd


    【解决方案1】:

    这是封面计算背后的逻辑。

    你有四个基本值:

    imgWidth // your original img width
    imgHeight
    
    containerWidth // your container  width (here 698px)
    containerHeight
    

    从这些值得出的两个比率:

    imgRatio = (imgHeight / imgWidth)       // original img ratio
    containerRatio = (containerHeight / containerWidth)     // container ratio
    

    你想找到两个新值:

    finalWidth // the scaled img width
    finalHeight
    

    所以:

    if (containerRatio > imgRatio) 
    {
        finalHeight = containerHeight
        finalWidth = (containerHeight / imgRatio)
    } 
    else 
    {
        finalWidth = containerWidth 
        finalHeight = (containerWidth / imgRatio)
    }
    

    ... 你有一个 background-size 的等价物:封面。

    【讨论】:

    • 不知道为什么,但我必须将最后一行更改为 finalHeight = (containerWidth * imgRatio) 才能正常工作
    • @Ergec,没错! imgRatioheight / width。我们需要将height / width 乘以width 得到height。 @mddw,请修正你的答案,请
    • 这是官方实现还是逆向工程?
    【解决方案2】:

    我知道这是一个非常古老的问题,但我写的答案实际上更简洁,通过在图像之间的比率上使用 max 和 mins 而不是每个图像本身:

    var originalRatios = {
      width: containerWidth / imageNaturalWidth,
      height: containerHeight / imageNaturalHeight
    };
    
    // formula for cover:
    var coverRatio = Math.max(originalRatios.width, originalRatios.height); 
    
    // result:
    var newImageWidth = imageNaturalWidth * coverRatio;
    var newImageHeight = imageNaturalHeight * coverRatio;
    

    我喜欢这种方法,因为它非常系统化——也许用词不当——。我的意思是你可以去掉if 语句,让它以更“数学公式”的方式工作(输入=输出,如果有意义的话):

    var ratios = {
      cover: function(wRatio, hRatio) {
        return Math.max(wRatio, hRatio);
      },
    
      contain: function(wRatio, hRatio) {
        return Math.min(wRatio, hRatio);
      },
    
      // original size
      "auto": function() {
        return 1;
      },
    
      // stretch
      "100% 100%": function(wRatio, hRatio) {
        return { width:wRatio, height:hRatio };
      }
    };
    
    function getImageSize(options) {
      if(!ratios[options.size]) {
        throw new Error(options.size + " not found in ratios");
      }
    
      var r = ratios[options.size](
        options.container.width / options.image.width,
        options.container.height / options.image.height
      );
    
      return {
        width: options.image.width * (r.width || r),
        height: options.image.height * (r.height || r)
      };
    }
    

    我创建了一个 jsbin here 如果你想看看我对 systematic 的意思(它还有一个我认为在这个答案中不需要的 scale 方法但对平常以外的东西非常有用)。

    【讨论】:

      【解决方案3】:

      当使用background-size: cover时,它被缩放到覆盖整个背景的最小尺寸。

      因此,在它比高度更薄的地方,缩放它直到它的宽度与该区域相同。在它比它薄的地方高,缩放它直到它的高度与该区域相同。

      当它大于要覆盖的区域时,将其缩小直到适合(如果溢出的高度较少,则缩放到相同的高度,如果溢出的宽度较少,则缩放到相同的宽度)。

      【讨论】:

        【解决方案4】:

        感谢 mdi 为我指明了正确的方向,但这似乎不太正确。 这是对我有用的解决方案:

            $imgRatio = $imageHeight / $imageWidth;
            $canvasRatio = $canvasHeight / $canvasWidth;
        
            if ($canvasRatio > $imgRatio) {
                $finalHeight = $canvasHeight;
                $scale = $finalHeight / $imageHeight;
                $finalWidth = round($imageWidth * $scale , 0);
            } else {
                $finalWidth = $canvasWidth;
                $scale = $finalWidth / $imageWidth;
                $finalHeight = round($imageHeight * $scale , 0);
            }
        

        【讨论】:

          【解决方案5】:

          在长时间搜索如何在 div 上缩放和定位背景图像以匹配 html 背景图像同时还支持浏览器调整大小和 div 的临时定位的方法后,我偶然发现了这个 QA,我想出了这个。

          :root {
            /* background image size (source) */
          
            --bgw: 1920;
            --bgh: 1080;
          
            /* projected background image size and position */
          
            --bgscale: max(calc(100vh / var(--bgh)), calc(100vw / var(--bgw)));
          
            --pbgw: calc(var(--bgw) * var(--bgscale)); /* projected width */
            --pbgh: calc(var(--bgh) * var(--bgscale)); /* projected height */
          
            --bgLeftOverflow: calc((var(--pbgw) - 100vw) / 2); 
            --bgTopOverflow: calc((var(--pbgh) - 100vh) / 2);
          }
          

          JS 等效

          window.onresize = () => {
            const vw100 = window.innerWidth
            const vh100 = window.innerHeight
          
            /* background image size (source) */
          
            const bgw = 1920
            const bgh = 1080
          
            /* projected background image size and position */
          
            const bgscale = Math.max(vh100 / bgh, vw100 / bgw)
          
            const projectedWidth  = bgw * bgscale | 0
            const projectedHeight = bgh * bgscale | 0
          
            const leftOverflow = (projectedWidth  - vw100) / 2 | 0
            const topOverflow  = (projectedHeight - vh100) / 2 | 0
          
            console.log(bgscale.toFixed(2), projectedWidth, projectedHeight, leftOverflow, topOverflow)
          }
          

          尝试使用此 sn-p 调整窗口大小以查看结果。
          整页视图中查看效果最佳。提示:打开控制台。

          window.onresize = () => {
            const vw100 = window.innerWidth
            const vh100 = window.innerHeight
            const bgw = 1920
            const bgh = 1080
          
            const bgscale = Math.max(vh100 / bgh, vw100 / bgw)
          
            const projectedWidth = bgw * bgscale | 0
            const projectedHeight = bgh * bgscale | 0
          
            const leftOverflow = (projectedWidth - vw100) / 2 | 0
            const topOverflow = (projectedHeight - vh100) / 2 | 0
          
            console.log(bgscale.toFixed(2), projectedWidth, projectedHeight, leftOverflow, topOverflow)
          }
          :root {
            /* background image size */
            --bgurl: url('https://i.stack.imgur.com/3iy4y.jpg');
            --bgw: 1000;
            --bgh: 600;
            
            --bgscale: max(calc(100vh / var(--bgh)), calc(100vw / var(--bgw)));
            
            --pbgw: calc(var(--bgw) * var(--bgscale));
            --pbgh: calc(var(--bgh) * var(--bgscale));
            
            --bgLeftOverflow: calc((var(--pbgw) - 100vw) / 2);
            --bgTopOverflow: calc((var(--pbgh) - 100vh) / 2);
          }
          
          html {
            background: #000 var(--bgurl) no-repeat center center fixed;
            background-size: cover;
            overflow: hidden;
          }
          
          #panel {
            --x: 100px;
            --y: 100px;
            --w: 200px;
            --h: 150px;
            
            position: absolute;
            left: var( --x);
            top: var( --y);
            width: var(--w);
            height: var(--h);
            
            background-image: var(--bgurl);
            background-repeat: no-repeat;
            background-position: calc(0px - var(--bgLeftOverflow) - var(--x)) calc(0px - var(--bgTopOverflow) - var(--y));
            background-size: calc(var( --bgscale) * var(--bgw));
            filter: invert(1);
          }
          <div id="panel"></div>

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-02-15
            • 1970-01-01
            • 2019-01-04
            • 1970-01-01
            • 2021-02-15
            • 1970-01-01
            • 2021-09-21
            • 2012-08-23
            相关资源
            最近更新 更多