Когда запускать прокрутку после загрузки изображений - PullRequest
1 голос
/ 02 июня 2019

Использование платформы Quasar У меня несколько детей, и мне нужно прокрутить до определенного ребенка после загрузки страницы. Я могу сделать это, установив задержку с помощью setTimeout, но я бы предпочел лучшее / безотказное решение. Мой текущий подход заключается в том, чтобы дождаться монтирования всех детей (и использовать nextTick) и подумать, что он должен быть готов к прокрутке, но, очевидно, нет. В качестве альтернативы я могу подождать загрузки всех изображений (так как QImg имеет событие @load ), но это действительно поздно, так как вручную я уже могу запустить прокрутку, когда поля изображений уже отрисованы, но все еще загружаются , Каков наилучший способ срабатывания в «самый ранний момент»?

Точка-панель (родительская):

new Vue({
  el: '#q-app',
  template: `
      <div id="point-panel" class='map-overlay column scroll'>
            <small-cards @hook:mounted="cardsAreReady(cardIndex, picArray.length)"
                 v-for='(point, cardIndex) in picArray'
                 :key="cardIndex"
                 :point-object="point"
                 :card-index="cardIndex"></small-cards>
            <q-btn @click.native="scrollToCenter"
           fab
           ripple
           class='fixed'
           size="10px"
           color="black"
           label="scroll"
           style="right: 18px; bottom: 120px" />
  </div>
        </div>
    `,
  data: function() {
    return {
      selectedPointIndex: 6,
      picArray: ["https://image1", "https://image2, "https://image_etc."]
    }
  },
  methods: {
    notify(msg) {
      this.$q.notify(msg)
    },
    cardsAreReady(cardIndex, total) {
      console.log('one ready.......', `${cardIndex} ...and total ${total}`)
      let that = this
      if (cardIndex + 1 === total) {
        console.log('....all cards MOUNTED!', cardIndex)
        setTimeout(() => {
          that.$nextTick(() => {
            console.log('..........trigger scroll NOW!')
            that.scrollToCenter()
          })
        }, 0)
      }
    },
    scrollToCenter() {
      const that = this
      console.log('..........cardIndex: ', that.selectedPointIndex)
      that.notify('scroll triggered!')

      function scrollFunction() {
        const element = document.getElementsByClassName(
          that.selectedPointIndex.toString()
        )
        const target = document.getElementById('point-panel')
        const iW = window.innerWidth
        const iH = window.innerHeight
        const myOffset = element[0].offsetLeft
        Quasar.utils.scroll.setHorizontalScrollPosition(target, myOffset, 0)

      }
      setTimeout(() => scrollFunction(), 0)
    }
  }
})

Изображения / маленькие карточки (дети):

Vue.component('small-cards', {
  props: {
    pointObject: {
      type: Object,
      required: true
    },
    cardIndex: {
      type: Number,
      required: true,
      default: 0
    }
  },
  data: function() {
    return {
      selectedPointIndex: 6
    }
  },
  methods: {
    reportError(event) {
      console.log(`${event.name}: ${event.message}`);
    },
  },
  template: `
        <div class="mycard"
       :class="{'active': cardIndex === selectedPointIndex,
          [cardIndex]: true}">
    <q-img :src="pointObject"
           spinner-size="30px"
           style="background-color: rgba(255, 225, 215, 0.4)"
           @error="reportError">
    </q-img>
  </div>
  `
})

Здесь вы можете найти jsfiddle , показывающий проблему. Я добавил кнопку прокрутки, чтобы показать, что после загрузки вы можете успешно запустить прокрутку (6-е изображение, то есть «Тигр» прокручивается в нижний левый угол).

РЕДАКТИРОВАТЬ: Чтобы задать вопрос несколько больше направления: проблема в том, что если scroll будет запущен в ближайшее время, Dom еще не визуализируется, и, следовательно, расстояние до прокрутки еще не может быть определено. Но я думаю, что после mount Дом должен быть определен ?! Так почему же нынешний подход не работает?

1 Ответ

1 голос
/ 02 июня 2019

РЕДАКТИРОВАТЬ / НОВЫЙ ОТВЕТ:

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

  • ~ РЕДАКТИРОВАТЬ ~ добавление $nextTick (как предложено OP) делает это намного более последовательным! OP также смог получить этот рабочий компонент без оболочки
  • Компонент small-cards теперь emits событие, когда изображение завершает загрузку
  • Создан «обертка» для компонента small-cards с именем small-cards-wrapper
  • Эта «обертка» имеет 2 props: 1) items, массив источников изображений 2) scrollToIndex, индексный номер, к которому вы хотите прокрутить: mount
  • Компонент small-cards-wrapper будет извлекать событие, которое было отправлено (из компонента small-cards), и проверяет, является ли это индекс, к которому вы хотите прокрутить - если это так, мы прокручиваем к нему.
  • По сути, мы ждем загрузки изображения, прежде чем пытаться перейти к нему ..

Я уверен, что вы сможете увидеть, что я изменил после просмотра кода, но если у вас есть какие-либо вопросы, просто дайте мне знать!

[ NEWEST JSFiddle (с $nextTick)]

[ ОБНОВЛЕНО JSFiddle]



ОРИГИНАЛЬНЫЙ ОТВЕТ:

Я запустил метод scrollToCenter() во время mount, а также удалил логику hook.mounted из шаблона ..

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

Если вы не хотите, чтобы прокрутка происходила через 2 секунды после монтирования, вы можете удалить setTimeout - это было сделано, чтобы показать, как он прокручивается после загрузки (дает вам время увидеть, как это происходит) ..

Это то, что вы ищете?



