你可能听说过了,普通的老attr()
方法在CSS中刚刚得到了一个重大更新(详情)。目前仅在Chrome浏览器中可用,但我猜Firefox和Safari团队也在为此更新努力实现中!
所以有什么区别呢?那么,我们现在有 类型 和 备用值 ,所以我们可以直接将 CSS 自定义属性设置为数值,并设置一个备用值:
--property: attr(my-attr type<number>, 15); /* 设置属性为my-attr的数字类型值为15 */
全屏模式, 退出全屏
这也太酷了吧!这意味着我们现在可以创建非常复杂的纯CSS组件。在这篇教程中,我们将仅用纯CSS来构建一个<moon-phase>
组件!
首先,我们来创建一个简单的自定义元素,叫做 <moon-phase>
。我们给它添加两个属性:illumination
和 phase
。
有效的阶段包括:
- 新月
- 上弦月
- 盈凸相
- 盈凸
- 满月
- 亏凸
- 亏凸相
- 下弦月
例如:
<moon-phase
illumination="光照百分比为58%"
phase="渐亏的上弦月">
</moon-phase>
光照百分比为58%,处于渐亏的上弦月阶段。
切换到全屏模式,切换回正常模式
就这样了……(先这样)。我们来试试(CSS,层叠样式表)吧!
## CSS (层叠样式表)
我们的custom element需要几个默认样式。
月亮相位 {
aspect-ratio: 1;
border-radius: 50%;
display: block;
overflow: 裁剪;
position: relative;
}
全屏模式 退出全屏
我们在 `::before` 伪元素中添加了月亮的图像
/ 这段代码用来设置月亮图片的背景,通过:before伪元素来展示/
moon-phase::before {
background: url('moon.png') center / cover no-repeat;
content: '';
inset: 0;
position: absolute;
}
全屏模式 退出全屏 撤出
于是我们得到了:

真没劲!加个`filter`让它有点意思,
moon-phase::before {
filter: sepia(1) grayscale(.25);
}
切换到全屏,退出全屏
我们现在是这样的。

好多了!如果你想玩玩CSS滤镜,我做了一个简单的编辑器。[https://codepen.io/stoumann/pen/MWeNmyb](https://codepen.io/stoumann/pen/MWeNmyb)。
现在,我们在后面加上一个 ` ::after ` 伪元素哦。
moon-phase::after {
background-color: #000;
border-radius:
var(--_btlr, 0)
var(--_btrr, 0)
var(--_bbrr, 0)
var(--_bblr, 0);
content: '';
height: 100%;
inset-inline: var(--_ii, auto 0);
position: absolute;
width: var(--_w, 0%);
}
这是一段CSS代码,用于定义`moon-phase::after`伪元素的样式。该伪元素的背景颜色为黑色,边角半径由变量定义,内容为空,高度为100%,内边距根据变量设置,绝对定位,宽度由变量定义。
点击全屏进入 全屏模式 点击退出退出 全屏模式
呼,这个可真是要好好想想!我们增加了四个属性来控制 `border-radius` 的各个边角,还有一个属性是用来控制 `width` 的。那我们就从这里开始吧:
/* 月相样式 */
moon-phase {
--_w: calc(100% - 1% * attr(illumination type(<number>), 0%));
}
/* 注释:这里的 `moon-phase` 是用来定义月相,`calc` 函数用于计算月相的宽度。 */
进入全屏 退出全屏
**那到底是怎么回事呢?** 我们将 `illumination` 属性读取为一个数值,通过乘以 `1%`,然后将其转换成百分比,再从全宽中减去这个数值。
所以,如果将光照值 `illumination` 设为 `6%`,则宽度值将是 `94%`,依此类推。
接下来,我们要根据月亮的`phase`来调整`border-radius`属性和`inset`,如下所示:
[phase="第一四分之一"],
[phase="上弦"] {
--_ii: 0 auto;
}
[phase="新月"],
[phase="第一四分之一"],
[phase="上弦"] {
--_bblr: 100%;
--_btlr: 100%;
}
[phase="新月"],
[phase="最后四分之一"],
[phase="下弦"] {
--_btrr: 100%;
--_bbrr: 100%;
}
[phase*="盈凸"]::after {
border-radius: 0;
width: 100%;
}
全屏;退出全屏
来看看我们现在的情况如何,目前的照明率为6%。

哇,一片薄薄的月亮!
现在,在“凸月”阶段,由于形状是向外的,我们不能使用 `border-radius` 属性。
相反,`::after` 元素占满 `100%`,并通过 `mask` 裁剪。
[phase="盈凸相"]::after {
mask: radial-gradient(circle at 100% 50%,
#0000 calc(100% - var(--_w)),
#000 calc(100% - var(--_w) + 1px 100%));
}
[phase="亏凸相"]::after {
mask: radial-gradient(circle at 0% 50%,
#0000 calc(100% - var(--_w)),
#000 calc(100% - var(--_w) + 1px 100%));
}
全屏模式 退出全屏
当月光达到**58%亮度**时,我们得到:

## 纬度与时间
现在,月亮在地球上看起来会因为你的位置不同而有所差异,所以我们给这个组件增加两个新属性。
<moon-phase
illumination="25"
phase="上弦"
lat="-33.86"
hour="22">
</moon-phase>
全屏,退出全屏
和之前一样,我们直接在 CSS 中读取它们。
moon-phase {
/* 月相 */
--_lat: attr(lat type(<number>), 0);
/* 纬度 */
--_hour: attr(hour type(<number>), 12);
/* 小时 */
}
进入全屏 / 退出全屏
计算旋转角度所需的步骤有点复杂:
月相计算:_l 变量通过将纬度乘以 1.5 度计算得出;_a 变量通过将小时数减去 12 后乘以 15 和 0.7 再乘以 1 度计算得出;_r 变量则是通过将 _l 和 _a 相加得出。
全屏模式 退出全屏
我们来拆解一下这个:
1. **纬度倾斜 (`--_l`)**
我们将纬度放大1.5倍来模拟月亮倾斜随位置北移或南移的变化。这样就会:
* **南半球(负纬度)向上倾斜。**
* **北半球向下倾斜。**
2. **小时旋转 (`--_a`)**
其原理如下:计算 `calc(((var(--_hour) - 12) * 15 * 0.7) * 1deg)`
* `(var(--_hour) - 12)`:将旋转中心对准正午太阳。
* `* 15`:地球每小时自转15度(太阳时)。
* `* 0.7`:减弱效果以匹配月球较慢的视运动速度(约14.5度/小时)。
* `* 1deg`:转换为度。
3. **结合旋转 (`--_r`)**
将 `--_l` 和 `--_a` 相加以获得更真实的方位。例如以下情况:
* **赤道(纬度=0)**:垂直线(🌒)。
* **悉尼(纬度:-34)**:向右倾斜(/)。
* **伦敦(纬度:51.5)**:向左倾斜(\\)。
* **北极(纬度=90)**:水平线(⊐)。
moon-phase {
rotate: var(--_r, 0deg); /* 设置最终的旋转角度 */
}
全屏显示 退出全屏
咱们来看个例子:同一天,但不同的纬度下:

> 一个小提示:在极端纬度(比如大于80°)时,倾斜计算会变得不那么准确(在这个公式下,月球在极地不会完全平躺)。另外——尽管我尽力让行为符合实际情况——我不是天体物理学家。如果你发现了计算中的错误,就知道该找谁算账了!
## 示例
这里有一个 Codepen,包含了月亮的不同形状;它完全是用 CSS 编写的,但目前仅在 Chrome 中有效:
共同學(xué)習(xí),寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章