【问题标题】:Calculating shadow values for all Material Design elevations计算所有 Material Design 高程的阴影值
【发布时间】:2015-08-12 12:36:28
【问题描述】:

在最新的 Material Design 文档 (https://www.google.com/design/spec/what-is-material/elevation-shadows.html#elevation-shadows-elevation-android-) 中,引用了一组详尽的 UI 元素及其各自的高度(z-index in dp)。例如,switch 提升 1dp,而 dialog 提升 24dp。目前 Google 的 UI 元素列表使用 10 种不同的海拔高度。由于高程决定了元素的阴影,我们需要 10 个不同的阴影。这就是我迷路的地方。

您如何计算/推断每个高程级别的正确阴影值(颜色、x 偏移、y 偏移、模糊、散布)?

我找到了计算 5 个不同高度的阴影值的不同来源 (https://news.layervault.com/stories/42319-calculating-shadow-values-for-material-design)。但是,5 个海拔台阶是不够的,它们也没有解释它们是如何达到这些各自的值的。

【问题讨论】:

  • -sigh- 很遗憾没有人回答这个问题...
  • @MashedPotatoes 检查 Troncoso 的答案。

标签: material-design shadow box-shadow


【解决方案1】:

这是我想出的一个函数,我相信它会产生很好的结果:

(Javascript)

function getShadow(object, dp)
{
    if (dp <= 0)
    {
        panel.style.boxShadow = "none";
        return;
    }

    panel.style.boxShadow = "0px " + dp + "px " + dp + "px " + "rgba(0, 0, 0, .38)";
}

一个小提琴手:https://jsfiddle.net/crkb906z/

基本上,与基础层的距离是阴影顶部的距离,我使用相同的模糊值来使其随着材质变得越来越模糊。

我将这个结果与聚合物使用的 3 个阴影进行了比较,它们非常具有可比性。由于我什至无法找到大多数人提供的 5 层阴影之间的一致性,我想它的确切计算方式并不那么重要。

编辑

好的,在研究了 5 个深度的可用框阴影之后(它们源自 here,但 Google 已从文档中删除了它),我想出了一个更复杂的公式,它会导致阴影看起来更像该链接中的示例:

function applyShadow(element, dp)
{
    if (dp == 0)
    {
        element.style.boxShadow = "none";
    }
    else
    {
        var shadow = "0px ";

        var ambientY = dp;
        var ambientBlur = dp == 1 ? 3 : dp * 2;
        var ambientAlpha = (dp + 10 + (dp / 9.38)) / 100;

        shadow += ambientY + "px " + ambientBlur + "px rgba(0, 0, 0, " + ambientAlpha + "), 0px ";

        var directY = (dp < 10 ? (dp % 2 == 0 ? dp - ((dp / 2) - 1) : (dp - ((dp - 1) / 2))) : dp - 4);
        var directBlur = dp == 1 ? 3 : dp * 2;
        var directAlpha = (24 - Math.round(dp / 10)) / 100;

        shadow += directY + "px " + directBlur + "px rgba(0, 0, 0, " + directAlpha + ")";

        element.style.boxShadow  = shadow;
    }
}

我更新https://jsfiddle.net/crkb906z/1/ 以显示差异。

【讨论】:

  • @Troncoso 很棒的东西。正是我需要的!
  • directY的计算可以简化为dp &lt; 10 ? Math.floor(dp / 2) + 1 : dp - 4不要使用嵌套的三元运算符 :)
【解决方案2】:

这是移植到 Stylus 的 Troncoso 公式。

elevation(dp)
  if dp == 0
    box-shadow none
  else
    dp = unit(dp, px)
    blur = (dp == 1 ? 3 : dp * 2)
    amba = (dp + 10 + (dp / 9.38)) / 100
    diry = (dp < 10 ? (dp % 2 == 0 ? dp - ((dp / 2) - 1) : (dp - ((dp - 1) / 2))) : dp - 4)
    dira = (24 - (round(dp / 10))) / 100
    box-shadow 0px dp blur rgba(0, 0, 0, amba), 0px diry blur rgba(0, 0, 0, dira)

【讨论】:

    【解决方案3】:

    我从 Google 的 Material Design Lite 获得了一些影子。我认为这段代码可能会有所帮助。

    .shadow--2dp {
        box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
    }
    
    .shadow--3dp {
        box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12);
    }
    
    .shadow--4dp {
        box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
    }
    
    .shadow--6dp {
        box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2);
    }
    
    .shadow--8dp {
        box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);
    }
    
    .shadow--16dp {
        box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2);
    }
    
    .shadow--24dp {
        box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
    }
    

    【讨论】:

      【解决方案4】:

      有一个好消息!我得到了从深度 1 到深度 24 的所有阴影深度。我从Angular Material 得到这个。希望这会对你有所帮助。

      .md-whiteframe-1dp {
          box-shadow:  0px 1px 3px 0px rgba(0, 0, 0, 0.2),
          0px 1px 1px 0px rgba(0, 0, 0, 0.14),
          0px 2px 1px -1px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-2dp {
          box-shadow:  0px 1px 5px 0px rgba(0, 0, 0, 0.2),
          0px 2px 2px 0px rgba(0, 0, 0, 0.14),
          0px 3px 1px -2px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-3dp {
          box-shadow:0px 1px 8px 0px rgba(0, 0, 0, 0.2),
          0px 3px 4px 0px rgba(0, 0, 0, 0.14),
          0px 3px 3px -2px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-4dp {
          box-shadow: 0px 2px 4px -1px rgba(0, 0, 0, 0.2),
          0px 4px 5px 0px rgba(0, 0, 0, 0.14),
          0px 1px 10px 0px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-5dp {
          box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
          0px 5px 8px 0px rgba(0, 0, 0, 0.14),
          0px 1px 14px 0px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-6dp {
          box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
          0px 6px 10px 0px rgba(0, 0, 0, 0.14),
          0px 1px 18px 0px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-7dp {
          box-shadow: 0px 4px 5px -2px rgba(0, 0, 0, 0.2),
          0px 7px 10px 1px rgba(0, 0, 0, 0.14),
          0px 2px 16px 1px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-8dp {
          box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2),
          0px 8px 10px 1px rgba(0, 0, 0, 0.14),
          0px 3px 14px 2px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-9dp {
          box-shadow: 0px 5px 6px -3px rgba(0, 0, 0, 0.2),
          0px 9px 12px 1px rgba(0, 0, 0, 0.14),
          0px 3px 16px 2px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-10dp {
          box-shadow: 0px 6px 6px -3px rgba(0, 0, 0, 0.2),
          0px 10px 14px 1px rgba(0, 0, 0, 0.14),
          0px 4px 18px 3px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-11dp {
          box-shadow: 0px 6px 7px -4px rgba(0, 0, 0, 0.2),
          0px 11px 15px 1px rgba(0, 0, 0, 0.14),
          0px 4px 20px 3px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-12dp {
          box-shadow: 0px 7px 8px -4px rgba(0, 0, 0, 0.2),
          0px 12px 17px 2px rgba(0, 0, 0, 0.14),
          0px 5px 22px 4px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-13dp {
          box-shadow: 0px 7px 8px -4px rgba(0, 0, 0, 0.2),
          0px 13px 19px 2px rgba(0, 0, 0, 0.14),
          0px 5px 24px 4px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-14dp {
          box-shadow: 0px 7px 9px -4px rgba(0, 0, 0, 0.2),
          0px 14px 21px 2px rgba(0, 0, 0, 0.14),
          0px 5px 26px 4px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-15dp {
          box-shadow: 0px 8px 9px -5px rgba(0, 0, 0, 0.2),
          0px 15px 22px 2px rgba(0, 0, 0, 0.14),
          0px 6px 28px 5px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-16dp {
          box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.2),
          0px 16px 24px 2px rgba(0, 0, 0, 0.14),
          0px 6px 30px 5px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-17dp {
          box-shadow: 0px 8px 11px -5px rgba(0, 0, 0, 0.2),
          0px 17px 26px 2px rgba(0, 0, 0, 0.14),
          0px 6px 32px 5px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-18dp {
          box-shadow: 0px 9px 11px -5px rgba(0, 0, 0, 0.2),
          0px 18px 28px 2px rgba(0, 0, 0, 0.14),
          0px 7px 34px 6px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-19dp {
          box-shadow: 0px 9px 12px -6px rgba(0, 0, 0, 0.2),
          0px 19px 29px 2px rgba(0, 0, 0, 0.14),
          0px 7px 36px 6px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-20dp {
          box-shadow: 0px 10px 13px -6px rgba(0, 0, 0, 0.2),
          0px 20px 31px 3px rgba(0, 0, 0, 0.14),
          0px 8px 38px 7px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-21dp {
          box-shadow: 0px 10px 13px -6px rgba(0, 0, 0, 0.2),
          0px 21px 33px 3px rgba(0, 0, 0, 0.14),
          0px 8px 40px 7px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-22dp {
          box-shadow: 0px 10px 14px -6px rgba(0, 0, 0, 0.2),
          0px 22px 35px 3px rgba(0, 0, 0, 0.14),
          0px 8px 42px 7px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-23dp {
          box-shadow: 0px 11px 14px -7px rgba(0, 0, 0, 0.2),
          0px 23px 36px 3px rgba(0, 0, 0, 0.14),
          0px 9px 44px 8px rgba(0, 0, 0, 0.12); }
      .md-whiteframe-24dp {
          box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
          0px 24px 38px 3px rgba(0, 0, 0, 0.14),
          0px 9px 46px 8px rgba(0, 0, 0, 0.12); }
      

      【讨论】:

      • 我想在 Android 中使用它,但不明白如何阅读?我怎么能读到这个。例如:当我们采取任何 dp 时,左、上、右、下 px 的阴影和颜色将是什么。
      • 帮助很大
      • @SaranSankaran 有四个属性:h-offset v-offset blur spread color,您可以在每个元素上使用逗号分隔多个属性,如上例所示。更多信息请访问w3schools.com/cssref/css3_pr_box-shadow.asp
      【解决方案5】:

      这是一个稍加修改的 Sass 端口:

      @mixin shadow($dp: 1) {
          @if $dp == 0 { box-shadow: none; }
          @else {
              $blur: if($dp == 1, 3, $dp * 2);
              $amba: ($dp + 11) / 100;
              $diry: $dp - floor($dp / 4);
              $dirb: floor(($dp + 5) / 2);
              $dira: (24 - (round($dp / 10))) / 100;
              box-shadow: 0px #{$dp + 'px'} #{$blur + 'px'} rgba(0, 0, 0, $amba), 0px #{$diry + 'px'} #{$dirb + 'px'} rgba(0, 0, 0, $dira);
          }
      }
      

      【讨论】:

        【解决方案6】:

        我只发布此答案是因为 Google 的官方 MDC Web 提升内容与目前发布的内容不同。

        这是我要复制的源文件:

        https://github.com/material-components/material-components-web/blob/e02b4c9fa2bd026c695f3a71efce58d01f460269/packages/mdc-elevation/_variables.scss

        Google 的官方box-shadow 海拔值(SASS 混合):

        //
        // Copyright 2017 Google Inc. All Rights Reserved.
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        //
        
        @import "@material/animation/variables";
        
        $mdc-elevation-baseline-color: black;
        $mdc-elevation-umbra-opacity: .2;
        $mdc-elevation-penumbra-opacity: .14;
        $mdc-elevation-ambient-opacity: .12;
        
        $mdc-elevation-umbra-map: (
          0: "0px 0px 0px 0px",
          1: "0px 2px 1px -1px",
          2: "0px 3px 1px -2px",
          3: "0px 3px 3px -2px",
          4: "0px 2px 4px -1px",
          5: "0px 3px 5px -1px",
          6: "0px 3px 5px -1px",
          7: "0px 4px 5px -2px",
          8: "0px 5px 5px -3px",
          9: "0px 5px 6px -3px",
          10: "0px 6px 6px -3px",
          11: "0px 6px 7px -4px",
          12: "0px 7px 8px -4px",
          13: "0px 7px 8px -4px",
          14: "0px 7px 9px -4px",
          15: "0px 8px 9px -5px",
          16: "0px 8px 10px -5px",
          17: "0px 8px 11px -5px",
          18: "0px 9px 11px -5px",
          19: "0px 9px 12px -6px",
          20: "0px 10px 13px -6px",
          21: "0px 10px 13px -6px",
          22: "0px 10px 14px -6px",
          23: "0px 11px 14px -7px",
          24: "0px 11px 15px -7px"
        );
        
        $mdc-elevation-penumbra-map: (
          0: "0px 0px 0px 0px",
          1: "0px 1px 1px 0px",
          2: "0px 2px 2px 0px",
          3: "0px 3px 4px 0px",
          4: "0px 4px 5px 0px",
          5: "0px 5px 8px 0px",
          6: "0px 6px 10px 0px",
          7: "0px 7px 10px 1px",
          8: "0px 8px 10px 1px",
          9: "0px 9px 12px 1px",
          10: "0px 10px 14px 1px",
          11: "0px 11px 15px 1px",
          12: "0px 12px 17px 2px",
          13: "0px 13px 19px 2px",
          14: "0px 14px 21px 2px",
          15: "0px 15px 22px 2px",
          16: "0px 16px 24px 2px",
          17: "0px 17px 26px 2px",
          18: "0px 18px 28px 2px",
          19: "0px 19px 29px 2px",
          20: "0px 20px 31px 3px",
          21: "0px 21px 33px 3px",
          22: "0px 22px 35px 3px",
          23: "0px 23px 36px 3px",
          24: "0px 24px 38px 3px"
        );
        
        $mdc-elevation-ambient-map: (
          0: "0px 0px 0px 0px",
          1: "0px 1px 3px 0px",
          2: "0px 1px 5px 0px",
          3: "0px 1px 8px 0px",
          4: "0px 1px 10px 0px",
          5: "0px 1px 14px 0px",
          6: "0px 1px 18px 0px",
          7: "0px 2px 16px 1px",
          8: "0px 3px 14px 2px",
          9: "0px 3px 16px 2px",
          10: "0px 4px 18px 3px",
          11: "0px 4px 20px 3px",
          12: "0px 5px 22px 4px",
          13: "0px 5px 24px 4px",
          14: "0px 5px 26px 4px",
          15: "0px 6px 28px 5px",
          16: "0px 6px 30px 5px",
          17: "0px 6px 32px 5px",
          18: "0px 7px 34px 6px",
          19: "0px 7px 36px 6px",
          20: "0px 8px 38px 7px",
          21: "0px 8px 40px 7px",
          22: "0px 8px 42px 7px",
          23: "0px 9px 44px 8px",
          24: "0px 9px 46px 8px"
        );
        
        /**
         * The css property used for elevation. In most cases this should not be changed. It is exposed
         * as a variable for abstraction / easy use when needing to reference the property directly, for
         * example in a `will-change` rule.
         */
        $mdc-elevation-property: box-shadow !default;
        
        /**
         * The default duration value for elevation transitions.
         */
        $mdc-elevation-transition-duration: 280ms !default;
        
        /**
         * The default easing value for elevation transitions.
         */
        $mdc-elevation-transition-timing-function: $mdc-animation-standard-curve-timing-function !default;
        

        这是我根据以上内容整理的一个香草 ES6 elevation 函数:

        const umbraColor = 'rgba(0, 0, 0, 0.2)';
        const penumbraColor = 'rgba(0, 0, 0, 0.14)';
        const ambientColor = 'rgba(0, 0, 0, 0.12)';
        
        
        const umbra = [
          "0px 0px 0px 0px",
          "0px 2px 1px -1px",
          "0px 3px 1px -2px",
          "0px 3px 3px -2px",
          "0px 2px 4px -1px",
          "0px 3px 5px -1px",
          "0px 3px 5px -1px",
          "0px 4px 5px -2px",
          "0px 5px 5px -3px",
          "0px 5px 6px -3px",
          "0px 6px 6px -3px",
          "0px 6px 7px -4px",
          "0px 7px 8px -4px",
          "0px 7px 8px -4px",
          "0px 7px 9px -4px",
          "0px 8px 9px -5px",
          "0px 8px 10px -5px",
          "0px 8px 11px -5px",
          "0px 9px 11px -5px",
          "0px 9px 12px -6px",
          "0px 10px 13px -6px",
          "0px 10px 13px -6px",
          "0px 10px 14px -6px",
          "0px 11px 14px -7px",
          "0px 11px 15px -7px"
        ];
        
        const penumbra = [
          "0px 0px 0px 0px",
          "0px 1px 1px 0px",
          "0px 2px 2px 0px",
          "0px 3px 4px 0px",
          "0px 4px 5px 0px",
          "0px 5px 8px 0px",
          "0px 6px 10px 0px",
          "0px 7px 10px 1px",
          "0px 8px 10px 1px",
          "0px 9px 12px 1px",
          "0px 10px 14px 1px",
          "0px 11px 15px 1px",
          "0px 12px 17px 2px",
          "0px 13px 19px 2px",
          "0px 14px 21px 2px",
          "0px 15px 22px 2px",
          "0px 16px 24px 2px",
          "0px 17px 26px 2px",
          "0px 18px 28px 2px",
          "0px 19px 29px 2px",
          "0px 20px 31px 3px",
          "0px 21px 33px 3px",
          "0px 22px 35px 3px",
          "0px 23px 36px 3px",
          "0px 24px 38px 3px"
        ];
        
        const ambient = [
          "0px 0px 0px 0px",
          "0px 1px 3px 0px",
          "0px 1px 5px 0px",
          "0px 1px 8px 0px",
          "0px 1px 10px 0px",
          "0px 1px 14px 0px",
          "0px 1px 18px 0px",
          "0px 2px 16px 1px",
          "0px 3px 14px 2px",
          "0px 3px 16px 2px",
          "0px 4px 18px 3px",
          "0px 4px 20px 3px",
          "0px 5px 22px 4px",
          "0px 5px 24px 4px",
          "0px 5px 26px 4px",
          "0px 6px 28px 5px",
          "0px 6px 30px 5px",
          "0px 6px 32px 5px",
          "0px 7px 34px 6px",
          "0px 7px 36px 6px",
          "0px 8px 38px 7px",
          "0px 8px 40px 7px",
          "0px 8px 42px 7px",
          "0px 9px 44px 8px",
          "0px 9px 46px 8px"
        ];
        
        export default (z) => (
          `
            box-shadow: ${umbra[z]} ${umbraColor},
                        ${penumbra[z]} ${penumbraColor},
                        ${ambient[z]} ${ambientColor};
          `
        );
        

        【讨论】:

        • 最规范的答案
        • 我想知道他们是如何选择这些值的……基于什么。 :D
        【解决方案7】:

        有一个 scss 库:SCSS-Material-Shadows

        这里是demo

        您可以在 scss 文件中查看整个计算。

        主要使用一个名为mdElevation() 的mixin。 mdElevationElement() 是对所有 here 找到的 Material 元素的预定义高程值的便捷实现

        对于高程过渡期间的动态效果,您可以在 scss 中添加mdElevationTransition()。作为参数,使用两个海拔之间的增量。

        有关更多详细信息,请访问 github 页面。

        【讨论】:

          【解决方案8】:

          以下 JS 函数接收海拔高度,并返回非常接近 Google 的 Material 组件的结果。

          export const getShadow = (elevation=1) => {
              const extraEl = elevation-1
              const getSize = (v1, v24) => `${v1+Math.round(((v24-v1)/23)*extraEl)}px`
              const getShade = (y, blur, spread, alpha) => 
                  `0 ${getSize(y[0], y[1])} ${getSize(blur[0], blur[1])} ${getSize(spread[0], spread[1])} rgba(0,0,0,${alpha})`
          
              return `box-shadow: ${getShade([2,11], [1,15], [-1,-7], 0.2)}, ${getShade([1,24], [1,38], [0,3], 0.14)}, ${getShade([1,9], [3,46], [0,8], 0.12)};`
          }
          

          演示

          const getShadow = (elevation=1) => {
          	const extraEl = elevation-1
          	const getSize = (v1, v24) => `${v1+Math.round(((v24-v1)/23)*extraEl)}px`
          	const getShade = (y, blur, spread, alpha) => 
          		`0 ${getSize(y[0], y[1])} ${getSize(blur[0], blur[1])} ${getSize(spread[0], spread[1])} rgba(0,0,0,${alpha})`
          
          	return `box-shadow: ${getShade([2,11], [1,15], [-1,-7], 0.2)}, ${getShade([1,24], [1,38], [0,3], 0.14)}, ${getShade([1,9], [3,46], [0,8], 0.12)};`
          }
          
          const appDiv = document.getElementById('app');
          appDiv.innerHTML = `<div style="margin: 50px; width: 100px; height: 100px; ${getShadow(12)}">Hello world</div>`;
          &lt;div id="app"&gt;&lt;/div&gt;

          它使用来自第一级和最后(24 级)高程的值。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-03-26
            • 1970-01-01
            • 1970-01-01
            • 2017-09-24
            • 1970-01-01
            • 1970-01-01
            • 2017-03-09
            相关资源
            最近更新 更多