记一次活动页中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结构;
  • 不要滑动翻页中嵌入自由滑动;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352