35292


<https://codepen.io/tadashi___/pen/mwdOZp>

[code]

    console.clear();
    var i = 0;
    var _wrapperHeight = document.getElementById('parent2').offsetHeight;
    var _child = document.querySelectorAll('.child');
    var _past2Index;
    var _pastIndex;
    var _currentIndex = 0;
    var _past2View;
    var _pastView;
    var _currentView;
    var _regulerDirection = true;
    var _direction = -1;
    // setTimeout(()=>{
    //   _direction = 1;
    // },15000);
    var velocity = 2;
    setInterval(function() {
      velocity = Math.floor(Math.random() * 50);
      console.log(velocity);
    }, 6000);
    var _positions = [];
    var _heights = [];
    var _pause = [];
    var _scrollLock = false;
    var _scrollTimeout;
    window.addEventListener('resize', function() {
      _scrollLock = true;
      clearTimeout(_scrollTimeout);
      _wrapperHeight = document.getElementById('parent2').offsetHeight;
      for (var i_1 = 0, len = _child.length; i_1 < len; i_1++) {
        _child[i_1].style.setProperty('top', _wrapperHeight + "px");
        if (_pause[i_1]) {
          _positions[i_1] = -_wrapperHeight;
          _child[i_1].style.setProperty('transform', "translateY(" + (_positions[i_1] - _heights[i_1]) + "px)");
        }
      }
      _scrollTimeout = setTimeout(function() {
        _scrollLock = false;
      }, 500);
    });
    // currentを update
    function updateCurrentView() {
      _currentView = _child[_currentIndex];
    }
    /**
     * DOMの尻尾が画面内に入ったか。
     * @param  {Element} _el
     */
    function isInTail(num) {
      var __y = _positions[num] + _heights[num];
      if (_regulerDirection) {
        return __y <= 0;
      } else {
        return __y > 0;
      }
    }
    /**
     * DOMの尻尾が画面外にでたか。
     * @param  {Element} _el
     */
    function isOutTail(num) {
      var __y = _positions[num] + _wrapperHeight;
      var __h = _heights[num];
      console.log(__y + __h, num);
      if (_regulerDirection) {
        return __y + __h <= 0;
      } else {
        return __y + __h > _wrapperHeight;
      }
    }
    /**
     * ポジションの更新
     */
    function updatePosition() {
      if (_past2View) {
        var _num_1 = _positions[_past2Index] += velocity * _direction;
        _past2View.style.setProperty('transform', "translateY(" + _num_1 + "px)");
      }
      if (_pastView) {
        var _num_2 = _positions[_pastIndex] += velocity * _direction;
        _pastView.style.setProperty('transform', "translateY(" + _num_2 + "px)");
      }
      var _num = _positions[_currentIndex] += velocity * _direction;
      _currentView.style.setProperty('transform', "translateY(" + _num + "px)");
    }
    
    function updateCurrent() {
      // currentのtailが画面内に入ったら処理
      if (isInTail(_currentIndex)) {
        // 画面外に出たらpause flagをたてる
        if (!_pause[_pastIndex] && isOutTail(_pastIndex)) {
          _pause[_pastIndex] = true;
        }
        // pastViewがあったら2つ前の履歴に突っ込む。
        if (_pastView) {
          _past2Index = _pastIndex;
          _past2View = _pastView;
          _positions[_past2Index] = _positions[_pastIndex];
        }
        // 現在のcurrentを過去分に
        _pastIndex = _currentIndex;
        _pastView = _currentView;
        _positions[_pastIndex] = _positions[_currentIndex];
        // 次をcurrentに
        var _nextIndex = _currentIndex - _direction;
        // loopするようindexがはみ出た際の調整
        if (_nextIndex > _child.length - 1) {
          _nextIndex = 0;
        } else
        if (_nextIndex < 0) {
          _nextIndex = _child.length - 1;
        }
        // currentを次のdivに差し替え。
        // positionを0にセットしflagをfalseに。
        _currentView = _child[_nextIndex];
        _positions[_nextIndex] = 0;
        _pause[_nextIndex] = false;
        _currentIndex = _nextIndex;
      }
      //
    }
    
    function update() {
      if (_scrollLock)
        return;
      updateCurrent();
      updatePosition();
    }
    // render
    function render() {
      // setTimeout(render,1);
      requestAnimationFrame(render);
      update();
    }
    
    function setup() {
      for (var i_2 = 0, len = _child.length; i_2 < len; i_2++) {
        _child[i_2].style.setProperty('top', _wrapperHeight + "px");
        _positions.push(0);
        _heights.push(_child[i_2].offsetHeight);
        _pause.push(false);
      }
      updateCurrentView();
      requestAnimationFrame(render);
      // setTimeout(render,1);
    }
    setup();
