Каков наилучший подход для вычисления расстояния между элементами DOM в Vuejs?

Я создаю инструмент для создания pdf файла из данных, и мне нужно построить в двух форматах: 105 мм * 148 мм и 105 мм * 210 мм. Поэтому я получил весь свой документ, и теперь мне нужно вставить разрывы страниц. Я делаю это с помощью простого класса:

.page-break { display: block; page-break-before: always; }

Теперь мне нужно вставить этот класс в мой цикл v-for. Итак, основная идея состоит в том, чтобы вычислить интервал, так как каждый индекс является кратным 6, я вставляю один. Но это не лучший способ сделать это, я хочу вставить разрыв, когда содержание превышает 90 мм.

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

Итак, вопрос прост: как вычислить это расстояние? Или, если есть лучший способ достичь моей цели, что я могу улучшить?

Ответ 1

Я считаю, что вы добавляете div/component в каждый v-for, и вы можете добавить уникальный идентификатор для каждого div. Теперь ниже методы могут дать вам высоту одного div в px, и у вас есть способ конвертировать px в mm.

Если вы используете jquery, вы можете сделать

$('#nthDiv').height();

Если нет, вы можете сделать следующее:

внутренняя высота:

document.getElementById('#nthDiv').clientHeight;

внешняя высота:

document.getElementById('#nthDiv').offsetHeight; 

если у вас есть следующий код:

<div v-for="(item, index) in items" :class="{'page-break': isBreakNeeded(index)}" :id="index + 'thDiv'">
   ///Your code
</div>

Вам нужно добавить следующий метод:

isBreakNeeded (index) {       
   var height = 0
   do {
      index -= 1;
      height += document.getElementById('#' + index + 'thDiv').offsetHeight; 
      } while (index >= 0 || pageBreakAdded[index] == true)
      if(height > threshold){
         pageBreakAdded[index] = true
         return  true
      }
      else{
         return  false
      }
}

Вам нужно добавить следующий хэш, а также в атрибут data вашего элемента vue, который будет отслеживать, на каких индексах вы добавили разрыв страницы:

pageBreakAdded: {}

Ответ 2

Итак, я пробовал этот подход:

  • вставьте page-break внутри моего шаблона цикла (div v-for each data text element)
  • выберите все page-break после рендеринга
  • вычислить с помощью getBoundingClientRect()
    • прокрутите выделение
    • получить расстояние между текущим разрывом и следующим
    • если он находится за пределами допустимого диапазона, удалите его из DOM.

Этот метод работает, но он очень уродлив, очень сложно вычислить хороший диапазон, и это супер дорого! С небольшим набором данных это нормально, но со всей книгой, это слишком долго...

И это первая попытка сделать это (с шаблоном Vue)

<template>
  <div class="content">
    <div class="pages">
      <div class="page-footer">
        <div class="page-break"></div>
      </div>

      <div class="dynamic">
        <div v-for="(entry, index) in conv" class="row">
          <div v-if="entry.msgs[0].value === 'break'">
            <span class="date">{{entry.msgs[0].metadate}}</span>
          </div>

          <div v-else>
            <span class="author">{{ entry.user }}</span>
            <span class="hour">{{ entry.msgs[0].metahour }}</span>
            <div class="phrases">
              <span v-for="msg in entry.msgs">
                {{ msg.value }}
              </span>
            </div>
          </div>

          <div class="page-footer" :data-base="index">
            <span class="page-number" :data-base="index">{{ pageNumberFor(index) }}</span>
            <div class="page-break"></div>
          </div>
        </div>
      </div>

      <div class="page-footer" data-base="end">
        <span class="page-number" data-base="end">{{ pageNumberFor('end') }}</span>
        <div class="page-break"></div>
      </div>
    </div>
  </div>
</template>

<script>

import und from 'underscore'

