2026-04-30 22:28:00
Z30 + 1650 套机用了大概四个月
机身是真的舒服,小巧轻便,外出没负担,这点我挺满意的
美中不足的是 1650 这个套头,焦段太短,爬山拍远景很鸡肋
当然,你要是喜欢裁切,那是另一回事
为了避免再交学费(避免不了)我认真想了下我的需求:
综合以上,在预算两千的情况下,筛出来两个型号:
这里没有选择 18140,因为焦段重叠(1650),而且前者价格过高
还有一点,这不是国行货,由京东购入(京东全球购摄影摄像自营专区)
客服曾说明:
到货后,通过包装说明得知,该产品由泰国制造


这里很霸王条款,撕毁不退,APP 内也有说明:全球购不支持七天无理由


如1图所示,镜面有一小白点,但是我放下手机后,却怎么也看不到这个白点
以此反复操作几次,我还是看不到,很奇怪

下面请欣赏,我一下午憋出一个屁的史诗级烂片:

2026-01-27 00:17:00
早上九点,顺风打电话来:"快递到了,放前台了啊。" 估摸着是那三脚架到了
这东西没有真不行,晚上出门一抹黑,不用长曝光,啥都拍不了
我有三个需求:轻量化、收纳短、伸缩1.5m,再三选择后,下单了富宝图旗下新品,空气三号 LITE
图一拆包后,我直接蒙了
周日在群里扯淡,看到 菜鸟之志 推荐的八爪鱼支架,我笑了,这什么玩意,长得那么招笑
真的受够了这种秒懂的日子
还别说,这工具不错,小巧,轻便,灵活度很高,推荐



八爪鱼是送的,但图三这个快拆套装我感觉买亏了。加了一百块钱,就换回这么一小块铁片,关键脚架本身就带一个

出门前纠结了半天要不要背双肩包,最后想想算了,索性直接挂在包拉链上了

有没有一种吕布骑狗的感觉,我这破尼康配上这个架子,多少有点高攀了





今天全程下雨,走在西湖边吹冷风,冻得头疼
回家发现买的电池也到了,价格方面,只有原装的二分之一,应该够我折腾了
天知道这支架是哪个小作坊代工的,漆水和碳布的质量,不敢苟同,我最垃圾的鱼竿,破烧火棍的碳纤维材料都比这个强,不如高性能的铝材料,甚至不如玻璃钢
他的旋转设计也有缺陷,不容易锁紧,又不敢用力拧,因为碳布根本受不了这种扭矩,哪怕你有交叉工艺,也顶不住,这不符合物理规律
再加上碳纤维这东西本身就是消耗品,树脂会氧化。三脚架这玩意,风吹雨打的,扭矩承受力只会越来越差。网上已经看到不少人把它拧断了,总之不推荐
2026-01-24 05:50:00
这两天突发奇想,在赶一个新项目
从早上 8 点写到晚上 11 点,除了去卫生间,人基本没动过,饭也没吃
这不是我多能坐得住,我只是倔
妄想一步登天,做不完就死磕。十几个小时是日常,通宵是常态,哪个月没有 4~5 次 20h + 作战记录?
GitHub Commits 一眼就能看出来:我从凌晨提交到凌晨,电子厂的狗都不会这么干
也许这就是倔驴 + 心流?
而我,天生心流圣体
老实说,我对“心流”没概念,只是前阵子偶然了解到。所以我并不明白,但我知道那是一种状态
当然,不吃饭是不行的
晚上 11:30,厨房炒个菜,微波炉热两个馒头,回屋再冲一大杯黑咖啡,两勺燕麦,少许牛奶,就算齐活
坐回电脑前,打开 BlogFinder,随手一翻,看到了阮一峰的周刊
心想,周五了
低头再看,周六了
然后,我看到了他的第一个推荐文章,心里“咯噔”一下:
点进去,翻译看看,整篇文章的核心观点就是:
如果你在意开源软件,就应该停止使用 MySQL
MySQL 虽然是基于 GPL v2 开源,实际上已经没有开源精神,因为 MySQL 正走向封闭

