Encapsulation of Slow Moving Function in JS Carousel

  • 2021-07-12 04:27:48
  • OfStack

The essence of the carousel is actually the packaging of the slow-moving function. If the carousel is a running car, then the slow-moving function is its engine. Today, this article will take everyone from simplicity to complexity and package their own slow-moving function ~ ~

Let's start from the perspective of requirements and first give a simple requirement:

1. I want a box in the page to move to the right at a constant speed from the starting position to 200px. How to realize it?

Analysis:

1) We need to know where the box is, which can be obtained through offsetLeft attribute;

2) To make the box move at a uniform speed, setInterval is definitely needed for js;

3) To make the box run to the right? That is, it is necessary to constantly change the distance between the box and the starting point on the left, including margin-left and the positioning left. Here I chose left; which changed the absolute positioning;

4) Run to the distance of 200px from the starting point. We have to stop and use clearInterval.

Next, go directly to the code


<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="UTF-8" />
 <title>Document</title>
 <style type="text/css">
  * {
  margin: 0;
  padding: 0;
  }
  div {
  position: absolute;
  top: 50px;
  width: 100px;
  height: 100px;
  background-color: red;
  }
  input {
  width: 100px;
  height: 30px;
  color: #fff;
  background-color: yellowgreen;
  }

 </style>
 </head>

 <body>
 <div></div>
 <input type="button" value=" Move to 200" />


 <script type="text/javascript">
  //  Gets the element (there is a small detail here, if the element is set id Name, even if you don't use the method to get the element, you can pass this id Name gets the element oh ~~ You can try it yourself 1 Below) 
  var btn = document.querySelector('input'),
   dv = document.querySelector('div');
  //  Add a click event 
  btn.addEventListener('click',function() {
  var timer = null,//  Save timer 
   currentDistance = dv.offsetLeft, //  Current distance from parent box 
   step = 8,//  The distance of each change 
   target = 200;//  Target distance 
  timer = setInterval(function() {
   currentDistance += step;//  Current distance  =  Upper 1 Current distance  +  Changed distance 
   if((target - currentDistance) < step) { 
   currentDistance = target; //  If the difference between the target distance and the current distance is less than the distance to be changed, then we directly make the current distance equal to the target distance to prevent the error when the box stops 
   clearInterval(timer); //  Clear timer 
   timer = null; //  Will timer Unbind, free memory 
   }
   dv.style.left = currentDistance + 'px'; //  At the core 1 Step, change the box's left Is the current distance 
  },17)
  })
 </script>
 </body>
</html>


2. The effect of one initial movement has been realized, so then we improved the requirements:

After the box moves to the position of 200px, we will continue to move the box to the position of 400px?

Analysis:

1) At this time, two buttons should be clicked, one moving to 200px and one moving to 400px

2) Although there are two motions, they all use one function, which moves from one point to another, so we consider encapsulating the motion in 1 into one function for reuse.

Code on ~


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8" />
 <title>Document</title>
 <style type="text/css">
 * {
 margin: 0;
 padding: 0;
 }
 div {
 position: absolute;
 top: 50px;
 width: 100px;
 height: 100px;
 background-color: red;
 }
 input {
 width: 100px;
 height: 30px;
 color: #fff;
 background-color: yellowgreen;
 }

 </style>
</head>

<body>
 <div></div>
 <input type="button" value=" Move to 200" />
 <input type="button" value=" Move to 400" />
 <script type="text/javascript">
 //  The encapsulation function, box and target distance are all uncertain, so we can pass them as parameters. 
 function animation(tag,target) {
 var timer = null,
  currentDistance = tag.offsetLeft,
  step = 5;
 step = currentDistance < target? step: -step;//  Judge step The plus and minus of, 200 To 400 Time is increasing, 400 To 200 The time is decreasing 
 timer = setInterval(function() {
 if(Math.abs(currentDistance - target) > Math.abs(step)) { //  Here, the judgment conditions should also be slightly changed, and absolute values should be used for comparison 
  currentDistance += step; /
  tag.style.left = currentDistance + 'px';
 }else {
  tag.style.left = target + 'px' //  When the difference between the current distance and the target distance is less than step When changing the distance, we directly move the box to the target distance. 
  clearInterval(timer);
  timer = null;
 }
 },17)
 }
 var btns = document.querySelectorAll('input'),
 dv = document.querySelector('div');
 btns[0].addEventListener('click',function() {
 animation(dv,200);
 })
 btns[1].addEventListener('click',function() {
 animation(dv,400);
 })
 </script>
</body>
</html>

3. We have packaged the function of the box moving back and forth, but let's think about the scrolling effect of the next carousel. It does not move at a constant speed, but is very block at the beginning, and the speed gradually decreases when the scrolling is finished.

Requirement: Let the box move slowly (that is, move at variable speed)

Code on ~


function animation(tag,target) {
 var timer = null;
 timer = setInterval(function() {
 var currentDistance = tag.offsetLeft,
  step = (target - currentDistance) / 5;//  Divide the difference between the target distance and the current distance by 5 We have achieved the variable speed movement we need, because step Every time the customizer executes, it changes, so it is put into the timer 
 step = step > 0 ? Math.ceil(step):Math.floor(step);//  Here if you will currentDistance The declaration outside the timer can not be written, if it is declared inside the timer, because offsetLeft The rounding characteristic should be compared with step Rounding 
 if(Math.abs(currentDistance - target) > Math.abs(step)) {
  currentDistance += step;
  tag.style.left = currentDistance + 'px';
 }else {
  tag.style.left = target + 'px'
  clearInterval(timer);
  timer = null;
 }
 },17)

Ok, the most basic slow motion function required for a carousel map is completed ~

Here to add a more complete slow-moving function: its function is more comprehensive 1 point, can change multiple styles at the same time.   


function perfectAnimate(tag, obj, fn) {//  Biography 3 Parameters, moving boxes, objects (can pass multiple attributes), callback functions (can perform custom functions after execution) 
 clearInterval(tag.timer);//  Here, the timer is used as tag The attribute of the tag is saved, and the function can be called many times to clear the 1 A timer. 
 tag.timer = setInterval(function () {
 var flag = true;
 for (var k in obj) {
         //  Because not all attributes have px Unit, so the judgment is set here respectively  
  if (k == 'opacity') {
  var currentDistance = getStyle(tag, k) * 100,
   target = obj[k] * 100,
   step = (target - currentDistance) / 10;
  step = step > 0 ? Math.ceil(step) : Math.floor(step);
  currentDistance += step;
  tag.style[k] = currentDistance / 100;
  } else if (k == 'zIndex') {
  tag.style[k] = obj[k];
  else {
  var currentDistance = parseInt(getStyle(tag, k)) || 0,
   target = obj[k],
   step = (target - currentDistance) / 10;
  step = step > 0 ? Math.ceil(step) : Math.floor(step);
  currentDistance += step;
  tag.style[k] = currentDistance + 'px';
  }
  if (target != currentDistance) {
  flag = false //  As long as there are attributes that have not been completed, the timer will not be clear 
  }
 }
 if (flag) {
  clearInterval(tag.timer)
  fn && fn();//  When all timers are finished, the callback function is executed here, and the short-circuit operation avoids not passing the callback function and not reporting errors. 
 }
 }, 17)
}

//  Gets the compatibility function of the style, and the dependence of the above slow-moving function 
function getStyle(tag, attr) {
 if (tag.currentStyle) {
 return tag.currentStyle[attr];
 } else {
 return getComputedStyle(tag, null)[attr];
 }
}

Related articles: