《今天吃什么》中的效果用 CSS + JavaScript 不难实现。
不过,为了确保安全性,微信不会让我们为所欲为。要在公众号中做出漂亮的排版或者实现酷炫的效果,就得学会戴着镣铐跳舞。
今天就来教大家怎样在不使用 JavaScript 的情况下实现《今天吃什么》中的效果。
核心部分是一张矢量图──没错,滚动动画和点击交互都在这张图里了。
我们平常网络上见到的图片基本都是位图(bitmap),像是 JPG、PNG、GIF,存储的是一个个像素点的颜色。而矢量图(vector graphics)就像我们在直角坐标系当中画图一样,存储的是一个个坐标点形成的图形,比如,有一个圆心在 (2, 2)、半径为 1 的圆。
矢量图不会因为缩放产生锯齿(字体中的一个个字符也是矢量图,所以你可以在 Word 里把字号设置得非常大),随着智能设备屏幕越来越高清(PPI 越来越高),开发者也越来越倾向于用矢量图代替位图。SVG 就是一种常见的矢量图格式。
下面的就描述了一张宽(width)、高(height)为 100 的 SVG 矢量图。在 (25, 25) 有一个宽、高 50 的矩形,填充(fill)深青色(darkcyan)。
<svg width="100" height="100">
<rect x="25" y="25"
width="50" height="50"
fill="darkcyan">
</rect>
</svg>
实际效果是这样的:
我们可以为矩形增加一个以 (50, 50)(画布的中心位置)为旋转中心、从 0° 旋转到 360°、持续(dur) 2s、一直旋转(rotate)的动画:
<svg width="100" height="100">
<rect x="25" y="25"
width="50"
height="50"
fill="darkcyan">
+ <animateTransform
+ attributeName="transform"
+ type="rotate"
+ from="0 50 50" to="360 50 50"
+ dur="2s"
+ repeatCount="indefinite"
+ />
</rect>
</svg>
看起来就像是这样:
我们甚至可以设置在点击 0.5s 之后才开始(begin)动画:
<svg width="100" height="100">
<rect x="25" y="25"
width="50"
height="50"
fill="darkcyan">
<animateTransform
attributeName="transform"
type="rotate"
from="0 50 50" to="360 50 50"
dur="2s"
repeatCount="indefinite"
+ begin="click+0.5s"
/>
</rect>
</svg>
试着点击下面的方块并等待 0.5s:
当然,通常我们不会手写 SVG,而是用 Figma、Sketch 或是 Adobe Illustrator 这样的可视化工具来绘制。
回到《今天吃什么》,我们可以把整张图看成两层,固定的蓝色渐变背景和移动的候选项。我们把所有候选项竖向排列,然后加上一个不断重复的移动动画,就可以实现无限滚动效果了。
为了让首尾衔接的时候在视觉上没有跳变,第一项「火锅」出现了两次(动图里为了方便看清,两次移动动画之间刻意设置了停顿,实际上是无缝衔接的)。
蓝色渐变背景 🔗
由于公众号正文不支持 id,所以没法用 SVG 实现渐变。但是 SVG 支持 <foreignObject>。我们可以在 <foreignObject> 里嵌入 HTML 元素,配合 CSS 来实现渐变效果。公众号正文不支持 <div>,可以强行使用 <span>。
<svg width="300" height="150">
<foreignObject x="0" y="0"
width="300" height="150">
<span style="display: block;
width: 300px;
height: 150px;
background: linear-gradient(
to bottom,
#51B4F7,
#34A0EA
);
border-radius: 10px">
</span>
</foreignObject>
</svg>
遮罩 🔗
为了让候选项滚动到边界的时候不那么突兀,我在候选项上方盖了一层蒙版。如果你仔细观察,会发现《今天吃什么》中的候选词滚动到上下边缘的时候有一层渐变遮罩:
![](mask-demo@2x.png)
实现原理很简单,复制一份「蓝色渐变背景」到最上层,调整渐变色,在淡入淡出的位置添加线性插值,并将不透明度渐变到 0。大多数软件在添加颜色时会自动计算插值,不需要手动计算。
![](mask@2x.png)
至此,背景和遮罩就完成了。
篇(再)幅(水)原(一)因(篇),候选项部分请听下回分解。
关注我,第一时间获得推送。如果觉得还不错,点个「在看」是对我最好的鼓励!