这是 Otto 在 1 月 11 日给出的截图
数据显示,从去年 9 月开始,到今年 1 月 11 日,MySQL 几乎完全断更
我看到这里,第一反应是去查官方信息 MySQL 核心团队博客,无果后,再 Google 一下,事情就对上了
早在 9 月 11 日,外媒就曝出 Oracle 大幅裁减 MySQL 核心员工 (约 70 人) 的消息,甚至还有传闻称,MySQL 团队已被并入 HeatWave 部门,Oracle 要把资源优先投入 AI
新闻刚出来时,MySQL 还没断更
现在再把这张“断崖式提交图”放出来,那可谓是深水炸弹
更有意思的是,Otto 还特意把图做成黑白,再配上大字标语当封面
手段可见一斑,毕竟,这人是 MariaDB Foundation (玛利亚数据库基金会) CEO
而 MariaDB 是 MySQL 的最重要的分支与直接竞争者
当年 Oracle 宣布收购 Sun Microsystems 的当天,MySQL 原始作者之一 Monty Widenius 就发起了 MariaDB fork
有意思的是:
就连 MariaDB 的 LOGO 海狮,也是有故事背景的
It happened when Monty and his older daughter My were snorkeling on one of the islands in the Galapagos. Something big, brown and fast suddenly appeared at an arm’s distance, laughing in their faces. Fond memories of this fast and funny creature, scaring the tourists, popped into Monty’s mind when asked picking a logo for MariaDB. He wanted to adhere to the tradition of animals as symbols of Open Source projects.

下面是 Otto 的举证,他认为 MySQL 所有开发工作都在 Oracle 内部进行
而公开的 Bug 追踪器只是个摆设,Oracle 内部有自己的系统
更离谱的是,他声称:
那些提交 PR 的人,除了石沉大海,就只剩下第二个选择
被 Oracle 员工重新编写并抹去原作者的贡献记录,原作者仅在博客中被提及其名
Otto 还证言道:
我在 AWS 负责 RDS MySQL 和 MariaDB 团队时,部门员工极其抗拒向 MySQL 提交代码,因为Oracle 的接纳态度极其恶劣
MySQL 8.0.29 将 ALTER TABLE 的默认行为改为 in-place
而这个问题,直到 8.0.32 才算彻底修完
更大的问题在于:
自 MySQL 8.0 发布以来,整整六年,没有一次真正意义上的大版本更新,几乎没有像样的功能可以讲
甚至,有传言新版本的 MySQL 性能大幅下降,之后被 MySQL 性能专家 Mark Callaghan 进行基准测试后得出MySQL 9.5 的吞吐量比 8.0 版本竟然低了 15%
最后,Otto 顺势给出了“迁移方案”:
说自家的 MariaDB 作为 MySQL 的亲兄弟,有极高的兼容性,对于传统 LAMP 架构,可以无缝切换
他说得也没错,MariaDB 起源于 MySQL 5.1 分支,早期开发策略几乎完全对标 MySQL,以至于后来被开源社区认可,尤其在 Debian、Ubuntu、Fedora 等发行版中,直接成为默认数据库

说实话,我已经不想看下去了,我只是个普通人,一个喜欢编程的小学生
从初一第一次接触数据库开始,就是 MySQL
从上学、工作,到后来失业
我靠它吃了很多年饭
刚毕业那会儿,我为了面试,背得最多的就是 MySQL
对着镜子:一会儿 Indexing、一会儿 Concurrency 人都要疯了
所以,我对 MySQL,是有感情的
现在它要没落了,心里说不出的滋味
虽然国内教学体系还在用 MySQL,但现实已经很清楚了
MySQL 不可能再回到从前老大哥的位置了
哎,天道如来,也罢
2026-01-18 23:48:00
这事半个月前就计划好了,本想放到 Photosuite 下个版本里
拖到现在,若不是为了昨天拍的一套图,我也懒得折腾
就是为了这口醋,才包的这顿饺子
我的目的很简单:不引入新的语法,而是通过换行来实现拼图
在 Markdown 中连续插入多张图片,只要它们之间没有非空白内容,就应被视为一个整体自动组合





