青协招新推文幕后

今年年初,Apple的几篇推文可谓是刷新了推文制作的上限。因此,在暑期准备青协招新材料的时候,我们就下定决心要把这次的宣传推文做出与苹果的动效设计不相上下的水准。(也就是直接抄)

接下来就是具体的实现了,大家也可以对着青协这篇推文的网页源码来跟着一起理解。

众所周知,网页是由前端HTML,CSS和JavaScript组成的。其中HTML声明了网页的结构及元素,CSS规定了元素的位置及样式属性,而JavaScript则负责网页的交互。初看这次的推文,看起来用JavaScript来控制DOM,并更新window就可以实现整体的页面伸长效果。但从微信公众号开发手册可以得知,微信禁止推文中使用任何JS脚本,因此这条路看上去走不通。

那看起来只能从网页源码入手,这里用Apple的“新一届iPad
Pro,有深度”这篇推文作为例子,来看一看科技顶流是如何在推文中实现顺滑的网页伸缩效果的。

SVG 内的 CSS 动画不仅可以作用于内部路径,也可以作用于 SVG 本身。对 SVG
视窗附加<height>参数动画,就可以让这个 SVG
区块增长或缩短,也就由此使得图文增长或缩短,神奇地控制了页面尺寸。

SVG 动画在微信图文中的能力,是在2016
年,在市面上的一家主流的排版公司及微信团队下制定的《微信下的 SVG
Attribute Name 白名单框定而成的。而创造性提出图文伸长模型的,是业内 SVG
知名开发者 GL 开始制作的,在这之后陆续有品牌都定制过此类互动模型。

首先放上精简后的HTML结构代码

<div>
  <div class="content" style="height: 0px">
    <section> Content 1 </section>
    <section> Content 2 </section>
    <section> Content 3 </section>
    <section> ...</section>
  </div>
  <section class="wrapper">
    <svg height="?px" opacity="0">
      <animate attributeName="height" values="?;?;?" dur="1200s" begin="click + 2.75s" </animate>
      <animate attributeName="opacity" begin="click" dur="1200s" values="0;1;1"> </animate>
    </svg>
  </section>
</div>

其中<class>wrapper<section>里面的元素是一个具有heightopacity两个<animate>属性,因此可以单击后变长的<svg>。而<class>content<div>里面的元素则为实际要展示的内容。content<height>被设置为了0,因此在浏览器看来<content>是“不占空间“的,决定window大小的只有wrapper。所以只要wrapper变长了,那么相应的content的内容就会显示出来了。

而要做到点击后GIF开始播放,则先需要将wrapper调整为透明来展示content中的静态图片,而在点击图片区域后,则调整wrapper的透明度为1,这样wrapper里面的GIF就能显示出来了。因为wrapper里面除了GIF其他部分都是透明度,因此对下面的内容也不会遮盖。

但我们把内容添加到这个精简版后,会发现一些问题:

  • 对于HTML5的默认实现,animate
    Mode为线性动画,这看起来不够自然。要做到流畅的easeInOut动画,应将<calcMode>设置为spline后,配合<keySplines>属性,设置每个时间段内的三次贝塞尔曲线。而高度动画还应通过<keyTimes>微分法,把伸长过程分为了两段瞬间段与长效段),这能有效阻止伸长过程中被二次触发。
  • 在推文头部还需要留下推文头部放GIF的空间,因此还要根据GIF的大小来调整margin-top。

因此完整的wrapper元素应为这样

<svg height=?px" opacity="0" style="background-image: url(...); background-size: contain; width: 100%; height: 375px margin-top: 100px;" viewBox="0 0 1080 375" width="100%">
  <animate attributeName="height" fill="freeze" restart="never" calcMode="spline" keySplines="0.42 0 0.58 1.0; 0.42 0 0.58 1.0" keyTimes="0;0.0035;1" values="375;8340;8340" dur="1200s" begin="click + 2.75s"> </animate> </svg>

推文中还会存在视频组件和各种二维码,这在contentwrapper覆盖后是有问题的,因为二维码在wrapper下面一层,微信长按触发的是wrapper层,因此是无法正确识别二维码的。这时候只需要耍一个小聪明,在相应的div上通过Z轴<transform>给二维码和视频控件提升页面优先级,使得各类被实际覆盖在SVG之下的内容均可以触摸到。

<section style="transform: translateZ(0.01px);"> 视频控件或二维码 </section>