帮助我们优化前端的4个技巧

精美的动画早已融入设计趋势。 UI设计人员制作精美的轮播,下载,菜单动画和其他装饰,前端开发人员将其转换为代码。 但是该站点不仅应该看起来不错,而且可以快速工作。


现代的前端应该优化其代码。 对于大多数观众从移动设备访问该网站的产品而言,尤其如此。 某些动画方法即使在顶级计算机上的Chrome中也滞后,但在普通的智能手机上应该可以正常使用。


我们的开发人员使用了大量技术来优化网站并加快其工作速度。 我收集了其中最有趣的4个。 我们分享对初学者和专业人士有用的知识,并提供指向有用教程的链接。



1. SCSS上的动画


该网站有很多动画。 我们需要浏览器以60 fps的稳定帧速率播放它。 在纯CSS中,这很难做到,因此我们使用SCSS。


要创建滑块,我们使用了Swiper库。 对于水平滑块,使用此库是合理的,因为我们需要向用户提供对svayp的支持。 但是对于垂直的无休止的旋转木马,则不需要Swiper,也不需要用户交互。 因此,我们只想使用CSS功能来重复相同的功能。


使用CSS动画时,第一个也是最重要的条件是仅使用transform和opacity属性。 浏览器能够独立优化这些属性的动画并产生稳定的60 fps。 但是,单独使用@keyframes不可能为不同的元素编写不同的动画,并且在纯CSS中单独为每个元素设置动画非常耗时。 如何快速编写所需的动画? 我们选择了SCSS,即SASS方言-一种功能更强大的CSS扩展。


让我们使用垂直滑块的示例来分析SCSS的使用。



我们可以使用一个容器,该容器的高度等于转盘上三个元件的高度。 里面是另一个包含轮播的所有元素的容器。


<div class="b-vertical-carousel-slider"> <div class="vertical-carousel-slider-wrapper slider-items"> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> </div> </div> 

我们删除将超出其范围的容器元素的可见性,并设置块的高度。


 .b-vertical-carousel-slider { position: relative; overflow: hidden; height: $itemHeight * 3; .vertical-carousel-slider-item { height: $itemHeight; } } 

仅当轮播中元素的数量发生更改时,动画的计算才会更改。 接下来,我们编写一个带有一个输入参数的$itemCount


 @mixin verticalSlideAnimation($itemCount) { } 

