【问题标题】:Calculating height and width of Vue slots计算 Vue 插槽的高度和宽度
【发布时间】:2018-05-05 08:08:08
【问题描述】:

我无法计算插槽的高度和宽度。我正在尝试在我的外围组件中渲染图像。这些图像的尺寸为 105x160。但是,当我控制台记录 clientWidth 和 clientHeight 时,我得到 0x24。

我相信我的问题与此有关:In vue.js 2, measure the height of a component once slots are rendered 但我仍然无法弄清楚。我已经尝试在 Perimeter 组件和单个插槽组件上使用 $nextTick 。

在我的外围组件中,我有:

<template>
  <div class="d-flex">
    <slot></slot>
    <div class="align-self-center">
      <slot name="center-piece"></slot>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Perimeter',
    mounted() {
      this.distributeSlots();
    },
    updated() {
      this.distributeSlots();
    },
    computed: {
      centerRadius() {
        return this.$slots['center-piece'][0].elm.clientWidth / 2;
      },
    },
    methods: {
      distributeSlots() {
        let angle = 0;
        const {
          clientHeight: componentHeight,
          clientWidth: componentWidth,
          offsetTop: componentOffsetTop,
          offsetLeft: componentOffsetLeft,
        } = this.$el;
        const componentXCenter = componentWidth / 2;
        const componentYCenter = componentHeight / 2;

        const slots = this.$slots.default.filter(slot => slot.tag) || [];
        const step = (2 * Math.PI) / slots.length;

        slots.forEach((slot) => {
          slot.context.$nextTick(() => {
            const { height, width } = slot.elm.getBoundingClientRect();
            console.log(`height ${height}, width ${width}`);
            const distanceFromCenterX = (this.centerRadius + componentXCenter) * Math.cos(angle);
            const distanceFromCenterY = (this.centerRadius + componentYCenter) * Math.sin(angle);
            const x = Math.round((componentXCenter + distanceFromCenterX + componentOffsetLeft) - (width / 2));
            const y = Math.round((componentYCenter + distanceFromCenterY + componentOffsetTop) - (height / 2));

            slot.elm.style.left = `${x}px`;
            slot.elm.style.top = `${y}px`;

            angle += step;
          });
        });
      },
    },
  };
</script>

我还编写了没有$nextTick 的distributeSlots() 方法:

distributeSlots() {
  let angle = 0;
  const {
    clientHeight: componentHeight,
    clientWidth: componentWidth,
    offsetTop: componentOffsetTop,
    offsetLeft: componentOffsetLeft,
  } = this.$el;
  const componentXCenter = componentWidth / 2;
  const componentYCenter = componentHeight / 2;

  const slots = this.$slots.default.filter(slot => slot.tag) || [];
  const step = (2 * Math.PI) / slots.length;

  slots.forEach((slot) => {
    const { height, width } = slot.elm.getBoundingClientRect();
    const distanceFromCenterX = (this.centerRadius + componentXCenter) * Math.cos(angle);
    const distanceFromCenterY = (this.centerRadius + componentYCenter) * Math.sin(angle);
    const x = Math.round((componentXCenter + distanceFromCenterX + componentOffsetLeft) - (width / 2));
    const y = Math.round((componentYCenter + distanceFromCenterY + componentOffsetTop) - (height / 2));

    slot.elm.style.left = `${x}px`;
    slot.elm.style.top = `${y}px`;

    angle += step;
  });
},

我传递给 Perimeter 组件如下:

<template>
  <perimeter>
    <div v-for="(book, index) in books.slice(0, 6)" v-if="book.image" :key="book.asin" style="position: absolute">
      <router-link :to="{ name: 'books', params: { isbn: book.isbn }}">
        <img :src="book.image" />
      </router-link>
    </div>
  <perimeter>
</template>

更糟糕的是,当我在 forEach 函数中使用 console.log(slot.elm) 并在浏览器控制台中打开数组时,我看到了正确的 clientHeight + clientWidth:

【问题讨论】:

    标签: javascript dom vue.js vuejs2


    【解决方案1】:

    通常在这种情况下,这是一个逻辑错误,而不是框架的问题。所以我会尽量简化你的代码来证明你的问题。

    假设您在mounted() 或之后获得clientWidthclientHeight,如下所示,它应该可以正常工作。

    避免任何计时器黑客,它们是极难调试的错误的罪魁祸首。

    <template>
      <div style="min-height: 100px; min-width: 100px;">
        <slot />
      </div>
    </template>
    
    <script>
    export default {
      name: 'MyContainer',
      data (){
        return {
          width: 0,
          height: 0,
        }
      },
      mounted (){
        this.width = this.$slots["default"][0].elm.clientWidth
        this.height = this.$slots["default"][0].elm.clientHeight
        console.log(this.width, this.height)  // => 100 100 (or more)
      },
    
    }
    </script>
    
    <style scoped lang="scss">
    </style>
    

    【讨论】:

      【解决方案2】:

      您可以使用一个技巧,将代码放在setTimeout 方法中,以便在另一个线程中执行它,并有微小的延迟:

      setTimeout(() => {
           // Put your code here
           ...
      }, 80)
      

      【讨论】:

      • 这是一个不可靠的 hack,并不能解决手头的问题。
      • 是的,但值得一提。
      • 如果 DOM 更新实际耗时超过 80 毫秒怎么办?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-19
      • 2015-12-16
      • 2011-03-28
      • 2017-10-21
      • 2019-05-31
      • 2012-12-16
      • 1970-01-01
      相关资源
      最近更新 更多