export default {
  name: "Data",

  data () {
    return {
      conv: null,
      cleaned: false,
      pageNumbers: {}
    }
  },

  computed: {
    mmScale: () => 0.264583333,
    heightBreak: () => 210
  },

  mounted () {
    this.$http.get('static/data_sorted_backup.json').then(
    // this.$http.get('static/data_sorted.json').then(
      (response) => {
        this.conv = this.groupBy(response.data)
      },
      (response) => {
        console.log('error');
      }
    )


  },

  updated () {
    console.log('updated');
    if(!this.cleaned) this.cleanPageBreak()
  },

  methods: {
    groupBy (json) {
      let result = []
      result.push(json[0])

      let punc = new RegExp(/[\?\.\!\;\(\)]?$/, 'ig')

      for (var i = 1; i < json.length; i++) {
        let val = json[i].msgs[0].value
        val = val.charAt(0).toUpperCase() + val.slice(1);
        if( punc.test(val) === false ) val += '.'
        json[i].msgs[0].value = val

        let len = result[result.length -1].msgs.length
        // if it not the same day

        let origin = result[result.length -1].msgs[len - 1].metadate
        let new_entry = json[i].msgs[0].metadate
        // console.log(i, origin, new_entry);
        if( origin !== new_entry ){
          result.push({
            msgs: [{
              value:"break",
              metadate: json[i].msgs[0].metadate
            }]
          })
          i--
          continue;
        }

        // if the previous author is the same
        if(result[result.length -1].user === json[i].user){
          result[result.length -1].msgs.push({
            value: json[i].msgs[0].value,
            metadate: json[i].msgs[0].metadate,
            metahour: json[i].msgs[0].metahour
          })

        }else{
          result.push(json[i])
        }
      }

      return result
    },

    cleanPageBreak () {
      console.log('cleanPageBreak');
      let breaks = this.$el.querySelectorAll('.page-footer')
      let distance
      let enough
      let previousTop = breaks[0].getBoundingClientRect().top

      let seuil = this.heightBreak * 0.85

      console.log(breaks.length, seuil);

      for (let i = 1; i < breaks.length; ++i) {
        console.log(i);
        distance = (breaks[i].getBoundingClientRect().top - previousTop) * this.mmScale

        enough = distance < this.heightBreak && distance >= seuil


        if (enough) {
          previousTop = breaks[i].getBoundingClientRect().top
        } else if(i != breaks.length -1) {
          breaks[i].remove()
        }
      }
      this.cleaned = true
      this.mapNumbers()
    },

    mapNumbers () {
      console.log('mapNumbers');
      let numbers = Array.from(this.$el.querySelectorAll('.page-number'))

      numbers.map( (elem, index) => {
        this.pageNumbers[elem.dataset.base] = {}
        this.pageNumbers[elem.dataset.base].index = index + 1
      })
    },

    pageNumberFor (index) {
      if(this.pageNumbers[index]) return this.pageNumbers[index].index
      return 0
    }
  }
}
</script>

<style lang="scss">
@import url('https://fonts.googleapis.com/css?family=Abel|Abhaya+Libre');


.page-footer{
  position: relative;
  margin: 12mm 0;
  // border: 1px dotted black;

  .page-break   {
    display: block;
    page-break-before: always;
  }

  .page-number {
    position: absolute;
    left: 50%;
    transform: translate(-50%, 0);
    top: -28mm;
  }
}

.content {
  padding: 0 16mm 0 10mm;
  font-family: 'Abhaya Libre', serif;

  .pages{
    position: relative;
  }
}
.row {
  margin: 0;
  font-size: 0;

  span{
    font-size: 13px;
  }
  span:first-letter {
    text-transform: uppercase;
  }

  .date, .hour {
    font-family: 'Abel', sans-serif;
  }
  .date {
    font-size: 15px;
    text-align: center;
    display: block;
    padding-top: 10mm;
    padding-bottom: 2mm;
  }
  .author{
    display: block;
    text-transform: uppercase;
    text-align: center;
  }
  .hour {
    margin-bottom: 3mm;
    margin-top: -2px;
    display: block;
    text-align: center;
    font-size: 9px;
  }
  .phrases{
    text-indent:5mm;
  }
}

.row + .row {
  margin-top: 5mm;
}
</style>