茜莉娅 Serelia
← Back to the beginning

海月拾遗(四)

海月拾遗

見えた世界が霞む

日常

下午好,这里是正在听Blur的しおりん。

今天的周记来记点什么呢?

Kotlin

最近,小织的一个朋友向我发出了一个help请求

9245f6d0395e276e7038b425cdeb932d

他给出了一个Github链接,里面是一个第三方的网易云播放器,然后要我完成的是去掉这个的探索模式。

所谓探索模式,估计是那种社交板块,比如说分享音乐帖子的社区。

如果干脆的砍掉一个板块所有功能,如果它和软件某个模块的耦合性太高,那修修补补将会是一件难事。
干脆不如直接把底部菜单的ui删除了,那就眼不见心不烦了。
(也是一种掩耳盗铃但是有效的方案)

‍

于是我翻阅源码看看。

@@ -38,6 +38,17 @@
 /** 悬浮胶囊底栏专用的 tabs(包含右侧设置入口,共 3 项) */
 val floatingNavTabs = listOf(
     BottomBarTab.Library,
     BottomBarTab.Explore,
     BottomBarTab.Settings,
 )
+
+/**
+ * 底栏实际渲染的 tabs(纯 UI 门面,与 pager 页序解耦)。
+ *
+ * 探索入口下线:这里只保留「我的」,底栏不再出现探索按钮。
+ * 注意不要用这个列表去算 pager 页数/索引——[tabs] 与 [floatingNavTabs] 保持原样,
+ * HomePager 的 when(page) 仍是 0=我的 / 1=探索 / 2=设置,删 entry 不会错位。
+ */
+val navBarTabs = listOf(
+    BottomBarTab.Library,
+)

嗅到了比较浓的ai风格注释,估计也是vibe的第三方客户端,但不得不说,ui还是很不错的。增加一个​val navBarTabs。

这里是git diff的视图。

-       val targetPage = tabs.indexOfFirst { it.route == item.route }
+       val targetPage = navBarTabs.indexOfFirst { it.route == item.route }

将​targetPage​引入到我们新的​var navBarTabs即可做到无痛转移。