拼图分组思路由贪心算法实现:
当扫描到一张图片时,以它为起点向后遍历,将所有连续相邻的图片依次纳入同一组
一旦相邻关系被打断,就停止扩展,并根据组内图片数量决定是否生成拼图(不足两张则原样保留)
难点在于“相邻”的定义:
因为 Markdown 编译为 HTML 后,图片容器之间可能夹杂换行符或空白文本节点
所有,不能直接依赖 nextSibling 进行判断
// src/modules/imageGrid.ts
/**
* 检查两个容器是否相邻
*
* @param container1 - 第一个容器
* @param container2 - 第二个容器
* @returns 是否相邻
*/
function areContainersAdjacent(container1: HTMLElement, container2: HTMLElement): boolean {
// 获取两个容器的父元素
const parent1 = container1.parentElement;
const parent2 = container2.parentElement;
// 如果父元素不同,不是相邻的
if (parent1 !== parent2 || !parent1) return false;
// 获取父元素的所有子元素
const siblings = Array.from(parent1.children);
const index1 = siblings.indexOf(container1);
const index2 = siblings.indexOf(container2);
// 检查是否相邻(中间只能有文本节点或空白节点)
if (index2 !== index1 + 1) {
// 检查中间是否只有空白文本节点
let hasNonWhitespace = false;
let node = container1.nextSibling;
while (node && node !== container2) {
if (node.nodeType === Node.ELEMENT_NODE) {
hasNonWhitespace = true;
break;
}
if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {
hasNonWhitespace = true;
break;
}
node = node.nextSibling;
}
return !hasNonWhitespace && node === container2;
}
return true;
}
在解决了相邻判定的问题之后,分组逻辑本身就变得很简单了
当 Photosuite 扫描到一张图片时,会从当前位置向后查找连续相邻的图片,并将它们归为一组
出于美观考虑,我这里将单组图片数量限制为最多三张
// src/modules/imageGrid.ts
function processImageGrids(root: Element) {
// 获取所有图片元素
const images = Array.from(root.querySelectorAll("img"));
// 用于标记已处理的图片
const processed = new Set<Element>();
for (let i = 0; i < images.length; i++) {
const img = images[i];
// 跳过已处理的图片
if (processed.has(img)) continue;
// 检查是否可以形成拼图
const gridImages = [img];
processed.add(img);
// 查找连续的图片(最多3张)
for (let j = i + 1; j < images.length && gridImages.length < 3; j++) {
const nextImg = images[j];
// 检查两个图片容器是否相邻
const currentContainer = ensurePhotosuiteContainer(gridImages[gridImages.length - 1]);
const nextContainer = ensurePhotosuiteContainer(nextImg);
if (areContainersAdjacent(currentContainer, nextContainer)) {
gridImages.push(nextImg);
processed.add(nextImg);
} else {
break;
}
}
// 如果找到了多张连续的图片(2-3张),创建拼图
if (gridImages.length >= 2) {
createImageGrid(gridImages);
}
}
}
我用 Boardmix 做了一个流程图,通俗易懂:

