css+js实现旋转90度后宽高互换

2,065 阅读1分钟

背景

我们使用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发生变化时和slotdom发生变化后;重新获取传入dom宽高,通过获取的宽高和旋转的角度判断是否宽高互换,计算出一个实际宽高,赋值给外层的dom就可以了,完美解决transform: rotate(90deg)宽高是不发生变化的问题。

下面是体验地址

示例代码已经部署在gitee;下面是地址 体验地址