自由样式 (Custom Style)
在自由样式时钟里,你可以通过编写 HTML 和 CSS 代码来设计自己的时钟样式,同时,遵循下面的指南,你可以轻易地将时间信息集成到你设计的时钟当中。
为了更好地理解接下来的内容,你至少需要以下基础知识:
- 能使用、理解 HTML 和 CSS 语言
- 了解自定义元素 (opens in a new tab)的相关特性
现在,开始准备你的代码。第一步,你需要一个 <template>
元素,接下来的所有代码都将在这个 <template>
元素内完成。
<template>
<!-- Write your code here -->
</template>
自由样式时钟的工作流程
自由样式时钟的自定义元素名为 <custom-style>
和其他所有时钟一样,自由样式时钟同样继承于Clock Provider,同样是提供了 init()
和 update()
方法,分别用于初始化时钟和每分钟一次的更新时钟,不同的是,自由样式时钟的 HTML 和 CSS 由用户提供。在初始化时,用户提供的 HTML元素将会被挂载在自由样式时钟内部的 Shadow DOM 中。选择 Shadow Dom 而非 Light Dom,是因为使用插槽(<slot>
)元素是重要的特性之一。当然,使用 Shadow Dom 也有几点不便,比如,为了保证其内部的隔离性,Shadow Dom 中无法解析 @import
@font-face
@property
规则(也许还有更多),下面是 <custom-style>
自身的样式:
<custom-style>
<!-- Shadow DOM -->
<slot></slot>
<style>
:host {
display: block;
width: 100%;
height: 100%;
}
</style>
<!-- Shadow DOM -->
</custom-style>
设计自己的时钟样式
时间字符解析器
Scene Tab 遵循最新的浏览器拓展规范 Manifest V3 设计,Manifest V3 提高了用户环境的安全性,但同时也使得浏览器拓展失去了一部分的灵活性,其中就包括不再允许行内的JavaScript脚本以及从字符串生成的JavaScript脚本,这意味着你无法提交自己的 JavaScript 代码。为此,我们为自由样式时钟设计了一种基于格式化字符的时间字符解析器。
要使用这个解析器,你需要在的HTML元素中添加 data-format
属性,并为其赋予一个或多个字符组合的值。这些字符会被解析器识别并转换为相应的时间单位。例如,假设您有一个HTML元素如下:
<span data-format="d"></span>
解析器将 data-format
属性值 d
识别为当前日期的两位数字。例如,如果今天是2023年2月21日,那么这个元素的 innerHTML 将被替换为"21"。
<!-- 经过解析后的元素 -->
<span data-format="d">21</span>
可以被正确识别并转换的字符将会在下一段落的表格中展示,你可以自由组合 data-format
属性中的字符,无法识别的字符将会原样输出。例如,如果您希望显示当前时间的小时和分钟,可以将data-format属性设置为 h:i
<span data-format="h:i"></span>
在这个例子中,H
和 i
是可以被识别的字符,分别代表小时和分钟,而 :
并不是可识别的字符,因此,这个 HTML 元素会被解析为:
<!-- 假设现在时间是上午十时零九分 -->
<span data-format="h:i">10:09</span>
我们可以再给出一个例子:
<span data-format="H:i A, d F Y"></span>
<!-- 解析后的代码 -->
<span data-format="H:i A, d F Y">10:09 AM, 21 February 2023</span>
特别的,如果想要原样输出能够识别的字符,使用反斜杠 \
来标记下一位字符:
<span data-format="\D d"></span>
<!-- 解析后的代码 -->
<span data-format="\D d">D 21</span>
支持的时间格式字符
时间字符串解析器的设计灵感来源于 PHP DateTime::format
(opens in a new tab) 方法,因此支持的字符格式与 PHP 中的格式大部分相同,具体如下表所示,需要注意的是,部分字符并未做支持(用删除线和红色标记),具体原因会在后文阐述;也有一些字符的使用方法于前文略有区别(用黄色标记),同样将会在后文说明。
format 字符 | 说明 | 返回值示例 |
---|---|---|
日 | --- | --- |
d | 月份中的第几天,有补零的两位数字 | 01 到 31 |
D | 文字表示星期几,三个字母 | Mon 到 Sun |
j | 月份中的第几天,没有补零 | 1 到 31 |
l (小写 'L') | 完整文本表示星期几 | Sunday 到 Saturday |
N | ISO 8601 数字表示星期几 | 1 (星期一)到 7 (星期天) |
S | 月份中的第几天英文后缀,两个字符 | st 、nd 、rd 或 th 。可以和 j 一起使用 |
w | 数字表示星期几 | 0 (星期天)到 6 (星期六) |
z | 一年中的第几天(从 0 开始) | 0 到 365 |
周 | --- | --- |
W | ISO 8601 格式当年中的第几周,每周从周一开始 | 示例:42 (当年的第 42 周) |
月 | --- | --- |
F | 月份的完整文本表示,比如 January 或者 March | January 到 December |
m | 月份的数字表示,补零 | 01 到 12 |
M | 简短文本表示月份,三个字母 | Jan 到 Dec |
n | 数字表示几月份,不补零 | 1 到 12 |
t | 指定月份的天数 | 28 到 31 |
年 | --- | --- |
L | 是否是闰年 | 如果是闰年为 1 ,否则为 0 。 |
o | ISO 8601 数字年份表示。这和 Y 值相同,但如果 ISO 周数(W )属于上一年或者下一年,则用那一年。 | 示例:1999 或 2003 |
X | 年份的展开全数字表示,至少四位,- 表示公元前,+ 表示公元。 | 示例:-0055 、+0787 、 +1999 、+10191 |
x | 如果需要,年份可以展开全数字表示,如果可能的话,也可以用标准的全数字(Y )表示。至少有四位数字。公元前以 - 为前缀,年份不小于 10000 时以 + 为前缀。 | 示例:-0055 , 0787 , 1999 , +10191 |
Y | 年份完整数字表示,至少四位,使用 - 表示公元前。 | 示例:-0055 、0787 、 1999 、2003 、10191 |
y | 两位数的年份表示 | 示例:99 或 03 |
时间 | --- | --- |
a | 小写的上午和下午 | am 或 pm |
A | 大写的上午和下午 | AM 或 PM |
B | Swatch 互联网时间 | 000 到 999 |
g | 不补零的小时(12 小时制) | 1 到 12 |
G | 不补零的小时(24 小时制) | 0 到 23 |
h | 补零的小时(12 小时制) | 01 到 12 |
H | 补零的小时(24 小时制) | 00 到 23 |
i | 补零的分钟 | 00 到 59 |
s | 补零的秒 | 00 到 59 |
u | 微秒。 | 示例:654321 |
v | 毫秒。 | 示例:654 |
时区 | --- | --- |
e | 时区标识符 | 示例:UTC 、GMT 、Atlantic/Azores |
I (大写 i) | 是否为夏令时 | 如果是夏令时为 1 ,否则为 0 。 |
O | 跟格林尼治时间(GMT)的差异,小时和分钟时间没有冒号 | 示例:+0200 |
P | 跟格林尼治时间(GMT)的差异,小时和分钟时间有冒号 | 示例:+02:00 |
p | 跟 P 相同,区别是使用 Z 替换 +00:00 返回(PHP 8.0.0 起可用) | 示例:Z 或 +02:00 |
T | 如果知道会返回时区缩写,否则返回 GMT 时差。 | 示例:EST 、MDT 、+05 |
Z | 以秒为单位的时差。UTC 以西的时区为负的时差,以东为正的时差。 | -43200 到 50400 |
完整日期/时间 | --- | --- |
c | ISO 8601 日期 | 2004-02-12T15:19:21+00:00 |
r | » RFC 2822 (opens in a new tab)/» RFC 5322 (opens in a new tab) 格式化时间 | 示例:Thu, 21 Dec 2000 16:01:07 +0200 |
U | 从 Unix 纪元(January 1 1970 00:00:00 GMT)到至今的秒数 | 参见 time() (opens in a new tab) |
也许你已经注意到,和秒相关的秒 s
、毫秒 v
并没有做完全支持,而微秒 u
、时间戳 U
则并不被支持,这是因为 Scene Tab 设计的时钟信息更新周期为一分钟,不过我们仍然有其他方法实现秒数的展示。
进阶:将时间信息赋予 CSS 变量
上文介绍的将时间信息赋予 HTML 元素的方法简介直观,但仍然有不足:
- 时间信息只能用文本形式展示;
- 无法解析秒
如前文所述,浏览器不再允许在浏览器拓展中执行由用户提交的代码。为了解决上面两点问题,我们还在时间解析器中设计了基于 CSS 变量的工作模式,再加上一点点的 CSS 知识,你将几乎能够在 Scene Tab 中实现所有创意想法。
<!-- 基于 CSS 变量的工作模式 -->
<span data-format="s" data-target="--s"></span>
<!-- 解析后的元素 -->
<span data-format="s" data-target="--s" style="--s: 22;"></span>
只使用 HTML 和 CSS 来实现一个动态变化的时钟通常有几种解决方案:
- 在 HTML 中准备所有时刻的时间元素,通过 CSS 动画来轮换展示的部分
- 将 CSS 变量作为内容输出,配合
@property
特性和 CSS 动画使得 CSS 变量能够在数值上自动变化(@property
特性依赖较高的浏览器版本,具体的支持情况参考 CanIUse (opens in a new tab)。)
无论是哪种方法,都可以在不依赖 JavaScript 的情况下实现一个时钟,但是,想要初始化时钟的时刻,仍然需要 JavaScript 的帮助,而这项工作,正是由我们正在介绍的时间字符解析器完成的。你需要关注的,仅仅是让你的时钟按照真实的节拍动起来。
由于前文提到的原因,其实方案2是无法在这里应用的,下面的第一个例子使用方案 1 实现。
我将为你展示几个例子,来了解基于 CSS 变量的工作模式,更重要的是,领略 CSS 的迷人之处。
特别需要注意的是,CSS 变量的每次改变(在这里是每分钟更新一次),浏览器都将重新计算与之相关的 CSS 属性,因此有时会出现意料之外的情况,比如,当你将目标 CSS 变量用于动画的延时(animation-delay
)时候,每次更新都会重新计算动画的当前进程,这个时候,我们只需要第一次对 CSS 变量进行赋值,而非每次都更新,请使用 data-refresh="false"
来声明这一点,你会在下面的例子中看到具体的使用方法。
简单的数字时钟
<!-- 准备所有时刻的时间元素,通过 CSS 动画来轮换展示的部分 -->
<template id="second-clock">
<div class="container">
<div class="numbers" data-format="H"></div>
<div class="numbers" data-format="i"></div>
<div class="numbers" data-format="-sv\m\s" data-target="--delay">
<div class="s-box">
<div>00</div><div>01</div><div>02</div><div>03</div><div>04</div><div>05</div><div>06</div><div>07</div><div>08</div><div>09</div><div>10</div><div>11</div><div>12</div><div>13</div><div>14</div><div>15</div><div>16</div><div>17</div><div>18</div><div>19</div><div>20</div><div>21</div><div>22</div><div>23</div><div>24</div><div>25</div><div>26</div><div>27</div><div>28</div><div>29</div><div>30</div><div>31</div><div>32</div><div>33</div><div>34</div><div>35</div><div>36</div><div>37</div><div>38</div><div>39</div><div>40</div><div>41</div><div>42</div><div>43</div><div>44</div><div>45</div><div>46</div><div>47</div><div>48</div><div>49</div><div>40</div><div>51</div><div>52</div><div>53</div><div>54</div><div>55</div><div>56</div><div>57</div><div>58</div><div>59</div>
</div>
</div>
</div>
<style>
:host {
width: 100%;
height: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.container {
display: flex;
gap: 1rem;
padding: 6rem 0;
font-size: 3rem;
line-height: 1;
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, white 50%, transparent 100%);
mask-image: linear-gradient(to bottom, transparent 0%, white 50%, transparent 100%);
}
.numbers {
width: 6rem;
height: 5rem;
display: flex;
justify-content: center;
align-items: center;
border-radius: 1rem;
background-color: #ddd;
}
.s-box {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: visible;
animation: second 60s steps(60, end) var(--delay, -10s) infinite normal forwards;
}
.s-box div {
width: 100%;
height: 100%;
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
}
@keyframes second {
from { transform: translateY(0); }
to { transform: translateY(-6000%); }
}
</style>
</template>
使用图像插槽
也许你已经注意到,Scene Tab 中,有些时钟允许你指定其中某一部分的图像的内容,这是通过自定义元素的插槽 (<slot>
)实现的。在你的时钟中定义一个 <slot name="background">
元素,你将能够通过设置页面将任何已有的图像套用到插槽内。
<template>
<div class="my-clock">
<slot name="background"></slot>
</div>
</template>
选择背景图像后,背景图像元素将会插入这个插槽
<custom-style>
<gallery-background slot="background"></gallery-background>
</custom-style>
导入外部字体
在常规的网页开发中 从外部导入字体有以下几种方法:
- 使用
<link>
标签 - 使用
@import
- 使用
@font-face
正如前文所述,第二、第三种方法是不支持的。
使用第一种方法,那么 <link>
标签将会在其他元素插入文档之前先被插入到网页的 <head>
内。
<template>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<!-- Your other code -->
</template>
作者信息和预览图像
最后,你可以在你的时钟里添加 meta
元素,用来描述时钟的标题、简介、作者、和预览图,这四种元数据分别由 [property="og:title"]
、 [property="og:image"]
、[name="og:author"]
、[name="description"]
的元素描述:
<meta property="og:title" content="Title" />
<meta property="og:image" content="" />
<meta name="author" content="Author" />
<meta name="description" content="Write a description here." />
这些元数据并不会被插入网页中,以免影响网页原本的元数据信息。
真正自由的时钟样式
很遗憾的是,即使这样,你也并没有真正地以完全自由的方式创造自己的时钟,你仍然要遵循上文解释的一系列规范。不过,无论是代码还是设计,它们都仅仅是具象化你的创意表达的工具,真正自由地落地你的创意,你将动用的是你手边一切的工具,而不仅仅是代码或设计软件,现在,开始创造吧!