Как рассчитать видимые номера диапазона предметов для виртуального списка прокрутки? - PullRequest
0 голосов
/ 18 ноября 2018

Я попытался найти библиотеку, чтобы сделать это для меня, но я не нашел ту, которая бы обладала необходимыми функциями или стилем, поэтому я решил попробовать свои силы самостоятельно;единственная проблема с этим - вычисление диапазона отображаемых элементов, которые в данный момент видны в списке.Сам список работает практически без проблем, за исключением ранее описанного.

Основная проблема - математика, необходимая для определения этого диапазона и для того, чтобы эта математика работала для всех вариантов использования, где "все варианты использования"«просто означает прокрутку списка, и числа, представляющие видимые элементы, остаются точными.В настоящее время цифры в основном неточны, когда округлены в любом из трех возможных направлений;пол, ближайший и потолок.

Для демонстрации я удалил округление, чтобы проиллюстрировать ранее описанную проблему.

var comp = {
  template: `
  <div class="table-responsive container_table" :style="{height: viewHeight + 'px'}">
    <table class="table table-bordered table_outer host" ref="table">
      <thead>
        <tr>
          <th>Log</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td :colspan="1" ref="content">
            <div class="td_scroll" :style="td_scrollHeight" @scroll="refresh()" ref="td_scroll">
              <div class="total-padding" :style="{height:scrollHeight + 'px'}"></div>
              <div class="scrollable-content" :style="{transform:'translateY('+topPadding+'px)'}">
                <div class="table-responsive td_content" :style="{height: viewHeight + 'px'}" ref="td_content">
                  <table class="table table-bordered table_inner" ref="table">
                    <thead style="display: none;">
                      <tr>
                        <th>Log</th>
                      </tr>
                    </thead>
                    <tbody>
                      <tr v-for="(row, tr_i) in rows" :key="tr_i">
                        <td class="ellipsis" :style="{height: childHeight + 'px'}">
                          <span>{{row['value']}}</span>
                        </td>
                      </tr>
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>`,
  props: ["items", "viewHeight", "childHeight"],
  data() {
    return {
      scrollHeight: 0,
      topPadding: 0,
      rows: []
    };
  },
  methods: {
    // Below is the first bit of problem code
    calculateItems() {
      let start = (
        this.$refs.td_scroll.scrollTop /
        this.$refs.td_scroll.scrollHeight *
        this.items.length
      );
      let viewHeight = this.$refs.td_scroll.getBoundingClientRect().height + 2;
      let end = (start) + (viewHeight / this.childHeight);

      this.scrollHeight =
        this.childHeight * this.items.length + this.childHeight;
      this.topPadding = this.childHeight * (start);
      //start = Math.round(start);
      //end = Math.trunc(end);
      this.rows = this.items.slice(start, end);
      start = start;
      end = end;
      this.$emit("change", {
        start,
        end
      });
    },
    // End problem code 1
    refresh() {
      window.requestAnimationFrame(this.calculateItems);
    }
  },
  computed: {
    // Below is the second bit of problem code
    td_scrollHeight() {
      let styles = {};
      if (this.items.length > 6) {
        styles.height = this.viewHeight - 50 + 10 + "px";
      }
      return styles;
    }
    // End problem code
  },
  watch: {
    items() {
      this.refresh();
    }
  },
  mounted() {
    this.refresh();
  }
};

new Vue({
  el: '#app',
  data: {
    logStart: 0,
    logEnd: 0,
    logs: [{"value": "0"}, {"value": "1"}, {"value": "2"}, {"value": "3"}, {"value": "4"}, {"value": "5"}, {"value": "6"}, {"value": "7"}, {"value": "8"}, {"value": "9"}, {"value": "10"}, {"value": "11"}, {"value": "12"}, {"value": "13"}, {"value": "14"}, {"value": "15"}, {"value": "16"}, {"value": "17"}, {"value": "18"}, {"value": "19"}, {"value": "20"}, {"value": "21"}, {"value": "22"}, {"value": "23"}, {"value": "24"}, {"value": "25"}, {"value": "26"}, {"value": "27"}, {"value": "28"}, {"value": "29"}, {"value": "30"}, {"value": "31"}, {"value": "32"}, {"value": "33"}, {"value": "34"}, {"value": "35"}, {"value": "36"}, {"value": "37"}, {"value": "38"}, {"value": "39"}, {"value": "40"}, {"value": "41"}, {"value": "42"}, {"value": "43"}, {"value": "44"}, {"value": "45"}, {"value": "46"}]
  },
  methods: {
    updateShowing(pos) {
      this.logStart = pos.start;
      this.logEnd = pos.end;
    }
  },
  components: {
    comp
  }
})
.table-responsive {
  width: 100.1%;
}

.table-responsive.container_table {
  overflow: hidden;
}

.table-responsive>.table-bordered {
  border: 0;
  border-collapse: collapse;
  box-sizing: content-box;
  width: 100.1%;
  max-width: 100.1%;
}

.table_outer {
  padding: 0;
  width: 100.1%;
}

.table_outer>thead>tr>th {
  position: relative;
}

.table_outer>tbody>tr>td {
  padding: 0;
  border: none;
}

.table_outer .td_scroll {
  overflow: hidden;
  overflow-y: overlay;
  position: relative;
  display: block;
}

.table_outer .td_content {
  overflow: hidden;
}

.table_outer .table_inner {
  margin-bottom: 0px;
}

.host {
  overflow: hidden;
  overflow-y: auto;
  position: relative;
}

.scrollable-content {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  position: absolute;
}

.total-padding {
  width: 1px;
  opacity: 0;
}

.table-bordered thead td,
.table-bordered thead th {
  border: 0;
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
  {{logs.length}} Log{{(logs.length > 1 || logs.length === 0 || !logs) ? 's' : ''}}
  <span v-show="logEnd !== 0">
    <span> | Showing: {{logStart}} - {{logEnd}}</span>
  </span>
  <comp :items="logs" :view-height="350" :child-height="50" @change="updateShowing($event)"></comp>
</div>

Вот код блока, о котором идет речь, в приведенной выше демонстрации:

calculateItems() {
  let start = (
    this.$refs.td_scroll.scrollTop /
    this.$refs.td_scroll.scrollHeight *
    this.items.length
  );
  let viewHeight = this.$refs.td_scroll.getBoundingClientRect().height + 2;
  let end = (start) + (viewHeight / this.childHeight);

  this.scrollHeight =
    this.childHeight * this.items.length + this.childHeight;
  this.topPadding = this.childHeight * (start);
  //start = Math.round(start);
  //end = Math.trunc(end);
  this.rows = this.items.slice(start, end);
  start = start;
  end = end;
  this.$emit("change", {
    start,
    end
  });
},

Я пробовал различные методы округления,а также регулярные сложения и вычитания, и, конечно, проверки состояния, чтобы получить числа, где я хочу их.Ничего из этого не помогло.

Я также надеялся сохранить плавную прокрутку, но если это невозможно, ну ладно.

В реальном коде присутствует дополнительная разметка html, что не влияет на расчет диапазона, поэтому я не включил его в демо.HTML-разметка должна оставаться такой, как есть, если это возможно.

Есть ли способ правильно рассчитать диапазон видимых элементов? (1)

1: Я не ищу библиотеку, чтобы сделать это для меня.

...