Skip to content

专注主题代码构成 预览版本

更新时间:2025年3月30日

在上一章中,我们加载了一个示例专注主题项目。不难发现,其中有如下几个文件与文件夹:

  • info.json 信息文件
  • main.html 布局文件
  • preview.webp 预览图
  • setting 用户默认配置文件夹

JSON配置

JSON是一种数据格式,并不是编程语言。在专注主题中,info.json存储了关于该专注主题的唯一ID、版本、名称等信息。

以下是示例专注主题项目的info.json:

json
{
    "id": "yyy66d35-8048-46c8-8627-ac8d89a80ce9",
    "sceneRenderVersion":2,
    "version":20250310,
    "name": "标准 V2",
    "color": "#FF9800",
    "feature":[
        "followActionColor",
        "darkMode"
    ],
    "setting": [
        {
            "type": "SettingItemGroup",
            "items": [
                {
                    "type": "SettingItem",
                    "contentType": "image",
                    "title": "专注背景图片",
                    "preferenceName": "background.png",
                    "icon": "0xf029f"
                },
                {
                    "type": "SettingItem",
                    "contentType": "seekbar",
                    "data": {
                        "max": "10",
                        "min": "0"
                    },
                    "title": "背景模糊",
                    "preferenceName": "backgroundBlur",
                    "icon": "0xf5c9"
                }
            ],
            "description": "如果未设置专注背景图片,则显示专注模式主题色。\n\n背景模糊为0时不生效。"
        }
    ]
}

其中,各个字段的解释如下:

  1. id:由Doing系统自动分配的唯一ID,用于区分专注主题。如果仅仅是在本地测试开发,则可以填写任意UUID V4内容。
  2. sceneRenderVersion:固定值为2。
  3. version:版本序号。需要为纯数字,可用于比较版本大小。
  4. name:名称。用户将会在专注主题列表页面看到。
  5. color:主题色。可能会被用在展示该专注主题的信息页。
  6. feature:支持特性。列表可以填写的值包括followActionColor darkMode
  7. setting: 用户个性化配置。允许用户修改的配置信息。

TIP

不要忘记,JSON中无法使用注释

HTML文件

main.html是专注主题的布局文件。在这个文件中,我们可以使用HTML、CSS、JavaScript等技术来构建专注主题的界面。

WARNING

目前版本中,包括HTML、CSS、JavaScript的内容都需在一个文件中。

我们将示例专注主题项目的main.html进行拆分:

1. <head>与<style>部分

html
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="color-scheme" content="light dark">
    <title>番茄钟</title>
    <style>
        * {
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
        }

        p,
        h1,
        h2,
        h3,
        h4,
        h5,
        h6,
        li,
        span,
        div {
            cursor: default !important;
        }

        :root {
            --main-color: #2196F3;
            --background-color: #ffffff;
            --text-color: #212121;
        }

        @media (prefers-color-scheme: dark) {
            :root {
                --background-color: #121212;
                --text-color: #ffffff;
            }
        }

        body {
            margin: 0;
            height: 100vh;
            background: var(--background-color);
            color: var(--text-color);
            overflow: hidden;
            position: relative;
            font-family: 'Arial', sans-serif;
        }

        .timer-container {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            z-index: 2;
            width: clamp(200px, 70vw, 400px);
            aspect-ratio: 1;
        }

        ...
    </style>
</head>

</html>

该部分与网页开发类似。需要注意的是:

  • <meta name="viewport" content="width=device-width, initial-scale=1.0">:建议设置,以适配不同设备。
  • <meta name="color-scheme" content="light dark">:建议设置,以适配不同主题模式。
  • cursor: default !important;:建议设置,以避免鼠标指针样式被覆盖。

2. <body>部分

