一整块软木板铺满屏幕,木框、纸纹、颗粒噪点和偶尔一闪而过的暗影都在,角落里还会不定时盖上一枚“机密”印章。板上钉着十二张剪报式卡片,每张有来源、日期、被剪刀裁成一格一格的标题、正文里一段被墨迹涂掉的文字,以及归档与撤下两个按钮。标题上的每个字都带着轻微的随机旋转,像是从别处剪下来贴上去的。
它是能动的:卡片可以直接用鼠标拖到板上任意位置,第一次拖动时整块板会从网格布局切换到自由摆放模式;点“拉线索”,同一分类的卡片之间会拉出红色的线,拖动卡片时线会实时跟着变;点标题弹出档案详情,里面列着来源、登记时间、状态和关联物证;顶部还有按最新、最早、来源排序,以及只看未归档的筛选。想恢复原样就点“重新钉板”。
这块证据板里最见功夫的四处
板面质感全是 CSS 画的。软木的斑点由三层不同尺寸的径向渐变平铺叠加而成,纸纹是一层斜向重复渐变,木框用渐变作为边框图像,再叠一层内阴影压暗四周——没有用任何贴图。
标题被“剪”成了单字。脚本把标题逐字拆成独立元素,样式再用 nth-child 给第三、四、五个字轮换不同底色与旋转角,于是每张卡片的标题都自动呈现出勒索信般的拼贴感,不用手工排。
红线是实时计算的 SVG。开启后按分类分组,取每张卡片顶部图钉附近的坐标依次连线;拖动时每帧重画,所以线永远从图钉出发,不会飘在卡片中间。
拖拽有一次“模式切换”。初始是自动网格排布,第一次按下卡片时脚本先把所有卡片的当前位置量出来固化成坐标,再切成绝对定位——这样从整齐到凌乱的过渡没有任何跳动。
三个值得单独说的实现取舍
其一,自由模式的进入方式。很多拖拽看板一上来就是绝对定位,代价是初始排版要自己算。这里反过来:先用正常布局排好,等用户真的要拖时再快照坐标:
const r = c.getBoundingClientRect();
snap.push({ el: c, x: r.left - pinRect.left, y: r.top - pinRect.top });快照完成后还把容器高度锁成当时的高度,避免卡片脱离文档流导致板子突然塌下去。这个顺序颠倒过来就会看到明显的闪跳。
其二,拖拽和点击要分清楚。卡片既能拖又能点,如果不做处理,拖完松手会顺带触发一次点击。源码用一个标记位记录“刚刚发生过拖动”,在点击处理里拦掉一次并复位。同时按下时先判断落点是不是按钮或链接,是就完全不进入拖拽——这两条加起来,交互才不打架。
其三,涂黑的文字仍然保留着内容。正文中段那几个字被套上黑底黑字的元素,但原文写在 title 属性里,鼠标悬停整张卡片时黑块会转成浅色显出文字。这比直接删掉文字好:既有“被删节”的戏剧感,又不影响可读性和检索。
拿到源码后能改什么
内容在脚本顶部的数据数组,每条包含来源、链接、时间、标题、正文和分类标签。分类标签同时决定两件事:红线怎么分组,以及档案详情里显示什么类别名称——类别名称写在一个映射表里,新增分类时两处一起加即可。
版面上,卡片的随机倾角是由序号推导出来的(取模再偏移),所以每次刷新角度稳定不乱跳;想让板子更凌乱就扩大取值范围。入场动画按序号递增延迟,改那个系数可以控制“一张张钉上去”的节奏。
视觉方面,主色是牛皮纸底加暗红印章色,改这两个色值整块板的气质就变了:换成冷灰加荧光绿是科幻监控风,换成米白加靛蓝更像学术资料墙。字体用了三款外部字体(打字机、粗黑、等宽),换成本地字体时记得同时保留中文回退,否则中文会掉到默认字体。
运行提示:页面从网络加载三款网页字体,离线时会退回系统字体,功能不受影响。脚本为普通脚本引入,理论上可直接双击打开,但建议仍用本地服务器运行以避免个别浏览器的本地文件限制。建议使用近两年版本的 Chrome、Edge、Firefox 或 Safari。详细运行方法请参见压缩包内的帮助文档。
关于这块证据板的常见问题
卡片位置能保存下来吗?
当前位置存在一个内存里的对象中,刷新即丢失。想持久化很容易:在拖拽结束的回调里把这个对象写进本地存储,初始化时读回来即可,改动量只有几行。
能接自己的后台数据吗?
可以。把顶部的数据数组换成一次接口请求的结果,字段对齐后调用渲染函数即可。渲染是整体重绘的,数据更新后重新调用一次就行。
手机上能拖动吗?
能,拖拽用的是 pointer 事件,触摸屏同样有效。不过板面较宽,窄屏下卡片会缩成单列,建议在移动端默认关闭红线,避免连线横跨整屏影响阅读。
卡片多了以后线太乱怎么办?
连线是按分类顺次相连的链式结构,卡片一多难免交错。实用的做法是限制每类只连最近的若干张,或者给线加上透明度渐变——修改绘制连线的那个循环即可。
这套看板适合放在哪里
它非常适合做资讯聚合页、专题报道的线索梳理、产品路线图的可视化墙,也可以用在剧本杀、密室、影视宣传等需要“调查感”的活动页上。板面质感、单字拼贴标题、实时连线与拖拽模式切换的完整实现都在源码里,注释清晰,下载后可查看。