[/code]

[code]

    * {
      margin: 0;
      padding: 0;
      font-size: 0;
    }
    
    #parent2 {
      position: relative;
      display: inline-block;
      width: 100%;
      height: 100vh;
      overflow: hidden;
      background: yellow;
    }
    
    .child {
      display: flex;
      align-items: flex-end;
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 1200px;
      font-size: 100px;
      text-align: center;
      line-height: 300px;
      color: white;
      box-sizing: border-box;
      border-bottom: 1px solid #000;
      background: rgba(102, 102, 102, 0.5);
    }
    
    .child:nth-child(1) {
      height: 1000px;
    }
    
    .child:nth-child(2) {
      height: 1300px;
    }
    
    .child:nth-child(3) {
      height: 1500px;
    }
    
    .child:nth-child(4) {
      height: 1500px;
    }
    
    .child:nth-child(5) {
      height: 1600px;
    }
    
    .child:nth-child(6) {
      height: 1000px;
    }
    
    .child:nth-child(7) {
      height: 1200px;
    }
    
    .child:nth-child(8) {
      height: 1400px;
    }
    
    .child:nth-child(9) {
      height: 1500px;
    }
    
    .child:nth-child(10) {
      height: 1900px;
    }
    
    .child:nth-child(11) {
      height: 1100px;
    }
    
    .child:nth-child(12) {
      height: 1000px;
    }
[/code]

[code]

    <div id="parent2">
      <div class="childWrap">
        <div class="child" id="c1">1</div>
        <div class="child" id="c2">2</div>
        <div class="child" id="c3">3</div>
        <div class="child" id="c4">4</div>
        <div class="child" id="c5">5</div>
        <div class="child" id="c6">6</div>
        <div class="child" id="c7">7</div>
        <div class="child" id="c8">8</div>
        <div class="child" id="c9">9</div>
        <div class="child" id="c10">10</div>
        <div class="child" id="c11">11</div>
        <div class="child" id="c12">12</div>
      </div>
    </div>
    
    <script>
      (function() {
        var script = document.createElement('script');
        script.onload = function() {
          var stats = new Stats();
          document.body.appendChild(stats.dom);
          requestAnimationFrame(function loop() {
            stats.update();
            requestAnimationFrame(loop)
          });
        };
        script.src = 'https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js';
        document.head.appendChild(script);
      })()
    </script>
[/code]

上記のcodepenのように、  
複数のcを連ねて、画面に入っているもののみtranslateYを上にずらしていく。  
divの底辺が画面内にはいったら次のdomを一緒に動かしていく。  
という内容を実装しています。

この時、velocityを6秒おきにランダムな速度に変更していますが  
velocityの速度が速い時、divとdivの間に不均等に隙間が生じてしまいます。

framerateが低いわけはないのですが、どのあたりの計算を見直すべきかわからず。。  
ご教示いただければ幸いです。

下記箇所で3つのdivを、自分の座標+=velocity*侵攻方向として移動しています。

