整屏铺着一张照片,上面盖着一副相机快门:十片黑色叶片首尾相接围成一个多边形孔洞,只露出中间一小块画面。鼠标在页面上左右移动,孔洞就跟着变大变小,像真的在调光圈。左上角有一个「显示辅助线」开关,打开后会用红色画出基准圆、正十边形的顶点和连线,直接把这个效果的几何原理摊在画面上。技术上是原生 Canvas 2D,没有用任何图形库,脚本不到一百五十行。
十片叶子是怎么拼成光圈的
叶片其实只画了一次。脚本先在一张离屏画布上画好一个三角形,再把这一张图平移旋转十次盖到十个顶点上,所以叶片数量再多也不增加绘制成本。
顶点按正多边形算。十个顶点均匀分布在同一个圆上,圆心固定在屏幕中心,每个顶点的角度是整圆除以边数再乘序号。
开合只是在改半径。鼠标移动时把横坐标折算成圆的半径重新算顶点,叶片跟着往外散或往里收,孔洞自然变大变小。
三角形为什么要按外角来转
每片叶子贴到顶点上之后还要转一个角度,才能和相邻叶片严丝合缝。这一行决定了转多少:
ctx.rotate(-Math.PI / 2 - exterior_angle / 2 - exterior_angle * i);
先减去四分之一圈把三角形从水平摆正到竖直,再减半个外角让它对准两条边的角平分线,最后按序号逐片递增一个外角。三项缺一项,叶片之间就会露缝或者互相压住。离屏三角形的尺寸取的是屏幕长边,这样无论窗口多大,叶片都能盖到画面之外,不会在角落漏光。
能改的地方
叶片数量是脚本开头的一个常量,改成 6 或 8 就是另一种光圈造型,其余计算会自动适配。叶片的颜色与描边在生成离屏三角形的那段渐变里;底图换成自己的照片只需改样式里的背景图路径。想做成点击触发的快门动画,把跟随鼠标的那段换成一个按时间推进的半径插值即可。
常见问题
叶片边缘有缝隙或者重叠?
多半是改了叶片数量却没让旋转角度跟着变。外角是由边数算出来的,只要通过那个常量修改,两处会一起更新;直接写死角度就会对不上。
手机上能用吗?
可以。交互监听的是 pointermove,触摸拖动同样会触发;样式里也给画布加了 touch-action 处理,拖动时不会误触发页面滚动。
可以换成遮罩别的内容吗?
可以。画布是覆盖在页面之上的独立层,底下放视频、轮播图或整页内容都行,快门只负责遮挡,不关心被遮的是什么。
适合用在哪
摄影类站点的首屏、相机与镜头产品的介绍页、图集的进场过渡,以及需要「聚焦」隐喻的开场动画。多边形顶点计算与叶片复用的完整写法都在脚本里,下载后可查看。