This is the third day of my participation in the August Text Challenge.More challenges in August

rendering

1. One-way sliding verification

The ideal Dom structure would look like this, in addition to the Canvas implementation.

 <div class="container">
     // Here is the original hidden green drag bar
    <section class="left-line">// This is the small slider</section>
    <div class="block">
    </div>
  </div>
Copy the code

The initial state is zero

  • Container Settings are positioned relative to each other
  • Small slider and drag bar absolutely positioned
  • Small slider: left: 0; Drag bar: left: -100%;

The following effects

Of course, I haven’t added overflow: Hidden to the container for the sake of observation

JavaScript implementation

let block ,
    leftLine
block = document.getElementsByClassName('block') [0];
leftLine = document.getElementsByClassName('lift-line') [0];
Copy the code
  • The logic is fairly clear

    • Listen for the mouse down event of the small slider

      Will the onMousemove event be triggered when the mouse is not down?

      Of course it will, so here you need to add a lock to control the trigger mechanism of the mouse movement event under the small slider.

      let isMove = false
      Copy the code

      At the same time, in order to calculate the sliding distance, we also need to record the position of the mouse down and the boundary value of the slider movement. A Boolean value is also given for successful validation

      let initX  = 0,
        success = false
      const Min = 0,
            Max = 400.// Max should be the container width minus the slider width, which is calculated directly here.
      block.addEventListener('mousedown'.(e) = >{
        isMove = true
        initX = e.clientX
      })
      Copy the code
    • Monitor the mouse movement event of the small slider, calculate the movement distance, and determine whether it is successful.

      block.addEventListener('mousemove'.(e) = > {
        if (isMove) {
          let abs = e.clientX - initX
          abs = abs < Min ? Min : abs
          if (abs > Max) {
            abs = Max
            leftLine.classList.add('success')
            isMove = false
            success = true
            console.log('Verification successful');
            block.innerText = 'tick'
          }
          block.style.transform = `translate3d(${abs}` px, 0, 0)
          leftLine.style.transform = `translate3d(${abs}` px, 0, 0)}})Copy the code
    • Monitor mouse lift event of small slider, slider reset.

      // When the mouse is up, two things happen:
      // 1. The slide has been verified successfully. No action required.
      // 2. If sliding fails, reset.
      block.addEventListener('mouseup'.(e) = > {
            isMove = false
            if(! success) {console.log('Verification failed');
              leftLine.style.transform = ` ` translate3d (0, 0)
              block.style.transform = ` ` translate3d (0, 0)}})Copy the code

      The overall code is as follows

      <! DOCTYPEhtml>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
        <title>Document</title>
        <style>
          .container {
            width: 240px;
            height: 40px;
            border: 1px #ccc solid;
            margin: 180px auto;
            position: relative;
            background-color: #ccc;
            padding: 0px;
            border-radius: 2px;
            overflow: hidden;
          }
          .left-line {
            position: absolute;
            top: 0;
            right: 100%;
            height: 100%;
            width: 100%;
            background-color: #177cb0;
          }
          .block {
            position: absolute;
            left: 1px;
            top: 1px;
            height: 38px;
            line-height: 38px;
            text-align: center;
            width: 38px;
            background-color: rgba(255.255.255.0.788);
            cursor: pointer;
            font-size: 20px;
            color: rgba(0.0.0.0.747);
            user-select: none;
          }
          .success {
            background-color: #0eb83a;
          }
          .block:hover {
            box-shadow: 0 0 10px 2px #ccc;
          }
        </style>
      </head>
      <body>
        <div class="container">
          <section class="left-line">
          </section>
          <div class="block">
            →
          </div>
        </div>
        <script>
          let block,
            leftLine,
            isMove = false,
            initX = 0,
            success = false
          block = document.getElementsByClassName('block') [0];
          leftLine = document.getElementsByClassName('left-line') [0];
          const Min = 0,
            Max = 200
          block.addEventListener('mousedown'.(e) = > {
            isMove = true
            initX = e.clientX
          })
          block.addEventListener('mousemove'.(e) = > {
            if (isMove) {
              let abs = e.clientX - initX
              abs = abs < Min ? Min : abs
              if (abs > Max) {
                abs = Max
                leftLine.classList.add('success')
                isMove = false
                success = true
                console.log('Verification successful');
                block.innerText = 'tick'
              }
              block.style.transform = `translate3d(${abs}` px, 0, 0)
              leftLine.style.transform = `translate3d(${abs}` px, 0, 0)
            }
          })
          block.addEventListener('mouseup'.(e) = > {
            isMove = false
            if(! success) {console.log('Verification failed');
              leftLine.style.transform = ` ` translate3d (0, 0)
              block.style.transform = ` ` translate3d (0, 0)}})</script>
      </body>
      </html>
      Copy the code
  • Let’s see what happens

It doesn’t seem to work very well that way. Add overflow: Hidden to the container and look again.

Three, two-way Slider

rendering

1. Dom structure design

Based on the effect implementation of sliding validation, it might be easy to build the following Dom structure.

 <div class="container">
    <div class="main-line">
      <section class="min-btn"></section>
      <section class="max-btn"></section>
      <section class="left-line"></section>
      <section class="right-line"></section>
    </div>
 </div>
Copy the code

2. The JavaScript implementation

