N ways to optimize the progress bar

Author: Mai Le Source: https://developer.hs.net/thread/2159

need

Implement a progress bar with pause and play functions. The rendering is roughly as follows:

2.png

The function is not very complicated, and there is no technical difficulty in the feeling. The first thing I thought of was to use a timer to realize this function. The button can control the start and pause of the timer. Next, vue is used to realize the first version.

Timer implementation

code show as below:

3FA276AD-57A3-45CD-925C-8BDB48DD414D.png

<script>
let timer, totalTime = 3000;
export default {
  name: "Progress",
	data() {
		return {
			isPlay: false,
			progress: 0
		}
	},

	methods: {
		run() {
			timer = setInterval(() => {
				const {progress} = this;
				this.progress = progress + 2;
				if(this.progress === 100) {
					this.cancel();
				}
			}, 3000/ 100)
		},
		cancel() {
			clearInterval(timer);
			timer = null;
		},
		changePlay() {
			const { isPlay } = this;
			this.isPlay = !isPlay;
			if(this.isPlay) {
				this.run();
			} else {
				this.cancel();
			}
		},
		replay() {
			this.isPlay = true;
			this.progress = 0;
			this.run();
		}
	}
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
	.g-contain {
		width: 240px;
		height: 25px;
		border-radius: 25px;
		background: #eee;
		overflow: hidden;
}
	.g-progress {
		height: inherit;
		border-radius: 25px 0 0 25px;
		background: #0f0;
	}
</style>

Rapidly incrementing the variable through a timer In progressorder to achieve the progress increase, each change of the variable will drive the view to recalculate and render, and will cause the entire page to continuously remake and redraw. If the project is not very complicated, it seems that the problem is not big. If you encounter a large-scale project, or a project with many DOM nodes on the page, you will feel obvious lag.

test:

Add 100 million elements to the page,

data() {
		return {
			isPlay: false,
			progress: 0,
			arr: []
		}
	},
	created() {
		for(let i = 0; i < 100000; i ++) {
			this.arr[i] = i;
		}
	},

1647681376897.jpg

Click the play button, you can see that the page has become stuck and the playback has slowed down.

Optimization 1

Use css animations.

4DC90A53-ECAC-48F7-8F99-BD4A6DE0C50D.png

<script>
export default {
  name: "Progress",
	data() {
		return {
			isPlay: false,
			type: 0, // 0播放, 1 重播
			totalTime: 2
		}
	},
	methods: {
		end(){},

		changePlay() {
			const { isPlay } = this;
			this.isPlay = !isPlay;
		},
		replay() {
			const { type } = this;
			this.isPlay = true;
			this.type = type ? 0 : 1;
		}
	}
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
	@keyframes play { 
    to {width: 100%}  
	}

	@keyframes replay {
    to {width: 100%}  
	}
	.g-contain {
		width: 240px;
		height: 25px;
		border-radius: 25px;
		background: #eee;
		overflow: hidden;
}
	.g-progress {
		width: 0;
		height: inherit;
		border-radius: 25px 0 0 25px;
		background: #0f0;
		/* animation-timing-function: linear; */
		-webkit-animation-timing-function: linear;
	}
	.g-progress.play {
		animation: play 3s infinite linear;
	}
	.g-progress.replay {
		animation: replay 3s infinite linear;
	}
	.g-progress.animationplay {     /* 使animation动画启动 */
		animation-play-state: running;
    -webkit-animation-play-state: running;
	}

	.g-progress.animationpause {    /* 使animation动画暂停 */
		animation-play-state: paused;
		-webkit-animation-play-state: paused;
	}
</style>

Using CSS3 keyframe animation to control the change of the progress bar reduces a lot of calculations and reduces performance consumption to a certain extent.

However, this method will still frequently trigger the reflow and redraw of the entire page.

Render the long list component to see the effect:

List.view

<template>
	<ul>
		<li v-for="(i, index) in arr" :key="index">{{i}}</li>
	</ul>
</template>

<script>
export default {
  name: "List",
	data() {
		return {
			arr: []
		}
	},
	created() {
		for(let i = 0; i < 100000; i ++) {
			this.arr[i] = i;
		}
	}
};
</script>


Introduce registration using:

import List from './List.vue'
components: {
    List
  },

During the playback process, although there is also a freeze, it is better than the previous solution. It is not convenient to see the results here. Finally, the code is attached, which can be downloaded and tested.

Optimization 2

Use CSS animation and enable GPU acceleration. GPU is mainly responsible for 3D rendering and hardware acceleration.

This solution has no difference between optimization 1 logic and page structure, the difference is in the style:

<style scoped>
@keyframes play { 
    0% {  
      transform: translateX(-50%) scaleX(0);  /* 用 scaleX 来代替 width */
    }
    to {
	transform: translateX(0) scaleX(1);
    }
}
@keyframes replay {
    0% {  
      transform: translateX(-50%) scaleX(0);  /* 用 scaleX 来代替 width */
    }
		to {
					transform: translateX(0) scaleX(1);
		} 
	}
	.g-contain {
		width: 240px;
		height: 25px;
		border-radius: 25px;
		background: #eee;
		overflow: hidden;
}
	.g-progress {
		width: 100%;
		height: inherit;
		border-radius: 25px 0 0 25px;
		background: #0f0;
                will-change: transform; /**通知浏览器提前过好优化 */ 
		animation-timing-function: linear;
		-webkit-animation-timing-function: linear;
	}
	.g-progress.play {
		animation: play 3s infinite linear;
	}
	.g-progress.replay {
		animation: replay 3s infinite linear;
	}
	.g-progress.animationplay {     /* 使animation动画启动 */
		animation-play-state: running;
    -webkit-animation-play-state: running;
	}

	.g-progress.animationpause {    /* 使animation动画暂停 */
		animation-play-state: paused;
		-webkit-animation-play-state: paused;
	}
</style>


Use will-change: transform; /**Notify the browser to optimize in advance*/

Use transform to enable GPU acceleration.

Enter the List component and test it, and you will find that the obvious lag on the page has disappeared.

Summarize:

When using animation, you can optimize performance by having the page form different composite layers.

How to open:

  • 3D or perspective transform CSS property
  • Elements using accelerated video decoding video>have 3D
  • (WebGL) context or accelerated 2D context canvaselement
  • Hybrid plugins (like Flash)
  • Animate your opacity with CSS or use an animated transform element (in previous browsers, this method will still cause repaints and rearrangements)
  • Elements with accelerated CSS filters
  • An element has a descendant node that contains a composite layer (in other words, an element has a child element that is in its own layer)
  • The element has a lower z-index sibling that contains a composite layer (in other words the element is rendered on top of the composite layer)

Github

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324094137&siteId=291194637
Recommended