为所有Material Design的高程计算阴影值

26
在最新的Material Design文档中(https://www.google.com/design/spec/what-is-material/elevation-shadows.html#elevation-shadows-elevation-android-),详尽列出了一组UI元素及其相应的高度(以dp为单位)。例如,开关的高度为1dp,而对话框的高度为24dp。目前,Google的UI元素列表使用了10个不同的高度级别。由于高度决定了元素的阴影,我们需要10种不同的阴影。这就是我迷茫的地方。
如何计算/推导每个高度级别的正确阴影值(颜色、x偏移量、y偏移量、模糊、扩散)?
我已经发现了不同的来源,他们计算了5个不同高度的阴影值(https://news.layervault.com/stories/42319-calculating-shadow-values-for-material-design)。然而,5个高度步骤是不够的,也没有解释他们是如何得出这些相应的值的。

-叹气-真遗憾没有人回答这个问题... - mashedpotats
@MashedPotatoes,请查看Troncoso的答案。 - Joshua Kifer
8个回答

25

好消息!我获得了从深度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); }

1
我想在Android中使用它,但不知道如何阅读它?我该如何阅读它?例如:当我们采用任何dp时,左侧,顶部,右侧,底部px和颜色的阴影会是什么样子。 - Saran Sankaran
它帮了很大的忙。 - Gratien Asimbahwe
@SaranSankaran,有四个属性:h-offset v-offset blur spread color,你可以通过用逗号分隔它们在每个元素上拥有多个属性,就像上面的例子一样。更多信息请参见https://www.w3schools.com/cssref/css3_pr_box-shadow.asp。 - Streching my competence

15

这里是我想出来的一个函数,我相信它会给出不错的结果:

(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)";
}

一个jsfiddle:https://jsfiddle.net/crkb906z/

基本上,从基层开始的距离是阴影顶部向下延伸的距离,我使用相同的值来模糊它,使材料越高越模糊。

我将此结果与聚合物使用的3个阴影进行了比较,它们非常相似。由于我甚至找不到大多数人提供的5层阴影之间的一致性,所以我想确切计算方式并不重要。

编辑

好的,在研究了可用的5个深度的盒子阴影后(它们源自这里,但谷歌已经将其从文档中删除),我想出了一个更复杂的公式,可以得到看起来更像该链接示例的阴影:

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 很棒。正是我所需要的! - Anthony Wijnen
1
directY的计算可以简化为dp < 10 ? Math.floor(dp / 2) + 1 : dp - 4。请不要使用嵌套的三元运算符 :) - Lucius

5
我只发表这个答案是因为Google官方的MDC Web高度相关内容与迄今为止发布的不同。以下是我复制的源文件:https://github.com/material-components/material-components-web/blob/e02b4c9fa2bd026c695f3a71efce58d01f460269/packages/mdc-elevation/_variables.scss。Google官方的高程(SASS mixin)box-shadow值:
//
// 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};
  `
);

最常见的答案 - Luke
我想知道的是他们是根据什么选择这些值的...哈哈 - Bogdan

3
这里是将Troncoso的公式移植到Stylus的结果。
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)

2

有一个针对此的scss库:SCSS-Material-Shadows

这是演示

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

主要使用的是名为mdElevation()的mixin。 mdElevationElement()是所有Material元素的预定义高程值的方便实现,可以在此处找到

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

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


1
以下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>`;
<div id="app"></div>

它使用海拔的第一和最后(第24级)水平的值。

1

我从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);
}

0
这是一个稍微修改过的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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接