0

        var container    = document.querySelector(".container");
        var bar          = document.querySelector(".bar");    
        var barL         = document.getElementsByClassName("barLayer");
        var value        = document.getElementsByClassName("value");
        var compare      = document.getElementsByClassName("compare"); // array vallue to show than code can't get the correct result of that bar's height   
        var pixelStep    = 4;   // cooficient to expand bar height. if  val=100 => bar.height = 400px
        var barNum       = 12;  // number of bars
        var spaceBetween = 100; // space between bars
        var intervalTime = 10;  // ms reading transition changes
        var delayTime    = 50;  // ms after drawing previous bar.

        const data       = [ 
        { brand : "SAMSUNG",  val : 40,  color : "#c00"}, 
        { brand : "APPLE",    val : 46,  color : "#0c0"}, 
        { brand : "HUAWEI",   val : 43,  color : "#f84"}, 
        { brand : "ASUS",     val : 71,  color : "#248"},    
        { brand : "XIAOMI",   val : 53,  color : "#0cc"},
        { brand : "OPPO",     val : 100, color : "#cc0"},
        { brand : "VIVO",     val : 66,  color : "#c0c"}, 
        { brand : "MOTOROLA", val : 86,  color : "#ccc"},
        { brand : "LENOVO",   val : 61,  color : "#c40"},
        { brand : "LG",       val : 93,  color : "#333"},
        { brand : "NOKIA",    val : 83,  color : "#088"}, 
        { brand : "OTHERS",   val : 51,  color : "#06c"} ]; 

        sortFunction(data, "val");  //sort Array

        clone(bar, container); // clone bar for (barNum-1) times

        for(let i = 0; i < data.length; i++){ barL[i].style.backgroundColor = data[i].color; } // colorize every bar with corresponding array color value 

        //---------------

      var myInterval = [];

        function anim(j){  // draw bars one after the other in sequence

          const computedStyleObj = getComputedStyle(barL[j]);
          value[j].textContent   = Math.round(parseInt(computedStyleObj.height)/pixelStep) + "K";

          compare[j].textContent = data[j].val;  

          barL[j].style.height   = value[j].style.bottom = (data[j].val * pixelStep) + "px"  //transiton value of height 
        //console.log("j : ", data[j].val + " - " + parseInt(computedStyleObj.height));
        barL[j].addEventListener("transitionEnd", () =>{clearInterval(myInterval[j]);});        // when transition ends, clear interval
        barL[j].addEventListener("webkitTransitionEnd",  () =>{clearInterval(myInterval[j]);});
      }

      for (let i = 0; i < data.length; i++) {
        setTimeout(function() {
        myInterval[i] = setInterval(function() {anim(i);}, intervalTime);   // after delayTime ms start the next bar animation
      }, i * delayTime);
      }
        //----------------

        function clone(item, container) {           // clone an item in a container function
          for(let i = 0; i < barNum-1 ; i++){
            var cln = item.cloneNode(true);
            container.appendChild(cln);
            cln.style.left = ((i+1) * spaceBetween ) + "px";
          }
        }

        function sortFunction(arr, key) {   // sort an array of objects by given key
          arr.sort(function(a, b){
            let x = a[key];
            let y = b[key];
            if (x < y) {return -1;}
            if (x > y) {return 1;}
            return 0;
          });
          data.reverse();  // reverse for descending array 
        }
        :root {
          --barWidth     : 80px;
          --color        : #aaa;
          --contentWidth : 1200px;
        }

        * {
          margin     : 0;
          padding    : 0;
          box-sizing : border-box;
        }

        body {
          width           : 100%;
          display         : flex;
          align-content   : center;       
          justify-content : center;
          flex-direction  : column;
          background      : var(--color);
          font-family     : 'Roboto Mono', monospace;
        }

        .container {
          position    : relative;
          width       : var(--contentWidth);
          height      : 500px;
          border      : 1px solid #0005;
          background  : #fff4;
          margin      : 10px auto;
        }

        .bar {
          position   : absolute;
          width      : var(--barWidth);
          margin     : 10px;
          display    : inline-block;
          border     : 0px solid #0005;
          bottom     : 0px;
        }

        .barLayer {
          position   : absolute;
          width      : var(--barWidth);;
          height     : 0px;
          bottom     : 0;
          border     : 0px solid #0005;          
        }

        .value, .compare {
          position    : absolute;
          width       : var(--barWidth);
          height      : calc(var(--barWidth)/2);
          bottom      : 0px;
          font-size   : calc(var(--barWidth)/4);
          text-align  : center;
          border      : 0px solid #fff;
          line-height : calc(var(--barWidth)/2);   
        } 

        .barLayer, .value { 
          transition : all 1s linear;
        }
      <div class="container">
        <div class="bar">     
          <div class="barLayer"></div>
          <div class="value"></div>
          <div class="compare"></div>
        </div>
      </div>  