把图片简单地放进一个 Flex 容器并不难
如果一张是横图(16:9),另一张是竖图(9:16),直接使用 flex: 1 只会让它们宽度相同,却无法保证高度一致,结果要么高低不齐,要么图片被强行拉伸
要让多张图片在同一行里等高对齐,关键不在 Flex,而在比例关系
直观来说:哪张图片更“扁”,就应该占更宽的位置;哪张更“瘦”,就占得窄一些,这样它们的高度才能最终一致
用更具体的话说:
每张图片在一行中所占的宽度,应该和它本身的宽高比成正比
宽高比越大(越横),分到的宽度就越多;宽高比越小(越竖),分到的宽度就越少
因此,在等高布局下,可以把每张图片的宽度理解为:
每张图片的宽度占比 ≈ 自身宽高比 ÷ 所有图片宽高比之和
其中:
宽高比 = 宽 / 高
基于这个思路,我没有让 Flex 自由分配空间,而是先计算好每张图片应占的宽度,再通过 flex-basis 精确控制布局
具体实现上,会先异步获取每张图片的宽高比,计算出总比例后,将容器宽度按比例拆分。同时考虑到图片之间的间距(gap),使用 calc() 对最终宽度进行修正,避免累计误差
// src/modules/imageGrid.ts
/**
* 异步更新拼图项宽度
* 基于图片宽高比计算宽度,使得所有图片高度一致
*/
async function updateGridDimensions(images: HTMLImageElement[], gridItems: HTMLElement[]) {
const ratios: number[] = [];
// 获取所有图片的宽高比
for (const img of images) {
const ratio = await resolveImageRatio(img);
ratios.push(ratio);
}
// 计算总比例
const totalRatio = ratios.reduce((sum, r) => sum + r, 0);
const gapCount = gridItems.length - 1;
// 设置每张图片的宽度百分比
gridItems.forEach((item, index) => {
if (totalRatio > 0) {
const ratio = ratios[index];
const percent = (ratio / totalRatio) * 100;
// 使用 calc 计算实际宽度:(比例% * 100) - (gap总宽 * 比例占比)
// 公式: calc(33.33% - (2 * var(--gap) * 0.3333))
const widthCalc = `calc(${percent}% - (${gapCount} * var(--photosuite-grid-gap, 4px)) * ${ratio / totalRatio})`;
// 设置 flex-basis 和 max-width
item.style.flex = `0 0 ${widthCalc}`;
item.style.maxWidth = widthCalc;
}
});
}
这样一来,不管图片比例多么悬殊,它们在拼图中都会自然对齐
高度一致、宽度合理、边缘整齐,同时也避免了任何形式的拉伸或裁剪
样式只负责布局:Flex + gap + 响应式间距,没有额外装饰,所有空间都留给图片本身
这里通过 CSS 变量统一管理间距;在移动端则适当缩小 gap,以保证有限屏幕宽度下的视觉紧凑度
// src/styles/image-grid.scss
/* 拼图容器 */
.photosuite-grid {
--photosuite-grid-gap: 4px;
display: flex;
gap: var(--photosuite-grid-gap);
width: 100%;
margin: 0;
padding: 0;
align-items: flex-start;
}
/* 移动端保持拼图布局,但减小间距 */
@media (max-width: 768px) {
.photosuite-grid {
--photosuite-grid-gap: 2px;
}
}
在交互层面,加了点人情味,当鼠标悬停在图片上时(拼图状态),会有一个轻微的放大效果
注:使用该功能无需配置,默认为
imageGrid: true拼图状态下不显示 EXIF 和标签,它们在编译阶段并未生成
至此,完成
感谢 Claude Code、Gemini、ChatGPT 对项目的大力支持
Photosuite 已发布至 npm,可直接安装:
pnpm add photosuite
# or
npm install photosuite
# or
yarn add photosuite
2026-01-13 00:39:00
晚上刷 1900 的博客,我忽然想起了 主题配色切换动画
随手输入关键字 “主题” 一搜,果然找到了那篇:给博客主题切换加个动画
看到左上角的发布日期时,不禁有些感叹,原来这已经是半年前的事了

实现思路与 1900 类似,本质上是利用 View Transitions API 接管视图更新,从而实现遮罩动画
通过 document.startViewTransition 捕获 DOM 快照,再配合 CSS 的 clip-path(裁剪路径)实现平滑过渡:
const transition = document.startViewTransition(() => {
themeValue = themeValue === "light" ? "dark" : "light";
setPreference(true);
});
transition.ready.then(() => {
// 从上至下的裁剪路径
const clipPath = [
'inset(0 0 100% 0)', // 开始:底部被完全裁剪
'inset(0 0 0 0)', // 结束:完全显示
];
document.documentElement.animate(
{ clipPath: clipPath },
{
duration: 1000, // 长动画,可能会掉帧
easing: "ease-out",
pseudoElement: "::view-transition-new(root)",
}
);
});
此外,我还给博客增加了两个功能按钮,灵感借鉴自 SeerSu,他的博客设计非常线性,我很喜欢
class="fixed bottom-6 md:bottom-8 z-50 w-10 h-10 rounded-md bg-white/40 dark:bg-gray-900/40 backdrop-blur-md border border-white/50 dark:border-gray-700/50 text-gray-700 dark:text-gray-200 shadow-sm hover:bg-white/70 dark:hover:bg-gray-800/70 hover:scale-110 hover:shadow-lg transition-all duration-300 ease-out translate-y-8 opacity-0 pointer-events-none flex items-center justify-center gap-1"
整体视觉设计上,采用了玻璃风格,悬停时有缩放效果
当页面向下滚动到一定范围,该按钮才会浮现,并自动调整“返回按钮”的位置
// 监听 BackToTop
window.addEventListener("backToTopVisibilityChange", e => {
// 如果回到顶部按钮出现,返回按钮自动向上移动让出位置
updateVerticalPosition(e.detail.visible);
});