I am having trouble calculating the height and width of slots. I am trying to render images in my Perimeter component. These images have a size 105x160. However, when I console.log the clientWidth and clientHeight, I get 0x24.
I believe my problem is related to this: In vue.js 2, measure the height of a component once slots are rendered but I still can't figure it out. I've tried using $nextTick on both the Perimeter component and the individual slot components.
In my Perimeter component, I have:
<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>
I also had my distributeSlots() method written without $nextTick:
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;
});
},
I am passing to the Perimeter component as follows:
<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>
Even worse, when I console.log(slot.elm) in the forEach function and open up the array in the browser console, I see the correct clientHeight + clientWidth:
Usually in such cases it's a logical mistake, rather than an issue with the framework. So I would go with simplifying your code to the bare minimum that demonstrates your issue.
Assuming you get clientWidth
and clientHeight
on mounted()
or afterwards, as demonstarted below, it should just work.
Avoid any timer hacks, they are the culprit for bugs that are extremely hard to debug.
<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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With