我想在过渡结束后清除“间隔”(带有过渡动画的绘图条)不要白白忙于CPU,但我无法获得正确的高度值。

您可以使用下面的截断链接查看结果。

顶部是错误值,底部是数组中的数据。我尝试了一些算术方法,但想使用“transitionend”事件监听器。谢谢!:)

4

1 回答 1

1

TL;DR:对于动画的东西,使用requestAnimationFrame

通常对于动画相关的事情,我们更喜欢使用requestAnimationFrame而不是间隔或超时。您可以将其requestAnimationFrame视为setTimeout将在下一帧触发的。所以我建议用它来代替你的 setInterval。

至于动画数字的“这个栏有多远”的度量,我认为只需观察已经过去了多少时间以及过渡应该持续多长时间,您就会得到一个足够好的近似值。这无论如何都不会是精确的,但是在每帧getComputedStylegetBoundingClientRect每帧上测量元素将是流动性的灾难。所以这就是为什么我在watchAnimation.

其余的,我没有太大变化。只是重新编写了您的代码,因为(对不起,但是)它有点乱,而且与手头的问题没有任何关系的事情太多了。

const container = document.querySelector('.container')
const bar = document.querySelector('.bar')

const values = [100, 80, 63, 20]
const DURATION = 1000 //ms

values.forEach((value, i) => {
    initItems(value, i)
    staggeredStart(value, i)
    watchAnimation(value, i)
})

function initItems(value, i) {
    let item = container.children.item(i)
    if(!item) {
        item = bar.cloneNode(true)
        container.appendChild(item)
    }
    // other things to initialize items
}

function staggeredStart(value, i) {
    const item = container.children.item(i)
    setTimeout(() => {
        item.classList.add('animate')
        item.addEventListener('transitionend', () => {
            item.classList.remove('animate')
        }, {once: true, passive: true})
        item.style.setProperty('--value', `${100 - value}%`)
    }, i * 50)
}

function watchAnimation(value, i) {
    const item = container.children.item(i)
    const span = item.querySelector('.value')
    span.textContent = '0'
    let requestAnimationFrameId
    let startTime

    function onStart(event) {
        startTime = event.timeStamp
        onFrame()
    }
    
    function onEnd() {
        cancelAnimationFrame(requestAnimationFrameId)
        span.textContent = value
    }
    
    function onFrame() {
        requestAnimationFrameId = requestAnimationFrame((time) => {
            const delta = time - startTime
            const ratio = Math.min(Math.max(0, delta / DURATION), 1)
            span.textContent = Math.round(value * ratio)
            onFrame()
        })
    }

    item.addEventListener('transitionstart', onStart, {once: true, passive: true})
    item.addEventListener('transitionend', onEnd, {once: true, passive: true})
}
* {
    box-sizing: border-box;
}

.container {
    height: 500px;
    border: 1px solid #0005;
    overflow: hidden;
    display: flex;

    --bar-width: 80px;
    --space-for-value: 40px;
}

.bar {
    --value: 100%;
    position: relative;
    width: var(--bar-width);
    height: calc(100% - var(--space-for-value));
    top: var(--space-for-value);
    transform: translateY(var(--value));
}

.bar:not(:first-child) {
    margin-left: 20px;
}

.bar.animate {
    transition: transform 1s linear;
}

.layer {
    position: absolute;
    width: 100%;
    height: 100%;
    bottom: 0;
    background: red;
}

.value {
    position: absolute;
    width: 100%;
    height: var(--space-for-value);
    bottom: 100%;
    text-align: center;
    line-height: var(--space-for-value);
}
<div class="container">
    <div class="bar">
        <div class="layer"></div>
        <div class="value"></div>
    </div>
</div>

于 2021-06-27T15:00:44.290 回答