-             tabs.forEach { item ->
-                val tabIndex = tabs.indexOf(item).coerceAtLeast(0)
-                val selected = if (isHomeRoute) {
-                tabs.getOrNull(homePageScroll.roundToInt())?.route == item.route
+             navBarTabs.forEach { item ->
+                 val tabIndex = navBarTabs.indexOf(item).coerceAtLeast(0)
+                 val selected = if (isHomeRoute) {
+                 navBarTabs.getOrNull(homePageScroll.roundToInt())?.route == item.route

同样遍历也是如此。

其实第一次看Kotlin的代码,甚至没有什么基础,让ai基于需求定位分析逻辑就比较简单了。
但不得不说的一点,无用的社交板块太多并不是一件好事,点名网易云。

但是不想迁走是因为歌单里留了两三千首歌,只是将就着用了。

Monopoly

这周和往常的几周一样,工作日都要上班。

广东的天气到了十一月还是维持在25-30度上下,家乡那里已经要下雪 (或者是已经下雪了)

而这里暂时还是要穿短袖。

image

对于小织来说,天气热出门真是一个比较痛苦的事。

(这下就更有理由窝在家了) 。

最近都懒得去刷希奇了,对Fediverse的关注和热情度也是下降了不少。
其实说到搞博客,其实已经是一年前的想法了。

在一年前的时候,就有想法要捣鼓一个个人网站来玩玩,但是也只是有玩玩的心思。

第一版的博客现在的电脑上已经没有了,但是第二版还是有的。

image

它已经尘封很久了

image

哦,我想起来了。是第三版的博客在基于第二版的时候做了些小改动导致现在已经乱套了。

image

看起来比较干净但是ui不合理的地方比较多。

‍


‍

c6c5240978b62faa511e45b05d5152b5_720

找到了第一版的博客截图,当时是用的一个叫Mizuki的开源主题。
不得不说,这个的风格还是很不错的,但现在更喜欢简约一些的了。

幸あれ

最近的作息是 ———— 澳大利亚袋鼠!

其实每天上班回来吃掉外卖然后洗澡,再大口吞下一枚褪黑素之后关灯躺平。
现在已经养成每天四五点钟起床的习惯了。但还是要尽量避免吃褪黑素了。

有人懂起床后迷迷糊糊的然后靠听歌唤醒的感觉吗
然后趁着离上班还有一段时间,就可以用来学一些东西。或者是写一写文档之类的。

最近在实习的时候解锁了一样新技能。

最近我们的一个项目要结尾了,公司购置了机器外壳,然后我和同事把开发板和外设组装到机器上,但是外壳要留给电源线和其他数据线的。所以需要进行一个打孔。

6aea220238fcaa45ac0c339d5665fcbd

于是经理掏出了一个电钻。
告诉我们一些注意事项和安全措施,安全地在外壳上打了这个孔。

电钻的功率比较大,打孔的时候整个办公室都充斥着噪音…
于是忍了一小会继续工作


最近给博客做了一个每个页的item加载动画。

image

把原来的等高线地图换做土星星环,但是歌词却是水星。

它是这样画出来的

/* ══════════════════════════════════════════════════════════════════════
   星间环 · 入场动画 整合版
   —— 把 stone-contour.js 与 stone-contour.css 里跟动画有关的代码,
      按**实际执行顺序**拼成一份。行号标在注释里,方便回去对照。
   ══════════════════════════════════════════════════════════════════════ */

/* ── 0. 几何常量(js:54 / js:58)──────────────────────────────────── */
var CX = 310, CY = 200, R = 96, TILT = -18, RY = 0.27;

var RINGS = [                        // 由外向内,一共七道
  { rx: 252, entry: null },          // 纯装饰环
  { rx: 230, entry: 0 },             // 入口一:个人介绍
  { rx: 208, entry: null },
  { rx: 188, entry: 1 },             // 入口二:友人新闻
  { rx: 168, entry: null },
  { rx: 148, entry: 2 },             // 入口三:友链
  { rx: 130, entry: null }
];

/* ── 1. 四个工具函数(js:80-114)──────────────────────────────────── */

function el(n, a) {                  // 造一个 SVG 元素
  var e = document.createElementNS(NS, n);
  for (var k in a) if (Object.prototype.hasOwnProperty.call(a, k)) e.setAttribute(k, a[k]);
  return e;
}

function ln(n, a, cls, delay) {      // 造一条"要画出来"的线
  var e = el(n, a);
  e.setAttribute('class', cls);
  e.setAttribute('data-len', '');                    // 提个记号:待会儿要量我
  if (delay != null) e.style.setProperty('--d', delay + 'ms');   // 排队时间
  return e;
}

function measure(root) {             // 量出每条线有多长,写进 --len
  [].slice.call(root.querySelectorAll('[data-len]')).forEach(function (e) {
    var len = 0;
    try { len = e.getTotalLength(); } catch (err) { len = 0; }
    if (!len || !isFinite(len)) { e.removeAttribute('data-len'); return; }
    e.style.setProperty('--len', (Math.ceil(len) + 2) + 'px');    // +2 是余量,防结尾漏一截
  });
}

function arcD(rx, half) {            // 算一条椭圆弧的路径
  var ry = (rx * RY).toFixed(2);
  return 'M' + (CX - rx) + ' ' + CY + 'A' + rx + ' ' + ry + ' 0 0 '
       + (half === 'top' ? 1 : 0) + ' ' + (CX + rx) + ' ' + CY;
}

/* ── 2. 建画布(js:143-146)───────────────────────────────────────── */
var svg = el('svg', { viewBox: '0 0 620 400', class: 'sc-svg' });
host.appendChild(svg);

var rot = el('g', { transform: 'rotate(' + TILT + ' ' + CX + ' ' + CY + ')' });  // 整体斜 18°
svg.appendChild(rot);

/* ── 3. 顺序 = 图层。先画后半 → 盖球 → 再画前半(js:148-194)────── */
RINGS.forEach(function (r, i) {      // ① 环的后半(会被球盖掉)
  var p = ln('path', { d: arcD(r.rx, 'top') }, 'ink' + (r.entry === null ? '-soft' : ''), 200 + i * 60);
  if (r.entry !== null) p.setAttribute('data-ring', r.entry);
  rot.appendChild(p);
});

(function globe() {                  // ② 球
  var g = el('g', { class: 'globe' });
  g.appendChild(el('circle', { cx: CX, cy: CY, r: R, class: 'globe-fill' }));  // ← 遮挡盘,和底色同色
  g.appendChild(ln('circle', { cx: CX, cy: CY, r: R }, 'ln globe-edge', 60));
  /* (略:三条纬线 140 / 185 / 230ms、三条经线 275 / 320 / 365ms,
      只是算出椭圆坐标,跟动画机制无关) */
  rot.appendChild(g);
})();

RINGS.forEach(function (r, i) {      // ③ 环的前半(压在球上)
  var p = ln('path', { d: arcD(r.rx, 'bottom') }, 'ink' + (r.entry === null ? '-soft' : ''), 200 + i * 60);
  if (r.entry !== null) p.setAttribute('data-ring', r.entry);
  rot.appendChild(p);
});

/* ── 4. 三个按钮:点 + 中文 + 手写英文 + 命中框(js:202-244)─────── */
RINGS.forEach(function (r) {
  if (r.entry === null) return;
  var it = ITEMS[r.entry], pos = ANCHORS[r.entry];

  var a = el('a', { class: 'entry no-veil', tabindex: '0', 'data-entry': r.entry });
  a.setAttribute('href', it.href);
  a.setAttributeNS(XLINK, 'xlink:href', it.href);
  a.setAttribute('aria-label', it.cn + ' · ' + it.en);

  var plate = el('rect', { class: 'plate', x: 0, y: 0, width: 0, height: 0 }); // 反色底面(透明)
  a.appendChild(plate);

  a.appendChild(el('circle', { cx: pos.dx, cy: pos.dy, r: 3, class: 'sat', 'data-dot': r.entry }));

  var tCn = el('text', { x: pos.lx, y: pos.ly - 10, 'text-anchor': 'middle', class: 'lab-cn' });
  tCn.textContent = it.cn;
  var tEn = el('text', { x: pos.lx, y: pos.ly + 20, 'text-anchor': 'middle', class: 'lab-en' });
  tEn.textContent = it.en;
  a.appendChild(tCn);
  a.appendChild(tEn);

  var hit = el('rect', { x: pos.lx - 46, y: pos.ly - 30, width: 92, height: 58, class: 'lab-hit' });
  a.appendChild(el('path', { d: arcD(r.rx, 'bottom'), class: 'ink-hit',
    transform: 'rotate(' + TILT + ' ' + CX + ' ' + CY + ')' }));
  a.appendChild(el('circle', { cx: pos.dx, cy: pos.dy, r: 14, class: 'sat-hit' }));
  a.appendChild(hit);                 // 最后 = 最上层,文字命中优先
  a.addEventListener('mouseenter', function () { go(r.entry); hov(r.entry); });
  a.addEventListener('focus',      function () { go(r.entry); hov(r.entry); });
  a.addEventListener('blur',       clearHov);
  a.addEventListener('mouseleave', clearHov);

  labs.push({ i: r.entry, a: a, hit: hit, cn: tCn, en: tEn, plate: plate });
  svg.appendChild(a);
});

/* ── 5. 量长度 + 收命中框(js:253-254)────────────────────────────── */
measure(svg);      // ← 所有带 data-len 的线在这里拿到真实长度
sizeHits(labs);

/* ── 6. 挂牌子,开始演(js:300-303)───────────────────────────────── */
requestAnimationFrame(function () {
  requestAnimationFrame(function () {
    document.documentElement.classList.add('draw');   // CSS 里所有动画都等这个类
  });
});

当然这部分代码是ai完成的,对应的技术原型是采用svg绘画。

var NS = 'http://www.w3.org/2000/svg';
function el(n, a) { var e = document.createElementNS(NS, n); ... }

感觉到了前端工程师的伟大,但又有一点对前端岗位被ai冲击的悲哀。

虽说有些人工智能模型训练出来的前端风格skill固然很不错,但小织仍然觉得这一切的精髓都要基于人类的审美之上。

基于未来人工智能的再发展,很难想象以后的大模型又能创造什么新的历史…

春禾

最近又加了一个``` html直接渲染的功能。

相对于裸html代码块来说,我们直接处理了渲染。

可以看到顶栏菜单的原始html被搬过来了,里面是实现的html。

这里面引入了单个字的字体库,所以代码会很长。

‍

好啦,这期没有什么其他可说的了

下期再会?

© 泠時月 2026,采用 CC BY 4.0 许可,转载保留署名。

留言 · 0 段对话

Serelia 茜莉娅 88×31 徽章
© 2026 茜莉娅 Powered By Node.js & Express By Rinzemoon RSS 订阅 联系 关于

扫码分享

二维码