|
|
@@ -1,497 +1,29 @@ |
|
|
|
<template> |
|
|
|
<view id="app" :class="{ done: done }"> |
|
|
|
<view> |
|
|
|
<h1>{{ title }}</h1> |
|
|
|
<button @click="random"> |
|
|
|
动起来 |
|
|
|
</button> |
|
|
|
</view> |
|
|
|
<view class="wrapper flex-all-center" ref="wrapperRef"> |
|
|
|
<view |
|
|
|
class="container" |
|
|
|
:style="{ width: containerW + 'px', height: containerH + 'px' }" |
|
|
|
> |
|
|
|
<view |
|
|
|
class="item flex-all-center" |
|
|
|
v-for="item in showList" |
|
|
|
:key="item.count" |
|
|
|
:style="{ |
|
|
|
left: item.left + 'px', |
|
|
|
top: item.top + 'px', |
|
|
|
height: item.height + 'px', |
|
|
|
width: item.width + 'px', |
|
|
|
background: item.color, |
|
|
|
}" |
|
|
|
> |
|
|
|
<view class="content"> |
|
|
|
<view class="title">{{ item.title }}</view> |
|
|
|
<view class="count">{{ item.count }}</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
<view class="container"></view> |
|
|
|
</template> |
|
|
|
<script> |
|
|
|
export default { |
|
|
|
data() { |
|
|
|
return { |
|
|
|
containerW: 0, |
|
|
|
containerH: 0, |
|
|
|
done: false, |
|
|
|
title: "泡泡堂", |
|
|
|
list: [], |
|
|
|
showList: [], |
|
|
|
groups: [], |
|
|
|
colors: [ |
|
|
|
"red", |
|
|
|
"green", |
|
|
|
"purple", |
|
|
|
"pink", |
|
|
|
"orange", |
|
|
|
"yellowgreen", |
|
|
|
"greenyellow", |
|
|
|
], |
|
|
|
layout: [ |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group1", |
|
|
|
direction: "horizontal", //左右 |
|
|
|
height: "30%", |
|
|
|
width: "100%", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group2", |
|
|
|
direction: "portrait", //上下 |
|
|
|
width: "40%", |
|
|
|
height: "100%", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 2, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 3, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group3", |
|
|
|
direction: "portrait", // 上下 |
|
|
|
width: "60%", |
|
|
|
height: "100%", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group4", |
|
|
|
width: "100%", |
|
|
|
height: "80%", |
|
|
|
direction: "horizontal", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 1, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 0, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group5", |
|
|
|
direction: "portrait", // 上下 |
|
|
|
width: "100%", |
|
|
|
height: "20%", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 4, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group6", |
|
|
|
direction: "horizontal", //左右 |
|
|
|
height: "30%", |
|
|
|
width: "100%", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group7", |
|
|
|
width: "50%", |
|
|
|
height: "100%", |
|
|
|
direction: "portrait", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 5, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 7, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group8", |
|
|
|
width: "50%", |
|
|
|
height: "100%", |
|
|
|
direction: "horizontal", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 8, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 6, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group9", |
|
|
|
width: "100%", |
|
|
|
height: "40%", |
|
|
|
direction: "horizontal", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group9", |
|
|
|
width: "30%", |
|
|
|
height: "100%", |
|
|
|
direction: "portrait", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 9, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 11, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 10, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group9", |
|
|
|
width: "70%", |
|
|
|
height: "100%", |
|
|
|
direction: "portrait", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group10", |
|
|
|
width: "100%", |
|
|
|
height: "30%", |
|
|
|
direction: "horizontal", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 12, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 16, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group11", |
|
|
|
width: "100%", |
|
|
|
height: "70%", |
|
|
|
direction: "horizontal", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group11", |
|
|
|
width: "50%", |
|
|
|
height: "100%", |
|
|
|
direction: "horizontal", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 17, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "group", |
|
|
|
name: "group11", |
|
|
|
width: "50%", |
|
|
|
height: "100%", |
|
|
|
direction: "portrait", |
|
|
|
children: [ |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 13, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 14, |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: "node", |
|
|
|
index: 15, |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
], |
|
|
|
}, |
|
|
|
], |
|
|
|
|
|
|
|
}; |
|
|
|
}, |
|
|
|
|
|
|
|
onShow() {}, |
|
|
|
mounted() { |
|
|
|
this.$nextTick(() => { |
|
|
|
console.log(this.$refs.wrapperRef); |
|
|
|
// this.containerW = this.$refs.wrapperRef.clientWidth; |
|
|
|
this.containerW = 230; |
|
|
|
// this.containerH = '1380rpx' |
|
|
|
this.containerH = this.containerW * 2; |
|
|
|
console.log(this.containerW, this.containerH); |
|
|
|
this.initList(); |
|
|
|
this.initPosition(); |
|
|
|
}); |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
percentToPx(cur, compared) { |
|
|
|
if (typeof cur !== "string" || !cur.includes("%")) return cur; |
|
|
|
return (compared * parseFloat(cur)) / 100; |
|
|
|
}, |
|
|
|
getRandom(min = 1, max = 10000) { |
|
|
|
return Math.floor(Math.random() * (max - min) + min); |
|
|
|
}, |
|
|
|
initList() { |
|
|
|
this.list = []; |
|
|
|
for (let i = 0; i < 18; i++) { |
|
|
|
let item = {} |
|
|
|
item = Object.create(null); |
|
|
|
item.title = `标题${i}`; |
|
|
|
item.color = this.colors[this.getRandom(0, this.colors.length - 1)]; |
|
|
|
item.no = i; |
|
|
|
item.count = this.getRandom(); |
|
|
|
item.width = this.containerW / 3; |
|
|
|
item.height = this.containerW / 3; |
|
|
|
this.list.push(item); |
|
|
|
} |
|
|
|
}, |
|
|
|
initPosition() { |
|
|
|
this.showList = this.list.map((item, index) => { |
|
|
|
return { |
|
|
|
left: ((index % 3) * this.containerW) / 3, |
|
|
|
top: (Math.floor(index / 3) * this.containerW) / 3, |
|
|
|
...item, |
|
|
|
}; |
|
|
|
}); |
|
|
|
}, |
|
|
|
random() { |
|
|
|
const parent = { |
|
|
|
left: 0, |
|
|
|
top: 0, |
|
|
|
name: "group0", |
|
|
|
width: this.percentToPx("100%", this.containerW), |
|
|
|
height: this.percentToPx("100%", this.containerH), |
|
|
|
direction: "portrait", |
|
|
|
childrenLength: this.layout.length, |
|
|
|
}; |
|
|
|
this.groups = [parent]; |
|
|
|
this.getPosition(this.layout, parent); |
|
|
|
this.showList = this.list; |
|
|
|
this.done = true; |
|
|
|
}, |
|
|
|
getPosition(layout, parent) { |
|
|
|
// 纵向的,只需要计算高度,如果只有一个元素,则高度100% |
|
|
|
// 横向的,只需要计算宽度,如果只有一个元素,则宽度100% |
|
|
|
// 如果type为node,则表明是最小单位了,则需要根据index获得数量,然后往上一层和同级别的组进行比较得出宽(横向)高(纵向) |
|
|
|
// 但是所有的宽高都计算完之后,还是百分比的,如何计算具体位置呢 |
|
|
|
const list = this.list; |
|
|
|
layout.forEach((item, index) => { |
|
|
|
if (item.type === "group") { |
|
|
|
const current = { |
|
|
|
left: 0, |
|
|
|
top: 0, |
|
|
|
name: item.name, |
|
|
|
width: this.percentToPx(item.width, parent.width), |
|
|
|
height: this.percentToPx(item.height, parent.height), |
|
|
|
direction: item.direction, |
|
|
|
childrenLength: item.children.length, |
|
|
|
}; |
|
|
|
const preItem = index === 0 ? null : layout[index - 1]; |
|
|
|
// 组 |
|
|
|
// 横纵向分布都按照设置来 |
|
|
|
if (parent.direction === "horizontal") { |
|
|
|
current.left = |
|
|
|
index === 0 |
|
|
|
? parent.left |
|
|
|
: preItem.left + this.percentToPx(preItem.width, parent.width); |
|
|
|
current.top = parent.top; |
|
|
|
} else { |
|
|
|
current.left = parent.left; |
|
|
|
current.top = |
|
|
|
index === 0 |
|
|
|
? parent.top |
|
|
|
: preItem.top + this.percentToPx(preItem.height, parent.height); |
|
|
|
} |
|
|
|
item.left = current.left; |
|
|
|
item.top = current.top; |
|
|
|
this.groups.push(current); |
|
|
|
item.nodeTotal = 0; |
|
|
|
if (item.children && item.children.length > 0) { |
|
|
|
if (item.children[0].type === "node") { |
|
|
|
item.children.forEach((v) => { |
|
|
|
item.nodeTotal += list[v.index].count; |
|
|
|
}); |
|
|
|
} |
|
|
|
current.nodeTotal = item.nodeTotal; |
|
|
|
this.getPosition(item.children, current); |
|
|
|
} |
|
|
|
} else { |
|
|
|
const listItem = list[item.index]; |
|
|
|
const preItem = index === 0 ? null : list[layout[index - 1].index]; |
|
|
|
if (parent.direction === "horizontal") { |
|
|
|
// 节点横向排布,计算宽度 |
|
|
|
listItem.left = |
|
|
|
index === 0 |
|
|
|
? parent.left |
|
|
|
: preItem.left + this.percentToPx(preItem.width, parent.width); // TODO: 需要计算上一个元素的left + width: ; |
|
|
|
listItem.top = parent.top; |
|
|
|
listItem.width = this.percentToPx( |
|
|
|
(listItem.count / parent.nodeTotal) * 100 + "%", |
|
|
|
parent.width |
|
|
|
); |
|
|
|
listItem.height = this.percentToPx("100%", parent.height); |
|
|
|
} else if (parent.direction === "portrait") { |
|
|
|
// 节点横向排布,计算宽度 |
|
|
|
listItem.left = parent.left; |
|
|
|
listItem.top = |
|
|
|
index === 0 |
|
|
|
? parent.top |
|
|
|
: preItem.top + this.percentToPx(preItem.height, parent.height); // TODO: 需要计算上一个元素的left + width: ; |
|
|
|
listItem.width = this.percentToPx("100%", parent.width); |
|
|
|
listItem.height = this.percentToPx( |
|
|
|
(listItem.count / parent.nodeTotal) * 100 + "%", |
|
|
|
parent.height |
|
|
|
); |
|
|
|
} |
|
|
|
listItem.name = `node${item.index}`; |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
}, |
|
|
|
methods: {}, |
|
|
|
onLoad() {}, |
|
|
|
onHide() {}, |
|
|
|
}; |
|
|
|
</script> |
|
|
|
|
|
|
|
<style lang="scss" scoped> |
|
|
|
* { |
|
|
|
margin: 0; |
|
|
|
padding: 0; |
|
|
|
} |
|
|
|
|
|
|
|
.wrapper { |
|
|
|
margin: 0 auto; |
|
|
|
width: 90%; |
|
|
|
} |
|
|
|
|
|
|
|
.container { |
|
|
|
position: relative; |
|
|
|
} |
|
|
|
|
|
|
|
.item { |
|
|
|
position: absolute; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
transform: scale(0.8); |
|
|
|
transform-origin: center; |
|
|
|
border-radius: 16px; |
|
|
|
text-align: center; |
|
|
|
transition: 400ms linear; |
|
|
|
border: 1px solid pink; |
|
|
|
background-color: yellow; |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
|
|
|
|
.group-item { |
|
|
|
position: absolute; |
|
|
|
border-radius: 8px; |
|
|
|
text-align: center; |
|
|
|
border: 1px solid pink; |
|
|
|
background-color: rgba(0, 128, 0, 0.4); |
|
|
|
box-sizing: border-box; |
|
|
|
font-size: 20px; |
|
|
|
} |
|
|
|
|
|
|
|
.item .content { |
|
|
|
position: absolute; |
|
|
|
bottom: 4px; |
|
|
|
.container{ |
|
|
|
width: 100%; |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: flex-end; |
|
|
|
text-align: bottom; |
|
|
|
min-height: 100vh; |
|
|
|
background: #f8f8f8; |
|
|
|
} |
|
|
|
|
|
|
|
.item .title, |
|
|
|
.item .count { |
|
|
|
font-weight: bold; |
|
|
|
line-height: 1em; |
|
|
|
} |
|
|
|
|
|
|
|
.item .title { |
|
|
|
font-size: 16px; |
|
|
|
flex: 1; |
|
|
|
text-align: left; |
|
|
|
} |
|
|
|
|
|
|
|
.item .count { |
|
|
|
font-size: 12px; |
|
|
|
padding-right: 12px; |
|
|
|
text-align: right; |
|
|
|
} |
|
|
|
|
|
|
|
.item .count::before { |
|
|
|
content: "票"; |
|
|
|
position: absolute; |
|
|
|
font-size: 12px; |
|
|
|
transform: scale(0.8); |
|
|
|
transform-origin: center 80%; |
|
|
|
color: #666; |
|
|
|
right: 0; |
|
|
|
bottom: 0; |
|
|
|
} |
|
|
|
|
|
|
|
.done .item { |
|
|
|
transform: scale(1); |
|
|
|
} |
|
|
|
|
|
|
|
.flex-all-center { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
} |
|
|
|
</style> |