Vue.component('small-cards', {
  props: {
    pointObject: {
      type: Object,
      required: true
    },
    cardIndex: {
      type: Number,
      required: true,
      default: 0
    },
  },
  data: function() {
    return {
      selectedPointIndex: 6
    }
  },
  methods: {
    reportError(event) {
      console.log(`${event.name}: ${event.message}`);
    },
    handleLoad() {
      this.$emit('loaded-card', true);
    }
  },
  template: `
  <div class="mycard" :class="{[cardIndex]: true}">
    <q-img :src="pointObject"
    	@load="handleLoad"
      spinner-size="30px"
      style="background-color: rgba(255, 225, 215, 0.4)"
    	@error="reportError">
    </q-img>
  </div>
  `
});

Vue.component('small-cards-wrapper', {
  props: {
    items: {
      type: Array,
      required: true,
    },
    scrollToIndex: {
      type: Number,
      required: false
    }
  },
  methods: {
    isLoaded(x) {
      if (Number(x) === Number(this.scrollToIndex)) {
        this.$nextTick(() => {
          const element = document.getElementsByClassName(x.toString())
          const target = document.getElementById('point-panel')
          const iW = window.innerWidth
          const iH = window.innerHeight
          const myOffset = element[0].offsetLeft
          Quasar.utils.scroll.setHorizontalScrollPosition(target, myOffset, 0)
          this.$q.notify("Scroll Triggered!");        
        })
      }
    }
  },
  template: `
	<div id="point-panel" class='map-overlay column scroll'>   
  	<small-cards
    	v-for='(point, cardIndex) in items'
      :key="cardIndex"
      :point-object="point"
      :card-index="cardIndex"
      @loaded-card="isLoaded(cardIndex)"
    ></small-cards>
  </div>
  `
})

new Vue({
  el: '#q-app',
  data: function() {
    return {
      selectedPointIndex: 6,
      picArray: ["https://images.takeshape.io/86ce9525-f5f2-4e97-81ba-54e8ce933da7/dev/144069dc-7390-4022-aa0f-abba022d3a2f/spec.jpg?auto=compress%2Cformat", "https://natureconservancy-h.assetsadobe.com/is/image/content/dam/tnc/nature/en/photos/prescribed_burn_oregon.jpg?crop=0,120,5760,3600&wid=1640&hei=1025&scl=3.5121951219512195", "https://orig11.deviantart.net/1062/f/2015/315/9/6/abstract__7_by_thejsyve1-d9gciwk.jpg", "https://natureconservancy-h.assetsadobe.com/is/image/content/dam/tnc/nature/en/photos/Brown_County_Hills_Leonetti.jpg?crop=33,0,1192,656&wid=4000&hei=2200&scl=0.29818181818181816", "https://www.telegraph.co.uk/content/dam/Travel/galleries/travel/destinations/northamerica/usa/US%20national%20parks/AP84847745_Yosemite_General-xlarge.jpg", "https://dehayf5mhw1h7.cloudfront.net/wp-content/uploads/sites/183/2016/09/15173325/Brown_County_Indiana_Estados_Unidos_2012-10-14_DD_10.jpg", "https://s-media-cache-ak0.pinimg.com/originals/19/e9/58/19e9581dbdc756a2dbbb38ae39a3419c.jpg", "https://cdn.pixabay.com/photo/2015/12/01/20/28/green-1072828_960_720.jpg", "https://www.alwareness.org/wp-content/uploads/2018/10/Bomen-Bos.jpg", "https://www.campz.be/info/wp-content/uploads/header-pic-mountain.jpeg", "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSUzRyiSPfzeIogLgkY1P8ugrvzls23SMhOcJi7vmUfCe4r1nKa", "https://upload.wikimedia.org/wikipedia/commons/f/ff/Pizigani_1367_Chart_10MB.jpg", "https://farm6.staticflickr.com/5720/22076039308_4e2fc21c5f_o.jpg"]
    }
  },
  template: `
	<div>
  	<h4>Scroll now occurs on 'mount'</h4>
    <small-cards-wrapper 
      :items="picArray" 
      :scroll-to-index="selectedPointIndex"
    ></small-cards-wrapper>
  </div>
  `,
})
body {
  position: absolute;
  z-index: 0;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 100px;
  min-height: 100vh;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.map-overlay {
  flex-flow: row nowrap;
  justify-content: flex-start;
  width: 100vw;
  height: 30vh;
  bottom: 0;
  left: 0;
  margin: 0;
  overflow-x: scroll;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  -ms-overflow-style: -ms-autohiding-scrollbar;
  display: flex;
  position: fixed;
  background-color: rgba(255, 150, 150, 0.3);
  font: 'Abel', 'Helvetica Neue', Arial, Helvetica, sans-serif;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  color: #8e3433;
}

.scroll {
  overflow: auto;
}

.column {
  flex-direction: column;
}

.row,
.column,
.flex {
  display: flex;
  flex-wrap: wrap;
}

div {
  display: block;
}

html,
body,
#q-app {
  width: 100%;
  direction: ltr;
}

.mycard {
  flex: 1 1 auto;
  min-width: 47vw;
  margin: 3px 0 9px 2vw;
  justify-content: flex-end;
  border-radius: 2px;
  display: flex;
}
<script src="https://unpkg.com/vue@2.6.10/dist/vue.min.js"></script>
<link href="https://unpkg.com/quasar-extras@2.0.9/material-icons/material-icons.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/quasar@^1.0.0-beta.0/dist/quasar.min.css" rel="stylesheet" type="text/css">

<script src="https://cdn.jsdelivr.net/npm/quasar@^1.0.0-beta.0/dist/quasar.umd.min.js"></script>

<div id="q-app"></div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...