编程加

《今天吃什么》的实现(上)

2021年3月14日

《今天吃什么》中的效果用 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>

遮罩 🔗

为了让候选项滚动到边界的时候不那么突兀,我在候选项上方盖了一层蒙版。如果你仔细观察,会发现《今天吃什么》中的候选词滚动到上下边缘的时候有一层渐变遮罩:

实现原理很简单,复制一份「蓝色渐变背景」到最上层,调整渐变色,在淡入淡出的位置添加线性插值,并将不透明度渐变到 0。大多数软件在添加颜色时会自动计算插值,不需要手动计算。

至此,背景和遮罩就完成了。

篇()幅()原()因(),候选项部分请听下回分解。

关注我,第一时间获得推送。如果觉得还不错,点个「在看」是对我最好的鼓励!

编程加公众号