[code]

    function updatePosition(){
    
    
      if(_past2View){
        let _num = _positions[_past2Index] += velocity * _direction;
        _past2View.style.setProperty('transform', `translateY(${ _num }px)`);    
      }
    
      if(_pastView){
        let _num = _positions[_pastIndex] += velocity * _direction;
        _pastView.style.setProperty('transform', `translateY(${ _num }px)`);    
      }
    
      let _num = _positions[_currentIndex] += velocity * _direction;
      _currentView.style.setProperty('transform', `translateY(${ _num }px)`);    
    
    }
    
[/code]

上記処理の前に下記も実行していきます。

[code]

    function updateCurrent(){
    
       // currentのtailが画面内に入ったら処理
       if(isInTail(_currentIndex)){
    
          // 画面外に出たらpause flagをたてる
           if(!_pause[_pastIndex] && isOutTail(_pastIndex)){
             _pause[_pastIndex] = true;
           }
    
          // pastViewがあったら2つ前の履歴に突っ込む。
           if(_pastView){
             _past2Index = _pastIndex;
             _past2View = _pastView;
             _positions[_past2Index] = _positions[_pastIndex];    
           }
    
          // 現在のcurrentを過去分に
           _pastIndex = _currentIndex;
           _pastView = _currentView;
           _positions[_pastIndex] = _positions[_currentIndex];
    
             // 次をcurrentに
           let _nextIndex = _currentIndex - _direction;
    
          // loopするようindexがはみ出た際の調整
           if(_nextIndex > _child.length - 1){
             _nextIndex = 0;
           }else if(_nextIndex < 0){
             _nextIndex = _child.length - 1;
           }
    
             // currentを次のdivに差し替え。
             // positionを0にセットしflagをfalseに。
           _currentView = _child[_nextIndex];
           _positions[_nextIndex] = 0;
           _pause[_nextIndex] = false;
    
          _currentIndex = _nextIndex;
    
      }
      //
    }
    
[/code]


CC BY-SA 4.0
2017-06-03T01:26:24.397
35238
2019-12-13T20:17:29.047
2019-12-13T20:17:29.047
32986
3131
question
javascript
複数のdivを同じスピードで順次positionの変更をしていくと、隙間ができる。


複数要素を同時に移動する手段は存在しないはずです。アプローチを変える必要があります。

一つ親の要素`<div class="childWrap">`をスクロールさせてやることで同様の効果が得られるはずです。その上で`<div
id="c1">`がスクロールアウトした時点で`<div
id="c12">`の下にくるように`top`なり`translateY()`なりで位置を調整すればいいでしょう。その方が処理もシンプルになります。

実際、元のアプローチですと`_currentView`、`_pastView`、`_past2View`で縦幅が足りない場合に`_past3View`も扱わなければならないというバグも存在しています。


CC BY-SA 3.0
2017-06-04T04:12:14.140
35261
2017-06-04T04:12:14.140
4236
35238
answer


自己解決はここに書くでよかったでしょうか？

質問のコードだとそれぞれのdivは個別に座標を管理していましたが

共通の座標を格納した変数を参照することで解決できました。  
簡易ですが、下記に該当箇所のコード記載します。  
offsetなどは省いています。

[code]

    let _posY = 0; // すべての子要素が共通で参照する座標
    let divArray = []; // 動かす子要素を格納した配列
    let divOffsetArray = []; // 自分より前の要素分自分をoffsetするための値をいれる
    
    
    function update(){
        _posY++;
    
        for(i=0;i<length;i++){
            divArray[i].style.transform = `translateY(
                ${_posY + divOffsetArray[i]}
            px)`;
        }
    }
    
    
    function render(){
        requestAnimationFrame(render.bind(this));
        update();
    }
    
    render();
    
[/code]


CC BY-SA 3.0
2017-06-05T05:55:08.900
35292
2017-06-05T05:55:08.900
3131
35238
answer