【问题标题】:When to trigger a scroll after images load图片加载后何时触发滚动
【发布时间】:2019-06-02 12:34:55
【问题描述】:

使用 Quasar 框架我有多个孩子,需要在加载页面后滚动到某个孩子。我可以通过使用 setTimeout 设置延迟来做到这一点,但我更喜欢更好/防故障的解决方案。

我目前的方法是等待所有孩子安装(并使用nextTick)并认为它应该准备好滚动,但显然不是。或者,我可以等待所有图像被加载(因为QImg 有一个@load event),但这已经很晚了,因为当图像框已经渲染但仍在加载时,我已经可以手动触发滚动了。

“尽早”触发的最佳方式是什么?

点面板(父级):

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"
      />
      <q-btn
        @click.native="scrollToCenter"
        fab
        ripple
        class="fixed"
        size="10px"
        color="black"
        label="scroll"
        style="right: 18px; bottom: 120px"
      />
    </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}`)
      const 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"
      />
    </div>
  `
})

在这里您可以找到显示问题的jsfiddle。我添加了一个滚动按钮,显示加载后可以成功触发滚动(第6张图片,即“老虎”滚动到左下角)。

编辑:给这个问题更多的方向:问题是如果scroll 很快被触发,DOM 还没有渲染,因此滚动的距离还不能确定。但我会认为之后 mount Dom 应该确定了吧?!那么为什么当前的方法不起作用呢?

【问题讨论】:

    标签: javascript vue.js quasar-framework


    【解决方案1】:

    编辑/新答案:

    我终于可以让它工作了......这是我必须做的修复它:

    • ~EDIT~ 添加$nextTick(如 OP 建议的那样)使这更加一致! OP 也能够在没有包装器组件的情况下使其正常工作
    • small-cards 组件现在 emits 图像完成加载时的事件
    • 为名为@9​​87654329@ 的small-cards 组件创建了一个“包装器”
    • 这个“包装器”有 2 个props:1)items,一个图像源数组 2)scrollToIndex,你想在mount滚动到的索引号
    • small-cards-wrapper 组件将接收发出的事件(来自small-cards 组件[s])并检查它是否是您要滚动到的索引 - 如果是,我们滚动到它。
    • 基本上,我们在尝试滚动到它之前等待图像加载..

    我相信您将能够在查看代码后看到我所做的更改,但如果您有任何问题,请告诉我!

    [NEWEST JSFiddle (with $nextTick)]

    [UPDATED JSFiddle]



    原始答案:

    我在mount期间触发了scrollToCenter()方法,并从模板中删除了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>

    【讨论】:

    • 感谢您的建议。那么有没有更好的解决方案呢?当我将您的 setTimeout 设置为 0 时,滚动不再起作用,所以这并不是我真正想要的,因为现在我必须猜测什么是始终有效的延迟(当我有大量图像时ETC。)。所以我正在寻找一个事件,上面写着“现在 Dom 已经准备好,你可以触发你的滚动了”。
    • 我正朝着同一个方向前进(我的意思是使用@load 事件),这也可以在没有包装器的情况下完成。我一开始以为它已经解决了,但是没有......你和我的解决方案是在它应该(老虎)之前展示一张图片(树林)......!在安装负载和按下按钮时比较我的小提琴。按钮触发完全相同的功能,但在按钮情况下,我认为 dom 已准备好......所以恐怕还没有完全解决。还在树林里!! Relevant new fiddle
    • 我添加了nextTick,然后我认为it works 应该如此!您能否更新答案,以便我批准?
    • 所以我的确实有效,但有时需要一点时间才能启动。这是因为“老虎照片”似乎需要更长的时间来加载。点击事件不会触发直到“老虎照片”完全加载。如果您等待 20 秒左右,点击事件最终会触发。这是这里的终极问题 - 添加nextTick 似乎没有任何效果。
    • 我无法重现该问题 - 每次加载此页面时,一旦加载老虎图片,它就会触发滚动事件..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-19
    • 2014-11-14
    • 2018-02-16
    • 1970-01-01
    • 2015-04-25
    • 2015-06-26
    • 1970-01-01
    相关资源
    最近更新 更多