Animating DOM Changes

栏目: IT技术 · 发布时间: 4年前

内容简介:A common misconception in Web Development is that the DOM is slow. The DOM, short forWhat makes changing the DOM slow is once you have changed an element the browser has to measure the new size of each element, then has to redraw all of the elements that c

Smoothly adding, removing, resizing and reordering elements

Animating DOM Changes

Apr 21 ·5min read

A common misconception in Web Development is that the DOM is slow. The DOM, short for Document Object Model , is the structure of the Web Site which your code interacts with. If you were to ask me whether the DOM is slow I would answer that, like most things in computer science, it depends on the circumstances.

What makes changing the DOM slow is once you have changed an element the browser has to measure the new size of each element, then has to redraw all of the elements that changed. This is always moderately expensive but modern browsers are smart about caching these calculations, invalidating as little of that cache as possible once a change is made and only doing the calculations at the last possible moment. So if you make a change to the page the browser won’t measure the new sizes until you request for it to measure them or it has to redraw the page for the next frame. If nothing has changed then it does not need to remeasure, it can use the cached values.

In real terms what this means is that when you have to change lots of the DOM’s layout in one go, you can cheaply measure the size of elements if you measure them all together, and you can cheaply make changes if you do all your DOM changes together. Interleaving reads and writes is when the expense can get out of hand.

Therefore when doing large many element animations where performance is important it’s important to take all your measurements in one go before you start making changes. That is the secret sauce behind efficiently animating changes made to the DOM.

NB:Please remember it’s important to respect user preferences for reduced motions, for some users lots of animation can make a site unusable and make them ill. So always be prepared to turn it off like using the example below.

const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;if (prefersReducedMotion !== true) {
  // do animation
}

The rest of this article is about building the animation for when the DOM changes. We will do it using the following method:

  1. Measure the position of all the things that will move. (reads)
  2. Perform all of your DOM changes (writes)
  3. Measure the new position of everything (reads)
  4. Use inexpensive composite effects, such as transform , opacity or clip-path to place elements back at their previous size position.
  5. Create an animation which smoothly animates from the starting point back to the endpoint. The end point in this case is what is already in the DOM.

In this the DOM needs to be recalculated at most twice, once before we take the first measurements and once after we have made our changes and we need to measure how things have changed.

Before we cover this in depth, here is a page where I test this technique in different scenarios:

Animating DOM Changes

A gif of the demo.

We use the same technique for adding elements and changing between different layout modes in each of the demos in the example page.

Measure all the things

First, we need to know the original position of the elements that will change. We are going to add an element into parent so we will measure the position of all it’s children.

// Turn an array of elements into a map of elements to their
// respective bounding boxconst boundingBoxMap = new Map(
 Array.from(parent.children).map(
   el => [ el, el.getBoundingClientRect() ]
 )
);// boundingBoxMap.get(parent.firstElementChild);
// DOMRect { x: 8, y: 21.4, width: 606, height: 38, top: 21.4, right: 614, bottom: 59.4, left: 8 }

Now we can make any changes we like, we can add, remove and reorder elements. Change classes and styles.

We then measure everything again using the same method as above.We now know the start and end points of the animation.

The trick is to then make a new animation that starts at the old point, and animates to the new point. We can do this using some math.

To get how the position has changed we will subtract the start position from the end position. This is the transform makes the element appear to be back where it came from:

const translateTransform = `translate(${oldPos.x - newPos.x}px, ${oldPos.y - newPos.y}px)`;

Animating size is a little trickier, we could scale the element or we could clip the element, both are fast to do. Scaling it will look good on images, as they often stretch to fill. For text the individual lines don’t get larger or smaller so clipping will look better on text as it will maintain the text size.

When scaling size the math is a lot easier to do if you do all of the transformations from the top left corner.

Either Scale

(Make sure the transform origin is set to 0,0)

const scaleTransform = `scale(${oldPos.width/newPos.width},${oldPos.height/newPos.height})`;

or Clip-Path:

const heightDiff = (newPos.height - oldPos.height);
const widthDiff = (newPos.width - oldPos.width);
const clipPath = `inset(0px ${widthDiff}px ${heightDiff}px 0px)`;

Animating DOM Changes

Examples of the animation change between scaling and clipping. Scale on top, clipping below.

We can then animate these using the Web Animation API. Don’t animate both scale and clip path just pick one to use.

This animation will animate it from the starting size & position we worked out to the final position and size. The final position and size is just a no-op since it needs to end up where it came from.

el.animate({
  transformOrigin: ['0 0','0 0'],  transform: [
    `${translateTransform} ${scaleTransform}`,
    'scale(1)'
  ],  clipPath: [clipPath, 'inset(0px 0px 0px 0px)']
}, {
  easing: 'ease-out',
  duration: 500
});

Handling new Elements

Any elements which are new won’t have a starting position, so you should find a way to animate them in nicely. For my demos I waited until the position/scale animation finished then I faded them in:

el.animate({
  opacity: ['0', '1']
}, {
  easing: 'ease-out',
  duration: 500,
  delay: 500,
  fill: 'backwards'
});

Tips

  • If the parent element is likely to change size do the same thing for its parent as well so all it’s siblings get animated accordingly.
  • If you are animating all of an element’s children don’t animate that element as well. It looks weird.
  • Clipping works best for text, scaling for images.
  • Save your animations for user interactions, as that is when the user is likely to expect it.
  • Don’t over-use animations they should be little moments of joy otherwise they will get tedious.

Using our library yourself

Since it’s a pretty general case we have made the library in the demo available on npm. It’s pretty bare-bones so if you need more features then please feel free to take inspiration from it to write your own or to fork it as you need.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

《Unity3D网络游戏实战(第2版)》

《Unity3D网络游戏实战(第2版)》

罗培羽 / 机械工业出版社 / 2019-1-1 / 89.00元

详解Socket编程,搭建稳健的网络框架;解决网游中常见的卡顿、频繁掉线等问题;探求适宜的实时同步算法。完整的多人对战游戏案例,揭秘登录注册、游戏大厅、战斗系统等模块的实现细节。 想要制作当今热门的网络游戏,特别是开发手机网络游戏,或者想要到游戏公司求职,都需要深入了解网络游戏的开发技术。本书分为三大部分,揭示网络游戏开发的细节。 第一部分“扎基础”(1-5章) 介绍TCP网络游......一起来看看 《《Unity3D网络游戏实战(第2版)》》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具