The logic here is

  • According to the current click point coordinates, to determine the distance from the left and right sides of the distance, which is closer.
  • Make close buttons move the correct distance.
  let maxBtn, minBtn, leftLine, rightLine, maiLine
    maxBtn = document.getElementsByClassName('max-btn') [0];
    minBtn = document.getElementsByClassName('min-btn') [0];
    maiLine = document.getElementsByClassName('main-line') [0];
    leftLine = document.getElementsByClassName('left-line') [0];
    rightLine = document.getElementsByClassName('right-line') [0];
    const ContainerWidth = parseInt(getComputedStyle(maiLine, null).width)
    maiLine.addEventListener('click'.(e) = > {
      // The offset of the initial buttons on both sides
      const _min = getTransX(minBtn)
      const _max = getTransX(maxBtn)
      // The distance between the mouse click and the buttons on both sides
      let lefX = e.offsetX - _min
      let rigX = ContainerWidth - e.offsetX - _max
      let abs = 0
      if (lefX > rigX) {
        console.log('Near to the right');
        // Subtract 9 from the radius of the button so that the click point ends at the center of the button
        abs =ContainerWidth - e.offsetX - 9
        maxBtn.style.transform = `translate3d(-${abs}` px, 0, 0)
      } else {
        console.log('Near left');
        abs = e.offsetX - 9 
        minBtn.style.transform = `translate3d(${abs}` px, 0, 0)}})// We simply use a function to get the absolute value of the element's X-axis offset
    function getTransX(el) {
      if(! el.style.transform) {return 0
      }
      let str = el.style.transform.replace(/. * (/.' ')
      return Math.abs(parseInt(str))
    }
Copy the code

When it’s done, it looks like this

It looks like nothing is wrong, so let’s move the sides of the slider and add overflow: Hidden.

This is where the first bug appears, which is actually a logical error.

That is, after the drag bar overwrites the mainLine, when we trigger the click event, E.offsetx calculates the value on the drag bar, not the value on the mainLine we want.

Solution:

The solution I use here is to work on the DOM structure.

Add a hidden DOM element, absolutely located on the mainLine, exactly the same size, z-index less than two buttons, larger than the drag bar. The background is transparent.

 <div class="container">
    <div class="main-line">
      <section class="min-btn"></section>
      <section class="max-btn"></section>
      <section class="left-line"></section>
      <section class="right-line"></section>// Click the registration element of the event<section class="hide-line"></section>
    </div>
 </div>
Copy the code
.hide-line{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 8;
}
Copy the code

Then, we just need to change the click event registered on mainLine to hideLine, and nothing else is changed.


2. The dragging effect of buttons

The effect of pressing and dragging the button is the same as the logic of sliding verification.

The only difference is that the upper and lower boundaries of the buttons on both sides change at any time.

function moveing(e, type) {
  if (obj.isMove) {
      // Type is used to indicate the button type
    if (type) {
      // Calculate the offset value
      let abs = e.clientX - obj.maxInitX
      // Calculate the upper limit of offset, 50 is the distance between the two buttons
      let maxVal = ContainerWidth - getTransX(minBtn) - 50
      // When the offset upper or lower limit is reached
      abs = abs > 0 ? 0 : abs < -maxVal ? -maxVal : abs
      maxBtn.style.transform = `translate3d(${abs}` px, 0, 0)
      rightLine.style.transform = `translate3d(${abs}` px, 0, 0)
    } else {
      let abs = e.clientX - obj.minInitX
      let maxVal = ContainerWidth - getTransX(maxBtn) - 50
      abs = abs < 0 ? 0 : abs > maxVal ? maxVal : abs
      minBtn.style.transform = `translate3d(${abs}` px, 0, 0)
      leftLine.style.transform = `translate3d(${abs}` px, 0, 0)}}}Copy the code

It is best to use throttling wrap here to prevent too high frequency movement from stalling.

The renderings are as follows

3. Numerical changes

Once the visual effect is in place, you add optional parameters to the parameter selector and drag it around.

Start by adding the DOM element where the parameter is located, and place it absolutely below the two buttons.

​
  <div class="container">
    <div class="wrap">
      <div class="main-line">
        <section class="min-btn">
          <span class="min-val">0</span>
        </section>
        <section class="max-btn"> 
           <span class="max-val">50000</span>
        </section>
        <section class="left-line"></section>
        <section class="right-line"></section>
        <section class="hide-line"></section>
      </div>
  </div>
Copy the code

Then there was the problem that mainLine set overflow: Hidden, causing two text to overflow and hide.

Thus, a perfect DOM structure needs to be redesigned.

  • The same idea is used to wrap the mainLine around a transparent hidden container that is the same size and position as the mainLine.
  • Overflow hiding is not set for this container.
  • Move the two buttons under the container’s direct element and add absolute positioning text to each button. You don’t have to change any JS code.
  <div class="container">
    <div class="wrap">
      <div class="main-line">
        <section class="left-line"></section>
        <section class="right-line"></section>
        <section class="hide-line"></section>
      </div>
      <section class="min-btn">
        <span class="min-val">0</span>
      </section>
      <section class="max-btn">
        <span class="max-val">50000</span>
      </section>
    </div>
  </div>
Copy the code

Computed boundary value

The idea of calculating the value is also obvious, starting with the offset distance of the drag bar or button, multiplied by a calculated coefficient k.

This function is then called after each click and each move.

Code implementation

// 50000 is the difference between the assumed maximum value and the minimum value, which should be (maxval-minval)/ContainerWidth
const K =  50000/ContainerWidth
function updateVal(x,type){
  if(type){
    / / Max
    maxVal.innerText =parseInt(parseInt(50000 + K*x)/100) * 100    
  }else{
    / / the minimum
    minVal.innerText =parseInt(parseInt(K*x)/100) *100}}Copy the code