一个用于演练/演示的桌面时钟工具:两个窗口 —— 控制窗口(普通)与时钟窗口(无边框、 可拖动、可拖边缘改大小,由控制窗口开与关)。 功能 - 两个窗口各自的「始终置顶」开关 - 显示样式:纯数字 / 圆形表盘,切换立即生效 - 实时设定时间(含「填入当前显示时间」) - 倍率:支持正数、负数(倒转)、0(暂停)、小数 - 暂停 / 继续(继续时回到暂停前的倍率,按钮文案会显示是几倍) - 快进五档:+5 秒 / +1 分钟 / +5 分钟 / +10 分钟 / +30 分钟 实现要点 - 走时是「基准时间 + 基准时刻 + 倍率」的锚点式模型(不累加、零漂移); 倍率、暂停、倒转、快进因此是同一套机制的四个用法,互不打架 - 跳转动画做在「呈现时间」这一层:数字滚动与表盘指针扫动共用同一个中间值, 所以永远同步,也不会跨 12 点倒着转一圈 - 无边框窗口的拖动(CSS app-region + 控件 no-drag)与拖边缘改大小 (自己算几何 + IPC setBounds,因为 frame:false 下系统边缘命中不可靠) 测试 152 例:走时模型 / 改大小几何 / 呈现时间 / 两个渲染进程(DOM 桩)
83 lines
3.8 KiB
JavaScript
83 lines
3.8 KiB
JavaScript
'use strict';
|
||
|
||
// 拖边缘改大小的几何用例(纯计算,不需要 Electron):
|
||
// node test/resize.test.js
|
||
// 无边框窗口里这段代码只有真鼠标拖拽才能触发,先用纯函数把它钉死,Windows 上就只剩「手感」要试。
|
||
|
||
const { computeBounds, MIN_SIZE } = require('../renderer/resize');
|
||
|
||
let passed = 0;
|
||
const failures = [];
|
||
|
||
function check(name, ok, detail) {
|
||
if (ok) {
|
||
passed += 1;
|
||
console.log(`PASS ${name}${detail ? ` [${detail}]` : ''}`);
|
||
} else {
|
||
failures.push(name);
|
||
console.log(`FAIL ${name}${detail ? ` [${detail}]` : ''}`);
|
||
}
|
||
}
|
||
|
||
const start = { x: 400, y: 300, width: 520, height: 280 };
|
||
const same = (a, b) => a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
||
|
||
console.log('--- 单边 ---');
|
||
check('东边 +120:只有宽度变,左上角不动',
|
||
same(computeBounds(start, 'e', 120, 0), { x: 400, y: 300, width: 640, height: 280 }));
|
||
check('南边 +60:只有高度变',
|
||
same(computeBounds(start, 's', 0, 60), { x: 400, y: 300, width: 520, height: 340 }));
|
||
check('西边 -80:宽度增加 80,左边界左移 80',
|
||
same(computeBounds(start, 'w', -80, 0), { x: 320, y: 300, width: 600, height: 280 }));
|
||
check('北边 -50:高度增加 50,上边界上移 50',
|
||
same(computeBounds(start, 'n', 0, -50), { x: 400, y: 250, width: 520, height: 330 }));
|
||
check('东边往回拖 -100:窗口变窄,左上角不动',
|
||
same(computeBounds(start, 'e', -100, 0), { x: 400, y: 300, width: 420, height: 280 }));
|
||
|
||
console.log('--- 四角 ---');
|
||
check('右下角 +40/+30:宽高同时增加',
|
||
same(computeBounds(start, 'se', 40, 30), { x: 400, y: 300, width: 560, height: 310 }));
|
||
check('左上角 -40/-30:宽高增加且整体左上移',
|
||
same(computeBounds(start, 'nw', -40, -30), { x: 360, y: 270, width: 560, height: 310 }));
|
||
check('右上角 +40/-30:宽度增加、上边界上移',
|
||
same(computeBounds(start, 'ne', 40, -30), { x: 400, y: 270, width: 560, height: 310 }));
|
||
check('左下角 -40/+30:左边界左移、高度增加',
|
||
same(computeBounds(start, 'sw', -40, 30), { x: 360, y: 300, width: 560, height: 310 }));
|
||
|
||
console.log('--- 最小尺寸 ---');
|
||
check('东边往死里拖:宽度停在 260', computeBounds(start, 'e', -9999, 0).width === MIN_SIZE.width);
|
||
check('南边往死里拖:高度停在 150', computeBounds(start, 's', 0, -9999).height === MIN_SIZE.height);
|
||
{
|
||
const r = computeBounds(start, 'w', 9999, 0);
|
||
check('西边往死里拖:宽度停在 260,且右边界不动(窗口不会被推走)',
|
||
r.width === MIN_SIZE.width && r.x + r.width === start.x + start.width, JSON.stringify(r));
|
||
}
|
||
{
|
||
const r = computeBounds(start, 'n', 0, 9999);
|
||
check('北边往死里拖:高度停在 150,且下边界不动',
|
||
r.height === MIN_SIZE.height && r.y + r.height === start.y + start.height, JSON.stringify(r));
|
||
}
|
||
{
|
||
const r = computeBounds(start, 'nw', 9999, 9999);
|
||
check('左上角往死里拖:宽高都停在最小值,右下角不动',
|
||
r.width === MIN_SIZE.width && r.height === MIN_SIZE.height &&
|
||
r.x + r.width === start.x + start.width && r.y + r.height === start.y + start.height, JSON.stringify(r));
|
||
}
|
||
|
||
console.log('--- 边界 ---');
|
||
check('位移为 0 时原样返回', same(computeBounds(start, 'e', 0, 0), start));
|
||
check('结果都是整数(避免 setBounds 收到小数后抖动)',
|
||
Number.isInteger(computeBounds(start, 'e', 12.4, 0).width));
|
||
check('负坐标(窗口在主屏左侧)也能算',
|
||
same(computeBounds({ x: -900, y: -200, width: 520, height: 280 }, 'w', -100, 0),
|
||
{ x: -1000, y: -200, width: 620, height: 280 }));
|
||
|
||
console.log('');
|
||
if (failures.length === 0) {
|
||
console.log(`全部通过:${passed}/${passed}`);
|
||
process.exit(0);
|
||
} else {
|
||
console.log(`通过 ${passed},失败 ${failures.length}:${failures.join(';')}`);
|
||
process.exit(1);
|
||
}
|