在mixin中,为每个元素生成关键帧,为其设置初始状态,然后使用:nth-child确定该元素的动画。


 for $i from * 1 through $itemCount { $animationName: carousel-item-#{$itemCount}-#{$i}; @keyframes #{$animationName} { 0% { transform: translate3d(0, 0, 0) scale(.95); } } .vertical-carousel-slider-item:nchild(#{$i}) { animation: $stepDuration * $itemCount $animation ease infinite; } } 

此外,在动画期间,我们将仅沿y轴移动元素,并更改中心的元素比例。


轮播元素状态:


  1. 平和
  2. Y偏移
  3. Y偏移
  4. Y偏移随着减少

每个项目将移动$itemCount次,移动一次增加一次,减少一次。 因此,我们将为每个向上运动生成并计算动画。


 @keyframes #{$animationName} { 0% { transform: translate3d(0, 0, 0) scale(.95); } @for $j from 0 through $itemCount { $isFocusedStep: $i == $j + 2; $isNotPrevStep: $i != $j + 1; $offset: 100% / $itemCount * ($animationTime / $stepDuration); @if ($isFocusedStep) { #{getPercentForStep($j, $itemCount, $offset)} { transform: getTranslate($j - 1) scale(.95); } #{getPercentForStep($j, $itemCount)} { transform: getTranslate($j) scale(1); } #{getPercentForStep($j + 1, $itemCount, $offset)} { transform: getTranslate($j) scale(1); } #{getPercentForStep($j + 1, $itemCount)} { transform: getTranslate($j + 1) scale(.95); } } @else if ($isNotPrevStep) { #{getPercentForStep($j, $itemCount, $offset)} { transform: getTranslate($j - 1) scale(.95); } #{getPercentForStep($j, $itemCount)} { transform: getTranslate($j) scale(.95); } } } } 

仍然需要定义一些变量和函数:


  • $animationTime补间$animationTime时间
  • $stepDuration一个动画步骤的总执行时间( $animationTime +轮播休息时间)
  • getPercentForStep($step, $itemCount, $offset) -一个以百分比形式返回状态之一极端值的函数。
  • getTranslate($step) -根据动画步骤返回翻译

函数实现示例:


 @function getPercentForStep($step, $count, $offset: 0) { @return 100% * $step / $count - $offset; } @function getTranslate($step) { @return translate3d(0, -100% * $step, 0); } 

我们有一个工作传送带,中间的元素越来越多。 在不断增加的要素下仍然有阴影。 最初,轮播中的每个元素都有一个伪元素:之后,又有一个阴影。 为了不对shadow属性设置动画,我们为其使用了opacity属性。 只是显示并隐藏了阴影。


但是在这种解决方案的新实现中,您需要为每个伪元素生成许多其他关键帧。 我们决定做得更容易:将有一个带有阴影的块,并且它将在转盘的中间元素正下方占据空间。


添加一个负责阴影的div。


 <div class="b-vertical-carousel-slider"> <div class="vertical-carousel-slider-wrapper"> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> </div> <div class="vertical-carousel-slider-shadow"></div> </div> 

我们对其进行样式化并添加动画。


 @keyframes shadowAnimation { 0% { opacity: 1; } 80% { opacity: 1; } 90% { opacity: 0; } 100% { opacity: 1; } } .vertical-carousel-slider-shadow { top: $itemHeight; left: 0; right: 0; height: $itemHeight; animation: $stepDuration shadowAnimation ease infinite; } 

在这种情况下,您无需生成和思考为每个元素下的阴影设置动画的方法,我们只需为一个块设置动画,它具有两种状态-可见和隐藏


结果,我们有了一个纯CSS轮播,它的动画只有经过优化的属性。 这允许浏览器使用硬件加速进行渲染。 因此,与JS动画相比,有形利润是:


  1. 在弱设备上滚动带有动画的页面时,在JS动画中明显跳过了帧,将FPS降至15-20。 CSS动画已明显改善。 在这些设备上,该数字至少为50-55 FPS。
  2. 我们摆脱了不需要第三方模块的工作。
  3. 即使禁用JS也会播放动画

如果需要严格控制每个帧,则应使用JS动画:暂停,倒带播放,倒带,对用户操作的反应。 在其他情况下,我们建议使用纯CSS。


有用的链接



2.使用Intersection Observer API


站点访问者看不到的动画在看不见的地方播放并加载CPU。 使用“交集观察器”,我们确定当前在屏幕上可见哪种动画,然后只播放它。


最后一段的所有技巧都可以与“交叉口观察器”结合使用。 此工具有助于避免向浏览器加载网站访问者看不到的动画。 以前,使用资源密集的事件“侦听器”来了解访问者是否正在观看动画元素,而这并不会产生强烈的“疲惫”。 在视口外使用动画和使用侦听器之间的差异很小。 Intersection Observer API需要较少的资源,并且有助于仅播放访问者可见的动画。


在我们的网站上,仅当元素出现在视口中时才会激活动画。 如果我们不这样做,则页面的超载将导致不断执行的循环动画无法显示。 Intersection Observer API使您可以监视元素与父级或文档范围的相交。


实施实例


例如,我们展示了如何在JS上优化动画。 这个想法很简单-在动画元素在视口中的同时播放动画。 为了实现,我们使用Intersection Observer API。


在样式中添加is-paused类处理


 .b-vertical-carousel-slider.is-paused { .vertical-carousel-slider-wrapper { .vertical-carousel-slider-item { animation-play-state: paused; } } .vertical-carousel-slider-shadow { animation-play-state: paused; } } 

即 当出现此类时,动画将暂停。


现在我们描述添加和删除此类的逻辑


 if (window.IntersectionObserver) { const el = document.querySelector('.b-vertical-carousel-slider'); const observer = new IntersectionObserver(intersectionObserverCallback); observer.observe(el); } 

在这里,我们创建了一个IntersectionObserver实例,指定了intersectionObserverCallback函数,该函数将在可见性更改时起作用。


现在定义intersectionObserverCallback


 function intersectionObserverCallback(entries){ if (entries[0].intersectionRatio === undefined) { return; } helperDOM.toggleClass(el, 'is-paused', entries[0].intersectionRatio <= 0); }; 

现在,仅在可见的那些元素上播放动画。 一旦元素从视野中消失,动画就会暂停。 当访客回到他身边时,播放将继续。


有用的链接



3. SVG渲染


加载大量图像或使用精灵会在加载后的头几秒钟内引起fr带和延迟。 在页面代码中嵌入SVG有助于从视觉上使加载更加平滑。


选择用于处理图像的方法时,我们有2个优化选项:将SVG嵌入HTML或使用精灵。 我们决定嵌入。 我们将每个图像的XML代码直接嵌入到页面的HTML代码中。 这会稍微增加它们的大小,但是SVG会立即随文档内联。


许多开发人员继续使用SVG精灵。 该方法的本质是什么:将图像阵列(例如,图标)收集在一个大的图像画布中,这称为子画面。 当需要显示特定的图标时,将调用一个精灵,然后给出其所在的特定工件的坐标。 即使在第一个HTTP版本上,此操作也已经进行了很长时间。 Sprites有助于积极地缓存文件并减少服务器请求的数量。 这很重要,因为许多同时请求都降低了浏览器的速度。 使用SVG精灵是一个典型的拐杖,为了节省资源,您忽略了它的工作逻辑。 现在请求的数量不是那么重要,因此我们建议嵌入。


首先,从访问者的角度来看,它会对性能产生积极影响。 他看到了图标如何立即加载,并且在加载页面后的头几秒钟内不会受到影响。 使用Sprite或PNG图像时,正在加载的页面会变慢一点。 如果访问者立即滚动加载的页面,尤其会感到这种感觉-在非顶级设备上,FPS会降至5-15。 在HTML中嵌入SVG有助于减少页面等待时间(从客户端的角度来看是主观的),并消除了加载过程中的fr带和跳帧。


4.使用Service Worker和HTTP缓存进行缓存


重新加载未更改的页面并使用访问者流量没有任何意义。 有很多缓存策略,我们选择了最有效的组合。


不仅要优化CPU / GPU的使用,还应优化网络的使用。 移动设备不仅在资源方面,而且在Internet速度和流量方面都是一个限制。 缓存在这里帮助了我们。 它使您可以保存对HTTP请求的响应并使用它们,而无需再次收到服务器的响应。


在考虑缓存策略时,我们选择同时使用Service Worker和HTTP Cache。 让我们从第一个和更高级开始。 Service Worker是一个js文件,可以控制其页面或文件,拦截和修改请求以及以编程方式缓存请求。 它充当站点和服务器之间的代理,并确定它们的脱机行为。 所有这些都是在“前端”完成的,而无需连接“后端”。


服务人员具有巨大的可变性。 我们可以根据需要对行为进行编程。 例如,我们知道访问第1页的访问者有90%的概率将访问第2页。 当访问者仍在第一页上时,我们要求SW加载第二页的背景。 当他进入页面时,页面将立即加载。 它可以用于不同的任务:


  • 后台数据同步
  • 离线计算器
  • 自定义模板
  • 对特定时间和日期的反应。

可以在不同的服务中创建Service Worker文件。 我们建议使用Workbox 。 这非常简单,它允许您创建一组规则来执行缓存,例如预缓存。


服务人员不支持40.0之前的所有浏览器,例如IE,Safari或Chrome。 如果设备无法使用它,它将遵循HTTP缓存缓存规则。 我们在页面中添加了以下HTTP标头:


 cache-control: no-cache last-modified: Mon, 06 May 2019 04:26:29 GMT 

在这种情况下,浏览器将对请求的响应添加到存储库,但随后的每个请求都发送一个标头以检查更改。


 if-modified-since: Mon, 06 May 2019 04:26:29 GMT 

如果未发生任何更改,则浏览器将收到代码304未经修改的响应,并使用存储在缓存中的内容。 如果对文档进行了更改,则响应将返回代码200,并将新的响应写入浏览器存储。


稍后,我们更改了缓存方法,并检查了文件名中的哈希值。 它确保资源将保持唯一。 因此,我们可以积极地缓存内容。 作为响应,添加了以下标头:


 Cache-control:max-age=31536000, immutable 

Max-age表示最长缓存时间,在本例中为1年。 不变值表示不需要检查此类答案是否有更改。


有用的链接



这些并不是优化站点的全部方法。 这可能会增加对引导程序的拒绝,从而减少DOM元素和事件的数量等等。 但是,尽管有大量的动画,但是这些都是帮助我们使网站快速响应的提示。


我们邀请您加入我们的团队


我们一直在圣彼得堡办公室寻找出色的专家来完成我们的雄心勃勃的任务:开发人员,测试人员,设计师。 下面有空缺-加入我们。


Source: https://habr.com/ru/post/zh-CN455700/


All Articles