html
<body>
    <div class="glow-container" id="glowContainer"></div>

    <div class="timer-container">
        <div class="progress-ring">
            <div style="justify-content: center; align-items: center; display: flex; flex-direction: column;">
                <div style="opacity: 0;">专注中</div>
                <div id="time"></div>
                <div id="mode"></div>
            </div>
        </div>
    </div>

    <div id="titlebar">
        <button class="exit-btn control-btn" onclick="doing.call('exit')">
            <div class="icon-exit"></div>
        </button>
        <div style="text-align: right; letter-spacing: 2px; opacity: 0.4;">12:34</div>
    </div>

    <div class="controls" id="controls">
        <button class="control-btn" onclick="doing.call('action', 'togglePause')" id="pauseBtn">
            <div class="icon-pause" id="pauseIcon"></div>
        </button>
        <button class="control-btn" onclick="doing.call('action', 'toggleDND')" id="dndBtn">
            <div class="icon-dnd" id="dndIcon"></div>
        </button>
        <button class="control-btn" onclick="doing.call('action', 'toggleKeepScreenOn')" id="screenBtn">
            <div class="icon-screen" id="screenIcon"></div>
        </button>
    </div>

    ...
</body>

该部分与网页开发类似。需要注意的是:

  • 图片等资源均需包含在dft文件中,不能使用外部链接。

3. <script>部分

html
    <script>
        document.oncontextmenu = () => false;
        document.addEventListener('selectstart', (e) => e.preventDefault());

        // 状态更新函数
        function updateDoingFocusState() {
            document.getElementById('time').textContent = doing.getState().timer.remainTime;
            document.getElementById('mode').textContent = doing.getState().timer.mode == "work" ? "专注中" : doing.getState().timer.mode == "rest" ? "休息中" : "已暂停";

            // 省略部分状态更新
        }

        ...
    </script>

该部分与网页开发类似。需要注意的是:

  • 脚本将无法访问网络内容,资源均应包含在dft文件中。

其中,updateDoingFocusState()方法将会在专注主题状态更新时被调用,以更新专注主题界面。因此,所有界面数据和状态更新需要从这个方法中获得。

例如,如果需要更新时间,可使用:

javascript
document.getElementById('time').textContent = doing.getState().timer.remainTime;

doing.getState()的结构如下:

javascript
{
    "timer": {
        "remainTime": "12:34",
        "currentPeriodTime": "40000",
        "periodTime": "60",
        "mode": "rest",
        "keepScreenOn": "false",
        "dnd": "true"
    }
}

WARNING

当前专注主题仍处于早期阶段,doing.getState()的结构可能会有所变动。请注意关注文档更新。

如果需要按钮来对专注计时进行修改,可以通过doing.call()方法来进行。 例如如果需要切换暂停状态:

javascript
doing.call(["action","togglePause"]);

其中,这些函数有:

javascript
{
      "action": [
        "exit",
        "togglePause",
        "toggleKeepScreenOn",
        "toggleDND",
        "toggleFullscreen",
        "rotateScreen",
        "minimizeFocus"
      },
      "function": [
        "showWhiteNoiseDialog",
        "showEventDialog",
        "showInstantRecordDialog",
        "showLeftScreen",
        "showRightScreen"
      ]
}

用户配置文件

setting/文件夹中存储了用户个性化配置。在info.json中,我们定义了用户可以修改的配置项,而这些配置项的默认值将会被存储在这个文件夹中。

例如,我们在info.json中定义了一个名为backgroundBlur的配置项:

json
{
    "type": "SettingItem",
    "contentType": "seekbar",
    "data": {
        "max": "10",
        "min": "0"
    },
    "title": "背景模糊",
    "preferenceName": "backgroundBlur",
    "icon": "0xf5c9"
}

那么,在setting/settings.json中,我们可以定义这个配置项的默认值:

json
{
    "backgroundBlur": "4.3
}

在专注主题中,我们可以通过doing.getPreference()方法来获取用户配置项的值:

javascript
const backgroundBlur = doing.getPreference("backgroundBlur");

WARNING

请注意,用户配置项的值是字符串类型。如果需要使用数值,请自行转换。

总结

通过本章节,我们了解了专注主题的代码构成。