Upupoo の HTML ベースの壁紙機能は、自由にカスタマイズできる点が魅力的です。其中广泛关注的一个 HTML 壁紙では、日付や時間の表示に加え、もっと凝った機能を追加したくなる場面があります。ここでは、农历(旧暦)情報を表示する機能と、曜日ごとに異なるメッセージ(「今日の一言」)を表示する実装について解説します。
农历変換ロジックの導入
まず、HTML 内に农历変換用の JavaScript 関数を追加します。これらの関数は、西暦日付を农历年月日に変換します。
// 天干地支・数字・月名・干支等相关定義
var tgString = "甲乙丙丁戊己庚辛壬癸";
var dzString = "子丑寅卯辰巳午未申酉戌亥";
var numString = "一二三四五六七八九十";
var monString = "正二三四五六七八九十冬腊";
var sx = "鼠牛虎兔龙蛇马羊猴鸡狗猪";
// 旧暦データ(1921〜2020年の各月の大小・閏月情報を圧縮)
var CalendarData = [
0xA4B,0x5164B,0x6A5,0x6D4,0x415B5,0x2B6,0x957,0x2092F,0x497,0x60C96,0xD4A,0xEA5,
0x50DA9,0x5AD,0x2B6,0x3126E,0x92E,0x7192D,0xC95,0xD4A,0x61B4A,0xB55,0x56A,0x4155B,
0x25D,0x92D,0x2192B,0xA95,0x71695,0x6CA,0xB55,0x50AB5,0x4DA,0xA5B,0x30A57,0x52B,
0x8152A,0xE95,0x6AA,0x615AA,0xAB5,0x4B6,0x414AE,0xA57,0x526,0x31D26,0xD95,0x70B55,
0x56A,0x96D,0x5095D,0x4AD,0xA4D,0x41A4D,0xD25,0x81AA5,0xB54,0xB6A,0x612DA,0x95B,
0x49B,0x41497,0xA4B,0xA164B,0x6A5,0x6D4,0x615B4,0xAB6,0x957,0x5092F,0x497,0x64B,
0x30D4A,0xEA5,0x80D65,0x5AC,0xAB6,0x5126D,0x92E,0xC96,0x41A95,0xD4A,0xDA5,0x20B55,
0x56A,0x7155B,0x25D,0x92D,0x5192B,0xA95,0xB4A,0x416AA,0xAD5,0x90AB5,0x4BA,0xA5B,
0x60A57,0x52B,0xA93,0x40E95
];
var madd = [0,31,59,90,120,151,181,212,243,273,304,334];
// ビット操作用補助関数
function getBit(num, pos) {
return (num >> pos) & 1;
}
// 西暦から旧暦の年月日を計算
function updateLunarDate(y, m, d) {
var total = 0, k, n, loopEnd = false;
var baseYear = (y < 1900) ? y + 1900 : y;
// 西暦1921年からの経過日数を算出
total = (baseYear - 1921) * 365 + Math.floor((baseYear - 1921) / 4) + madd[m] + d - 38;
if ((baseYear % 4 === 0 && m > 1)) total++;
for (var i = 0; i < CalendarData.length; i++) {
k = (CalendarData[i] < 0xfff) ? 11 : 12;
for (var j = k; j >= 0; j--) {
if (total <= 29 + getBit(CalendarData[i], j)) {
loopEnd = true;
break;
}
total -= 29 + getBit(CalendarData[i], j);
}
if (loopEnd) break;
}
var lunarYear = 1921 + i;
var lunarMonth = k - j;
var lunarDay = total;
// 閏月判定処理
if (k === 12) {
var leapMonth = Math.floor(CalendarData[i] / 0x10000) + 1;
if (lunarMonth === leapMonth) {
lunarMonth = -lunarMonth;
} else if (lunarMonth > leapMonth) {
lunarMonth--;
}
}
// 表示用に構築
var ganzhi = tgString.charAt((lunarYear - 4) % 10) + dzString.charAt((lunarYear - 4) % 12);
var zodiac = sx.charAt((lunarYear - 4) % 12);
var monthStr = (lunarMonth < 0) ? "(閏)" + monString.charAt(-lunarMonth - 1) : monString.charAt(Math.abs(lunarMonth) - 1);
var dayStr = (lunarDay < 11) ? "初" :
(lunarDay < 20) ? "十" :
(lunarDay < 30) ? "廿" : "三十";
if (lunarDay % 10 !== 0 || lunarDay === 10) {
dayStr += numString.charAt((lunarDay - 1) % 10);
}
return ganzhi + "(" + zodiac + ")年 " + monthStr + "月 " + dayStr;
}
HTML 上で农历表示用要素を追加します。
<div id="lunar-display" style="font-size:24px; font-weight:bold;"></div>
JavaScript で农历情報を定期更新します(毎秒1回)。
// タイマーで毎秒更新
function updateAll() {
var now = new Date();
var year = now.getFullYear();
var month = now.getMonth(); // 0〜11
var day = now.getDate();
// 表示用农历
var lunarText = updateLunarDate(year, month, day);
document.getElementById("lunar-display").textContent = lunarText;
// ...他の内容(時刻や曜日など)もここで更新
}
setInterval(updateAll, 1000);
曜日別メッセージ表示
さらに、「曜日ごとに自動で変わる一言」を追加します。このためには、メッセージ配列を曜日に応じて選択するように実装します。
// 素朴な一言リスト(7日分)
var dailyQuotes = [
"月曜日ですね、気を鼓舞して!",
"新しい挑戦の始まりです。",
"水曜日、週の半ばを乗り越えました。",
" slogans tomorrow!明日はもう一歩。",
"金曜日、モチベーションを維持!",
"週末への準備を始めましょう。",
"_dt=曰:今日も一日おつかれさまだ!"
];
// 内部で使っていた変数と競合しないよう、説明のため簡略化
function updateDailyShout() {
var today = new Date().getDay(); // 0:日, 1:月, ..., 6:土
document.getElementById("quote-display").textContent = dailyQuotes[today];
}
既存の HTML に表示エリアを追加して、先ほどの `updateAll()` 関数 inside で呼び出せば OK です。
<div id="quote-display" class="text" style="margin-top:20px;"></div>
実装上の注意点
- 旧暦付帯データ(十二支・六十干支)は、1921〜2020年までを前提として定義されています。範囲外年月の処理を Silkily 無視しているため、必要に応じて拡張してください。
- 配列のインデックスや文字列長さの範囲外アクセスを防ぐため、十分なテストを実施してください。
- `Math.floor` やビット演算の挙動は環境によって差異が出ないよう、意図を明確に保つことが望ましいです。