背景
我们使用css
transform: rotate(90deg)旋转元素的时候会发现;旋转后元素所占位置和宽高是不发生变化的;这就导致我们旋转一个长方形的元素;旋转后该元素的布局可能发生混乱;该元素可能遮挡了其他内容或被其他内容遮挡。
于是我就写了一个vue的组件;可以将需要旋转的dom元素通过slot的方式传入该组件;通过改变该组件的rotation属性;实现对传入dom元素的旋转;该组件可使得传入的dom元素旋转后宽高一起发生变化;这就解决了以上问题:
下面是示例图片
旋转前
旋转后
直接上代码
<template>
<div :class="{'rotate-wrap': isSlotContentRenderOver}" :style="wrapStyle">
<div class="rotate-box" :style="`transform: rotate(${rotation}deg)`">
<div class="content" ref="content" :style="contentStyle">
<div ref="contentInner">
<slot></slot>
</div>
</div>
</div>
</div>
</template>
<script lang="js">
import {ref, onMounted, nextTick, toRefs, onUpdated, computed, watch} from 'vue'
export default {
props: {
rotation: Number // 支持: 0, 90, 180, 270
},
setup(props) {
const isSlotContentRenderOver = ref(false)
console.log('props', props)
const {rotation} = toRefs(props)
const content = ref(null)
const contentInner = ref(null)
const contentWidth = ref(0)
const contentHeight = ref(0)
const contentWrapWidth = ref(0)
const contentWrapHeight = ref(0)
const wrapStyle = computed(() => {
if (isSlotContentRenderOver.value) {
return {width: `${contentWrapWidth.value}px`, height: `${contentWrapHeight.value}px`}
}
return {}
})
const contentStyle = computed(() => {
if (isSlotContentRenderOver.value) {
return {width: `${contentWidth.value}px`,height: `${contentHeight.value}px`}
}
return {}
})
// 设置宽高
const setContentRect = async () => {
await nextTick()
let dom = contentInner.value
let w = dom.clientWidth
let h = dom.clientHeight
contentWidth.value = w
contentHeight.value = h
// 度数转换到 0 到 360 之间
let r = (rotation.value % 360 + 360) % 360
// 旋转90度 宽高互换
if ([90, 270].includes(r)) {
w = dom.clientHeight
h = dom.clientWidth
}
contentWrapWidth.value = w
contentWrapHeight.value = h
isSlotContentRenderOver.value = true
}
onMounted(() => {
setContentRect()
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver
if (MutationObserver) {
let mo = new MutationObserver(() => {
setContentRect()
})
// 监控contentInner的dom发生变化;重新计算宽高
mo.observe(contentInner.value, {
attributes: true,
subtree: true,
childList: true
})
}
})
onUpdated(() => {
})
watch(rotation, (e) => {
setContentRect()
})
return {
isSlotContentRenderOver,
contentStyle,
wrapStyle,
content,
contentInner,
rotation,
contentWidth,
contentHeight
}
}
}
</script>
<style lang="stylus" scoped>
.rotate-wrap
display flex
align-items center
justify-content center
.rotate-box
width 0
height 0
display flex
justify-content center
align-items center
transition all 0.1s
.content
flex-shrink 0
</style>
其实原理很简单;就是在mounted时候去获取一下slot传入的dom的宽高;
然后监听旋转角度rotation发生变化时和slot内dom发生变化后;重新获取传入dom宽高,通过获取的宽高和旋转的角度判断是否宽高互换,计算出一个实际宽高,赋值给外层的dom就可以了,完美解决transform: rotate(90deg)宽高是不发生变化的问题。
下面是体验地址
示例代码已经部署在gitee;下面是地址 体验地址