使用手册
时钟
自由样式

自由样式 (Custom Style)

在自由样式时钟里,你可以通过编写 HTML 和 CSS 代码来设计自己的时钟样式,同时,遵循下面的指南,你可以轻易地将时间信息集成到你设计的时钟当中。

为了更好地理解接下来的内容,你至少需要以下基础知识:

现在,开始准备你的代码。第一步,你需要一个 <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>

在这个例子中,Hi 是可以被识别的字符,分别代表小时和分钟,而 : 并不是可识别的字符,因此,这个 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月份中的第几天,有补零的两位数字0131
D文字表示星期几,三个字母MonSun
j月份中的第几天,没有补零131
l(小写 'L')完整文本表示星期几SundaySaturday
NISO 8601 数字表示星期几1(星期一)到 7(星期天)
S月份中的第几天英文后缀,两个字符stndrdth。可以和 j 一起使用
w数字表示星期几0(星期天)到 6(星期六)
z一年中的第几天(从 0 开始)0365
------
WISO 8601 格式当年中的第几周,每周从周一开始示例:42(当年的第 42 周)
------
F月份的完整文本表示,比如 January 或者 MarchJanuaryDecember
m月份的数字表示,补零0112
M简短文本表示月份,三个字母JanDec
n数字表示几月份,不补零112
t指定月份的天数2831
------
L是否是闰年如果是闰年为 1,否则为 0
oISO 8601 数字年份表示。这和 Y 值相同,但如果 ISO 周数(W)属于上一年或者下一年,则用那一年。示例:19992003
X年份的展开全数字表示,至少四位,- 表示公元前,+ 表示公元。示例:-0055+0787+1999+10191
x如果需要,年份可以展开全数字表示,如果可能的话,也可以用标准的全数字(Y)表示。至少有四位数字。公元前以 - 为前缀,年份不小于 10000 时以 + 为前缀。示例:-0055, 0787, 1999, +10191
Y年份完整数字表示,至少四位,使用 - 表示公元前。示例:-005507871999200310191
y两位数的年份表示示例:9903
时间------
a小写的上午和下午ampm
A大写的上午和下午AMPM
BSwatch 互联网时间000999
g不补零的小时(12 小时制)112
G不补零的小时(24 小时制)023
h补零的小时(12 小时制)0112
H补零的小时(24 小时制)0023
i补零的分钟0059
s补零的秒0059
u微秒。示例:654321
v毫秒。示例:654
时区------
e时区标识符示例:UTCGMTAtlantic/Azores
I(大写 i)是否为夏令时如果是夏令时为 1,否则为 0
O跟格林尼治时间(GMT)的差异,小时和分钟时间没有冒号示例:+0200
P跟格林尼治时间(GMT)的差异,小时和分钟时间有冒号示例:+02:00
pP 相同,区别是使用 Z 替换 +00:00 返回(PHP 8.0.0 起可用)示例:Z+02:00
T如果知道会返回时区缩写,否则返回 GMT 时差。示例:ESTMDT+05
Z以秒为单位的时差。UTC 以西的时区为负的时差,以东为正的时差。-4320050400
完整日期/时间------
cISO 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 元素的方法简介直观,但仍然有不足:

  1. 时间信息只能用文本形式展示;
  2. 无法解析秒

如前文所述,浏览器不再允许在浏览器拓展中执行由用户提交的代码。为了解决上面两点问题,我们还在时间解析器中设计了基于 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 来实现一个动态变化的时钟通常有几种解决方案:

  1. 在 HTML 中准备所有时刻的时间元素,通过 CSS 动画来轮换展示的部分
  2. 将 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>

导入外部字体

在常规的网页开发中 从外部导入字体有以下几种方法:

  1. 使用 <link> 标签
  2. 使用 @import
  3. 使用 @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." />       

这些元数据并不会被插入网页中,以免影响网页原本的元数据信息。

真正自由的时钟样式

很遗憾的是,即使这样,你也并没有真正地以完全自由的方式创造自己的时钟,你仍然要遵循上文解释的一系列规范。不过,无论是代码还是设计,它们都仅仅是具象化你的创意表达的工具,真正自由地落地你的创意,你将动用的是你手边一切的工具,而不仅仅是代码或设计软件,现在,开始创造吧!