简单实现矩形面积块

最终实现效果:

在这里插入图片描述

实现算法

简单实现一个构造面积函数:
1、需要一个父级盒子,要知道它的宽高;
2、面积块之间的间隙
3、一个宽高比: 这个主要是用于渲染出来的面积块,大部分是满足这个宽高比的。
4、 每次取剩余面积的一条边作为块的宽或者高,最后一块直接等于剩余的面积,

// 外层盒子的一些配置
 parentInfo = {
    gap: 5, // 间隙
    parentW: 599, // 外层宽度
    parentH: 168, // 外层高度
    squareRatio: 2, // 宽高比
}

// 各个块的初始值,num表示块的大小
arr = [
   { id: 1, num: 2 },
   { id: 2, num: 1 },
   { id: 3, num: 5 },
],

 // 传入数据数组,总宽高,生成各自的宽高
getItemsWAndH(parentInfo, arr) {
  // 父级盒子信息
  const { gap, parentW, parentH, squareRatio } = parentInfo
  let remainX = parentW
  let remainY = parentH
  
  // 面积块各自占比
  const numList = arr.map((i) => i.num)
  const resWH = []
  arr.forEach((item, index) => {
    let obj = { id: item.id }
    if (index === arr.length - 1) {
      // 最后一个直接等于
      obj['width'] = remainX
      obj['height'] = remainY
    } else {
      // 计算剩余面积占比
      const remainSum = _.sum(numList.slice(index))
      // 当前块占比例
      const ratio = item.num / remainSum

      if (remainX / remainY < squareRatio) {
      	// 小于宽高比,取宽度
        obj['width'] = remainX
        const y = Math.floor(remainY * ratio) // 向下取整
        obj['height'] = y
        remainY = remainY - y - gap
      } else {
      	// 不小于宽高比,取高度
        obj['height'] = remainY
        const x = Math.floor(remainX * ratio)
        obj['width'] = x
        remainX = remainX - x - gap
      }
    }
	// 存储每一项的宽高
    resWH.push(obj)
  })
  // 设置每个项位置
  this.items = this.setItemPosition(resWH, this.parentInfo)
},

通过上一个函数就可以计算出每一项的宽高了,接下来就是满满当当的塞到父级盒子中,一开始直接用的flex布局

// flex布局,是不行的,
.rec-area-map {
  display: flex;
  flex-flow: column wrap;
  gap: 5px;
}

但是flex布局无法适应下面这种情况:
在这里插入图片描述

所以改用js构造整个面积图内的各个块的位置。主要是利用absolutetopleft来定义每个块的位置

setItemPosition(list, parentInfo) {
   // 行列间隙
   const divVGap = parentInfo.gap || 5
   const divHGap = parentInfo.gap || 5

   // 父级盒子宽高
   let boxWidth = parentInfo.parentW
   let boxHeight = parentInfo.parentH

   // 计算每一个项放入后的left和top位置
   let left = 0
   let top = 0
   list.forEach((item, i) => {
     const w = item.width
     const h = item.height

     item.left = left
     item.top = top

     // 宽度相等时
     if(w === boxWidth) {
       const localH = h + divHGap
       top += localH
       boxHeight -= localH
     }

     // 高度相等时
     if(h === boxHeight) {
       const localW = w + divVGap
       left += localW
       boxWidth -= localW
     }
   })
   return list
 },

完整代码

<template>
  <div class="rec-area-map" :style="parentStyle">
    <div
      v-for="(item, index) in items"
      :key="item.id"
      :style="{
        width: item.width + 'px', 
        height: item.height + 'px', 
        background: colors[index],
        top: item.top + 'px',
        left: item.left + 'px',
        position: 'absolute'
      }"
    >
      <div class="grid-content">
        <slot :info="{...item, index}">{{item.id}}</slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    colors: {
      type: Array,
      default: () => [
        '#E6F5FF',
        '#ECF2FF',
        '#E4FBF4',
        '#dfe7d7',
        '#cfe3ef',
        '#f1e0dc',
        '#e3c7c5',
        '#bbd69d',
      ],
    },
    config: {
      type: Object,
      default: () => ({}),
    },
    ratioList: {
      type: Array,
      default: () => [
        { id: 1, num: 2 },
        { id: 2, num: 1 },
        { id: 3, num: 1 },
      ],
    },
  },
  data() {
    return {
      items: [],
    }
  },
  computed: {
    parentStyle() {
      return {
        width: this.parentInfo.parentW + 'px',
        height: this.parentInfo.parentH + 'px',
        gap: this.parentInfo.gap + 'px',
      }
    },
    parentInfo() {
      return {
        gap: 5, // 间隙
        parentW: 599, // 外层宽度
        parentH: 168, // 外层高度
        squareRatio: 2, // 宽高比
        ...this.config, // 外部传入,覆盖上面的
      }
    },
  },

  methods: {
    // 传入数据数组,总宽高,生成各自的宽高
    getItemsWAndH(parentInfo, arr) {
      
      // 父级盒子信息
      const { gap, parentW, parentH, squareRatio } = parentInfo
      let remainX = parentW
      let remainY = parentH

      // 各个块的面积值
      const numList = arr.map((i) => i.num)

      const resWH = []
      arr.forEach((item, index) => {
        let obj = { id: item.id }
        if (index === arr.length - 1) {
          // 最后一个直接等于
          obj['width'] = remainX
          obj['height'] = remainY
        } else {
          // 计算剩余面积占比
          const remainSum = _.sum(numList.slice(index))
          const ratio = item.num / remainSum
          console.log(ratio, squareRatio, remainX, remainY, remainX / remainY)
          if (remainX / remainY < squareRatio) {
            // 小于宽高比,取宽度
            obj['width'] = remainX
            const y = Math.floor(remainY * ratio) // 向下取整
            obj['height'] = y
            remainY = remainY - y - gap
          } else {
            // 不小于宽高比,取高度
            obj['height'] = remainY
            const x = Math.floor(remainX * ratio)
            obj['width'] = x
            remainX = remainX - x - gap
          }
        }
        resWH.push(obj)
      })

      this.items = this.setItemPosition(resWH, this.parentInfo)
    },

    setItemPosition(list, parentInfo) {
      // 行列间隙
      const divVGap = parentInfo.gap || 5
      const divHGap = parentInfo.gap || 5

      // 父级盒子宽高
      let boxWidth = parentInfo.parentW
      let boxHeight = parentInfo.parentH

      // 计算每一个项放入后的left和top位置
      let left = 0
      let top = 0
      list.forEach((item, i) => {
        const w = item.width
        const h = item.height

        item.left = left
        item.top = top

        // 宽度相等时
        if(w === boxWidth) {
          const localH = h + divHGap
          top += localH
          boxHeight -= localH
        }

        // 高度相等时
        if(h === boxHeight) {
          const localW = w + divVGap
          left += localW
          boxWidth -= localW
        }
      })
      return list
    },
  },

  watch: {
    ratioList: {
      handler() {
        this.getItemsWAndH(this.parentInfo, this.ratioList)
      },
      deep: true,
      immediate: true,
    },
  },
}
</script>

<style lang="scss" scoped>
.rec-area-map {
  position: relative;
}
.grid-content {
  height: 100%;
  width: 100%;
}
</style>

待改进

1、最小面积块
2、现有算法无法实现瀑布流那种,仅适用于小区域使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值