记一次活动页中css动画实现过程中遇到的问题

最近接到一个需求,做一个活动页上下滑动翻页,并且有很多动画效果,比如:翻页后文字渐显、流星划过、火车不断驶过、人物的头发和领导不断飘动,门里有云彩不断飘过等等。

众所周知,css动画效果可以通过两种方法实现:transition 和 animation+keyframes。
transition: 用来给元素添加过渡效果,无法循环播放。
animation+keyframes: 定义开始和结束状态,系统自动添加过渡效果,可以循环播放。
不足之处是:
1、性能不好
2、兼容性存在问题
3、实现起来比较繁琐
4、效果与设计有差异,需设计师反复校验,会增加开发时间。

现在还有另一种实现方式:json动画。这种方式需要设计师在AE中制作动画并导出json,前端开发人员使用lottie-web即可。
这种方式需要设计人员知道如何制作AE动画。

我这个需求最后采用的方案是css动画。下面记录一些效果的实现方法和开发过程中踩过的坑。

动画效果无法在翻页动画结束后触发

swiper提供的 slideChangeTransitionEnd 事件在淡入切换时无法触发(effect: 'fade'),只能使用* slideChange*方法,这样造成翻页动画结束时,我们定义的动画已经开始了。我的方法是给将动效写在active中,给所有需要动效的元素一个属性,然后延迟改变classname。

.a {
  &.active {
    animation: spreadOut 1s linear infinite;
  }
}
const duration = 300;
function fnName($el) {
  // 其他操作
  $el && setTimeout(function () {
    $el.find('[data-trigger="true"]').addClass('active');
  }, duration);
}
siwper 中存在自由翻页

已知swiper阻止了默认的touch事件,所以我们主需要在子元素中阻止touch冒泡即可。
副作用是阻止冒泡区域内无法滑动翻页,优化方法是只有在子内容高度超出父元素是才组织冒泡。

$('[data-selector="stop-swiper"]')
   .on('touchstart', function (e) {
     e.stopPropagation();
   })
   .on('touchmove', function (e) {
     e.stopPropagation();
   });
相同的动画效果在后几页出现闪烁、跳动

因为要给设计展示实现效果,所以显示了全部页面。结果在前几页中正常的动画在后几页无法正常展示。这个问题也困扰了我很久。后来发现去掉几个页面之后问题就不存在。猜测是因为页面dom结构太多导致的。

人物头发和领带飘动效果

这个效果通过切换背景图的方式实现的。
遇到的问题是,每张背景图第一次渲染的时候出现闪烁的情况,猜测是图片展示时处在渲染过程中导致的。解决思路是图片预渲染。
中间尝试通过将背景图全部一次性渲染出来,通过translate改变父元素的位置,但是改变过程中会出现过渡效果。
在部分机型上可以通过带小数的百分比来取消过度,但是ios上还是不行。

25% {
  transform: translate(0, -100%);
}
25.001% {
  transform: translate(0, -200%);
}

最后的解决方案是,将图偏设置为透明元素的背景图,来达到预渲染的效果。

行驶的列车

设计的效果是页面展示时一辆车从右侧驶出,然后火车一直行驶。


效果展示

最初方案是两倍原图宽度的div让火车做从左至右的循环运动,当运动到一半即刚驶过一辆车的位置时返回到初始位置。

  0% {
    transform: translate3d(0, 0, 0);
  }
  100% {
    transform: translate3d(50%, 0, 0);
  }

但是实际测试中发现ios手机上出现跳动,移动的距离还未到自身的一半就返回了第一帧,只播放一次的时没问题。
如果需要循环播放则需要在开始和结束增加一段时间来维持状态。

  0% {
    transform: translate3d(0, 0, 0);
  }
  1% {
    transform: translate3d(0, 0, 0);
  }
  99% {
    transform: translate3d(50%, 0, 0);
  }
  100% {
    transform: translate3d(50%, 0, 0);
  }

但是需求这么做会导致动画有一瞬间的停止,所以我将一个div车拆成了两个

.animate-train {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 1075px;
  height: 188px;
  &.behind {
    right: 1075px;
    &.active {
      animation-name: trainBehindLeft2Right;
    }
  }
  &.active {
    animation: trainLeft2Right 2.2s linear infinite forwards;
  }
}
@keyframes trainLeft2Right {
  0% {
    right: 0;
    transform: translate3d(0, 0, 0);
  }
  50% {
    right: 0;
    transform: translate3d(110%, 0, 0);
  }
  51% {
    right: 1075px;
    transform: translate3d(0, 0, 0);
  }
  100% {
    right: 0;
    transform: translate3d(0, 0, 0);
  }
}
@keyframes trainBehindLeft2Right {
  0% {
    right: 1075px;
    transform: translate3d(0, 0, 0);
  }
  100% {
    right: 0;
    transform: translate3d(105%, 0, 0);
  }
}
水波纹扩散效果

水波纹扩散效果重点在于波纹扩散效果,先快速扩大然后扩散速度降低并逐渐消失。

@keyframes spreadOut {
  0% {
    opacity: 0.1;
    transform: scale(0.4, 0.4);
  }

  80% {
    opacity: 1;
    transform: scale(0.9, 0.9);
  }

  100% {
    opacity: 0.1;
    transform: scale(1, 1);
  }
}
音乐无法自动播放、无法循环播放

audio部分手机上需要触发用户操作才能播放,但是微信内是可以自动播放的。
所以我们只在微信内外打开的场景下才会自动播放。音乐使用mp3格式即可兼容全部机型。
ios上无法循环则是通过在audio结束通过js再次播放来解决的。

const audo = document.querySelector('audo');
audo.addEventListener('ended', function () {
   audo.load();
   audo.play();
});

audio标签不能加loop属性,否则不会触发ended事件

隐藏网页后音乐仍然播放问题

这个问题其实存在
解决思路是,通过监听visibilitychange事件获取当前页面的状态

// 隐藏时暂停
(function () {
  let vibibleState = '';
  let visibleChange = '';
  const { visibilityState, webkitVisibilityState } = document;
  if (typeof visibilityState !== 'undefined') {
    visibleChange = 'visibilitychange';
    vibibleState = 'visibilityState';
  } else if (typeof webkitVisibilityState !== 'undefined') {
    visibleChange = 'webkitvisibilitychange';
    vibibleState = 'webkitVisibilityState';
  }

  if (visibleChange) {
    document.addEventListener(visibleChange, function () {
      const status = document[vibibleState];
      if (status === 'hidden') {
        sound.pause();
      }
    });
  }
}());

// 退出时暂停
window.addEventListener('unload', function () {
        sound.pause();
});

用户杀掉浏览器进程后音乐仍然会播放,对于这种情况是没有办法处理的

今后css动画过程中需要注意的地方

  • 优先使用json动画;
  • 减少循环播放的动画效果;
  • 减少动效数量、同时展示的动效数量;
  • 注意页面复杂度,减少页面数量、精简dom结构;
  • 不要滑动翻页中嵌入自由滑动;

你可能感兴趣的:(记一次活动页中css动画实现过程中遇到的问题)