先把我想看到的那张表贴上。
struct rail_clip {
const char *country; /* "FI" */
const char *from; /* "Helsinki" */
const char *to; /* "Tampere" */
int forward; /* 1 = 镜头朝行进方向 */
int fps;
const char *yt_id;
};
三十几个字节,六个字段。没有一个是为那层车厢画框服务的。全是为「这是哪趟车」服务的。
我没读过 Railpass 的源码。一个嵌 YouTube 的网页,车厢画框里播列车前视录像,这周在 HN 上被翻出来,条目 50201414。但我敢打赌它没有这张表。
它有的是一堆 URL。
一个 URL 装不下这段画面朝哪开。也装不下这趟车从哪到哪。更装不下的是——这到底是机场快线,还是某人赶飞机路上顺手拍的通勤车。
所以评论区那些看着八竿子打不着的抱怨,是同一处窟窿的不同漏法。选德国,画面上方那块滚动显示牌写着纽约附近的地名。选芬兰,标着赫尔辛基机场,实际停靠 Lempäälä 和 Toijala,离机场一百五十公里,没有直达车。波兰那条是倒着放的。有人连点六次,五次座椅背对行进方向,也没个把座椅转过来的开关。雨雪的方向和车开的方向对不上。
有人说是素材挑错了。我不这么看。
评论区有人把芬兰那条解释清楚了:Lempäälä 和 Toijala 在坦佩雷到蒂库拉一线,去机场的人常在那条线上换通勤车,那段素材很可能是某个人完整的一趟机场行程,从家门口一路拍到航站楼。
这个解释比「挑错了」狠。它说明素材本来有语义,语义在拍摄者脑子里,作者手里没有,最后只剩一个 id。信息不是没被采集到,是压根没有地方放。
那块滚动显示牌更露骨。它显然是页面自己画的,不是视频里的内容。
<!-- 那块牌子,跟素材没有任何关系 -->
<div class="dot-matrix">Next stop: Newark</div>
它没从素材读到任何地名,所以只能写死,或者从别处拿一个。哪种都在说同一件事:作者手上没有任何东西知道这段视频是哪条线。
第二件事是随机。
反复点同一个国家,会不断拿到新线路。讨论里有人专门问:能不能指定某一条。
在这个设计里,这个问题不可表达。没做,也做不了——你手上是一堆无序 URL,除了「是不是德国的」,你问不出任何问题。随机于是成了唯一能做的事。
const pick = clips[Math.floor(Math.random() * clips.length)];
随机不是体验设计。它是没有模型的时候,最省事的那一步。洗牌一行就写完。可洗牌洗不出「我想坐一段往北开的车」。
那张表要是真在,同一个需求就长这样:
const pool = clips.filter(c =>
c.country === want &&
c.forward === 1 &&
c.to === dest);
const pick = pool[Math.floor(Math.random() * pool.length)];
代码没变长。变长的是 filter 里的条件。加一条 forward === 1,你就多了「永远正向」这个能力。加一条对 fps 的判断,你就多了「只看清楚的」这个能力。每多一条,都对应你现在缺的那块。
朝向那块有个看着很聪明的偷懒解法,其实不行:
.frame iframe { transform: scaleX(-1); }
翻一下,车往右开,座椅也正了。但画面里的字全反。铁路前视视频里最不缺的就是字——站名牌、线路牌,还有贴在车窗上的线路图。翻一次,全成镜像。错误从「方向不对」变成「方向对了但所有字都是反的」。
镜像不解决朝向。它只是把朝向换了个错法。
真正的解法还是那一个字段。forward = 0 的素材不该进候选池。就算要进,也得标出来,给人一个按钮。按钮能存在的前提,依然是有那个字段。
然后是我想说的正题:这项目的复杂度花错了地方。
它自己原创的是什么?那层画框——车窗、座椅、车厢内景,把视频裹在里头。这是它唯一的「设计」。评论区有人明确说,希望能把这层去掉,只留原始画面和声音。
你唯一原创的工作,用户的第一反应是要求删掉。
画框本身不复杂,纯 CSS,几行写完。问题是它换来什么。它想营造「坐在车里」的感觉。可「坐在车里」的前提,是你知道自己坐在哪趟车上、朝哪边坐。画框解决「看起来像车厢」,解决不了「我知道自己在哪」。前者是皮肤,后者是数据。
画质那条也一样。有人说它分辨率和帧率明显低于 YouTube 上那些原片。它没辙——它不是拍摄方,只是嵌了别人的视频;又因为没有 fps 这种字段,连「挑一段清楚的」都做不到。
性能那条我不猜。有用户说这页会严重拖慢标签页。我没看过它的代码,不知道卡在哪,不装懂。
说句题外话。那个帖后面一路跑到了 GDPR:第 2(2)(c) 条的个人活动豁免、13/14 条的告知义务、85 条留给成员国的空间,还有欧洲法院那几件案子和荷兰一个地方法院的裁决——法院把「照片可能经 Facebook 传给第三方」当成了否定家庭豁免的理由之一。有人拿英国说公共场所拍摄无需同意,有人拿奥地利说行车记录仪卡得死死的。
我读这段觉得挺有意思。一个东西只要开始播别人拍的画面,问题就从 CSS 跑到法律去了。而作者准备的显然只有 CSS。面部要不要模糊,传到 YouTube 之后豁免还在不在,成员国之间尺度差多远。这些都不是周末项目该回答的问题,我也没有答案。但它反过来证明一件事:复杂度全压在最好做的那一层。最好做的那层做得再细,也挡不住下一层冒出来的问题。
拉回来。
评论区还有一条我挺喜欢:最真实的步行模拟器是打开自家前门走到街上,只是多人模式还有待改进。
这个笑话成立,是因为它说中了。模拟器卖的从来不是画面,是「我在别处」。那层画框也想卖这个,但它手上只有一堆没有注释的 URL。
所以最后就一条规则。凡是你要「随机播放」的东西,先问一句:我要随机的那些维度,有没有一个结构体能装下。装不下的,不叫随机,叫瞎。
三十几个字节的事。
