409 lines
16 KiB
Vue
409 lines
16 KiB
Vue
<template>
|
||
<div class="page-container" ref="container">
|
||
<div class="layout-buttons">
|
||
<button @click="switchLayout('strategy1')">默认布局</button>
|
||
<button @click="switchLayout('strategy2')">策略2布局</button>
|
||
<button @click="switchLayout('strategy3')">策略3布局</button>
|
||
</div>
|
||
|
||
<div>屏幕宽度:{{ screenWidth }}px</div>
|
||
|
||
<div class="components-container">
|
||
<component
|
||
v-for="component in components"
|
||
:is="component.comp"
|
||
:key="component.id"
|
||
class="component"
|
||
:style="getComponentStyle(component)"
|
||
></component>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from 'vue';
|
||
import Comp1 from './comp1.vue';
|
||
import Comp2 from './comp2.vue';
|
||
import Comp3 from './comp3.vue';
|
||
import Comp4 from './comp4.vue';
|
||
|
||
interface ComponentState {
|
||
id: string;
|
||
comp: ReturnType<typeof defineComponent>;
|
||
x: number | string;
|
||
y: number | string;
|
||
width?: number | string;
|
||
minWidth?: number | string;
|
||
maxWidth?: number | string;
|
||
height: number;
|
||
zIndex?: number | string;
|
||
background?: string;
|
||
}
|
||
|
||
const components = ref<ComponentState[]>([]);
|
||
const container = ref<HTMLElement | null>(null);
|
||
const screenWidth = ref(0);
|
||
const screenHeight = ref(0);
|
||
const layoutComp1 = shallowRef(Comp1);
|
||
const layoutComp2 = shallowRef(Comp2);
|
||
const layoutComp3 = shallowRef(Comp3);
|
||
const layoutComp4 = shallowRef(Comp4);
|
||
|
||
const layoutCompMap = {
|
||
comp1: layoutComp1,
|
||
comp2: layoutComp2,
|
||
comp3: layoutComp3,
|
||
comp4: layoutComp4,
|
||
};
|
||
|
||
const containerWidth = ref(0);
|
||
const containerHeight = ref(0);
|
||
const currentLayoutStrategy = ref<LayoutStrategy>('strategy1');
|
||
|
||
onMounted(() => {
|
||
const observedElement = document.querySelector('#app');
|
||
if (observedElement) {
|
||
const resizeObserver = new ResizeObserver((entries) => {
|
||
const entry = entries[0];
|
||
const { width, height } = entry.contentRect;
|
||
screenWidth.value = width;
|
||
screenHeight.value = height;
|
||
|
||
console.log('screenWidth', width);
|
||
|
||
const compStyles = loadComponentWidthRange(currentLayoutStrategy.value, width);
|
||
if (compStyles) {
|
||
setComponentStyleByStrategy(compStyles);
|
||
}
|
||
// const screenResolution = getComponentWidthRange(width);
|
||
// if (screenResolution) {
|
||
// setComponentStyleByStrategy(screenResolution.compStyles);
|
||
// }
|
||
});
|
||
resizeObserver.observe(observedElement);
|
||
}
|
||
});
|
||
|
||
type Coordinate = number | [number, number];
|
||
|
||
type ComponentWidthRange = {
|
||
width: [number, number];
|
||
height: number;
|
||
compName: string;
|
||
background?: string;
|
||
x: Coordinate;
|
||
y: Coordinate;
|
||
};
|
||
|
||
type ScreenResolutionMap = {
|
||
[resolutionRange: string]: {
|
||
layoutStrategy: string;
|
||
compStyles: ComponentWidthRange[];
|
||
};
|
||
};
|
||
|
||
type LayoutStrategy = 'strategy1' | 'strategy2' | 'strategy3';
|
||
|
||
type Resolution = {
|
||
[resolutionRange: string]: ComponentWidthRange[];
|
||
};
|
||
|
||
type ScreenResolutionMap1 = {
|
||
layoutStrategy: LayoutStrategy;
|
||
resolution: Resolution;
|
||
};
|
||
|
||
const screenResolutionMap1: ScreenResolutionMap1[] = [
|
||
{
|
||
layoutStrategy: 'strategy1',
|
||
resolution: {
|
||
'[0, 768]': [
|
||
{ width: [498, 498], compName: 'comp1', height: 550, background: 'red', x: 266, y: 0 },
|
||
{ width: [498, 498], compName: 'comp2', height: 304, background: '#ccdc', x: [266, 266], y: 550 },
|
||
{ width: [266, 266], compName: 'comp3', height: 856, background: '#5E6673', x: 0, y: 0 },
|
||
{ width: [768, 768], compName: 'comp4', height: 304, background: 'blue', x: 0, y: 856 },
|
||
],
|
||
'[769, 1280]': [
|
||
{ width: [498, 836], compName: 'comp1', height: 610, background: 'red', x: 0, y: 0 },
|
||
{ width: [135, 222], compName: 'comp2', height: 610, background: '#ccdc', x: [498, 836], y: 0 },
|
||
{ width: [135, 222], compName: 'comp3', height: 908, background: '#5E6673', x: [768, 1012], y: 0 },
|
||
{ width: [768, 1012], compName: 'comp4', height: 356, background: 'blue', x: 0, y: 610 },
|
||
],
|
||
'[1281, 1440]': [
|
||
{ width: [836, 836], compName: 'comp1', height: 610, background: 'red', x: 0, y: 0 },
|
||
{ width: [222, 302], compName: 'comp2', height: 610, background: '#ccdc', x: 836, y: 0 },
|
||
{ width: [222, 302], compName: 'comp3', height: 908, background: '#5E6673', x: [1058, 1138], y: 0 },
|
||
{ width: [1012, 1138], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 610 },
|
||
],
|
||
'[1441, 1920]': [
|
||
{ width: [836, 1196], compName: 'comp1', height: 610, background: 'red', x: 0, y: 0 },
|
||
{ width: [302, 362], compName: 'comp2', height: 610, background: '#ccdc', x: [836, 1196], y: 0 },
|
||
{ width: [302, 362], compName: 'comp3', height: 908, background: '#5E6673', x: [1138, 1158], y: 0 },
|
||
{ width: [1138, 1558], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 610 },
|
||
],
|
||
'[1921, 3860]': [
|
||
{ width: [1196, 1696], compName: 'comp1', height: 610, background: 'red', x: 0, y: 0 },
|
||
{ width: [362, 432], compName: 'comp2', height: 610, background: '#ccdc', x: [1196, 1696], y: 0 },
|
||
{ width: [362, 432], compName: 'comp3', height: 908, background: '#5E6673', x: [1558, 2128], y: 0 },
|
||
{ width: [1558, 2128], compName: 'comp4', height: 356, background: 'blue', x: 0, y: 610 },
|
||
],
|
||
},
|
||
},
|
||
{
|
||
layoutStrategy: 'strategy2',
|
||
resolution: {
|
||
'[0, 768]': [
|
||
{ width: [200, 300], compName: 'comp1', height: 500, background: 'red', x: 0, y: 0 },
|
||
{ width: [300, 400], compName: 'comp2', height: 500, background: '#ccdc', x: [200, 300], y: 0 },
|
||
{ width: [300, 400], compName: 'comp3', height: 970, background: '#5E6673', x: [500, 700], y: 0 },
|
||
{ width: [300, 400], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
'[769, 1280]': [
|
||
{ width: [200, 300], compName: 'comp1', height: 500, background: 'red', x: 0, y: 0 },
|
||
{ width: [300, 400], compName: 'comp2', height: 500, background: '#ccdc', x: [200, 300], y: 0 },
|
||
{ width: [300, 400], compName: 'comp3', height: 970, background: '#5E6673', x: [500, 700], y: 0 },
|
||
{ width: [300, 400], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
'[1281, 1440]': [
|
||
{ width: [280, 400], compName: 'comp1', height: 600, background: 'red', x: 0, y: 0 },
|
||
{ width: [350, 450], compName: 'comp2', height: 500, background: '#ccdc', x: [280, 400], y: 0 },
|
||
{ width: [350, 450], compName: 'comp3', height: 970, background: '#5E6673', x: [630, 850], y: 0 },
|
||
{ width: [350, 450], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 600 },
|
||
],
|
||
'[1441, 1920]': [
|
||
{ width: [320, 420], compName: 'comp1', height: 500, background: 'red', x: 0, y: 0 },
|
||
{ width: [400, 500], compName: 'comp2', height: 500, background: '#ccdc', x: [320, 420], y: 0 },
|
||
{ width: [400, 500], compName: 'comp3', height: 970, background: '#5E6673', x: [720, 920], y: 0 },
|
||
{ width: [400, 500], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
'[1921, 3860]': [
|
||
{ width: [800, 1000], compName: 'comp1', height: 600, background: 'red', x: 0, y: 0 },
|
||
{ width: [500, 600], compName: 'comp2', height: 600, background: '#ccdc', x: [800, 1000], y: 0 },
|
||
{ width: [500, 600], compName: 'comp3', height: 1070, background: '#5E6673', x: [1300, 1600], y: 0 },
|
||
{ width: [500, 600], compName: 'comp4', height: 510, background: 'blue', x: 0, y: 600 },
|
||
],
|
||
},
|
||
},
|
||
{
|
||
layoutStrategy: 'strategy3',
|
||
resolution: {
|
||
'[0, 768]': [
|
||
{ width: [200, 300], compName: 'comp1', height: 500, background: 'red', x: 0, y: 0 },
|
||
{ width: [300, 400], compName: 'comp2', height: 500, background: '#ccdc', x: [200, 300], y: 0 },
|
||
{ width: [300, 400], compName: 'comp3', height: 970, background: '#5E6673', x: [500, 700], y: 0 },
|
||
{ width: [300, 400], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
'[769, 1280]': [
|
||
{ width: [200, 300], compName: 'comp1', height: 500, background: 'red', x: 0, y: 0 },
|
||
{ width: [300, 400], compName: 'comp2', height: 500, background: '#ccdc', x: [200, 300], y: 0 },
|
||
{ width: [300, 400], compName: 'comp3', height: 970, background: '#5E6673', x: [500, 700], y: 0 },
|
||
{ width: [300, 400], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
'[1281, 1440]': [
|
||
{ width: [280, 400], compName: 'comp1', height: 600, background: 'red', x: 0, y: 0 },
|
||
{ width: [350, 450], compName: 'comp2', height: 500, background: '#ccdc', x: [280, 400], y: 0 },
|
||
{ width: [350, 450], compName: 'comp3', height: 970, background: '#5E6673', x: [630, 850], y: 0 },
|
||
{ width: [350, 450], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 600 },
|
||
],
|
||
'[1441, 1920]': [
|
||
{ width: [320, 420], compName: 'comp1', height: 500, background: 'red', x: 0, y: 0 },
|
||
{ width: [400, 500], compName: 'comp2', height: 500, background: '#ccdc', x: [320, 420], y: 0 },
|
||
{ width: [400, 500], compName: 'comp3', height: 970, background: '#5E6673', x: [720, 920], y: 0 },
|
||
{ width: [400, 500], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
'[1921, 3860]': [
|
||
{ width: [800, 1000], compName: 'comp1', height: 600, background: 'red', x: 0, y: 0 },
|
||
{ width: [500, 600], compName: 'comp2', height: 600, background: '#ccdc', x: [800, 1000], y: 0 },
|
||
{ width: [500, 600], compName: 'comp3', height: 1070, background: '#5E6673', x: [1300, 1600], y: 0 },
|
||
{ width: [500, 600], compName: 'comp4', height: 510, background: 'blue', x: 0, y: 600 },
|
||
],
|
||
},
|
||
},
|
||
];
|
||
|
||
// 创建屏幕分辨率宽度到组件宽度区间的映射表
|
||
const screenResolutionMap: ScreenResolutionMap = {
|
||
'[1024, 1280]': {
|
||
layoutStrategy: 'strategy1',
|
||
compStyles: [
|
||
{ width: [200, 300], compName: 'comp1', height: 500, background: 'red', x: 0, y: 0 },
|
||
{ width: [300, 400], compName: 'comp2', height: 500, background: '#ccdc', x: [200, 300], y: 0 },
|
||
{ width: [300, 400], compName: 'comp3', height: 970, background: '#5E6673', x: [500, 700], y: 0 },
|
||
{ width: [300, 400], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
},
|
||
'[1281, 1440]': {
|
||
layoutStrategy: 'strategy2',
|
||
compStyles: [
|
||
{ width: [280, 400], compName: 'comp1', height: 600, background: 'red', x: 0, y: 0 },
|
||
{ width: [350, 450], compName: 'comp2', height: 500, background: '#ccdc', x: [280, 400], y: 0 },
|
||
{ width: [350, 450], compName: 'comp3', height: 970, background: '#5E6673', x: [630, 850], y: 0 },
|
||
{ width: [350, 450], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 600 },
|
||
],
|
||
},
|
||
'[1441, 1920]': {
|
||
layoutStrategy: 'strategy3',
|
||
compStyles: [
|
||
{ width: [320, 420], compName: 'comp1', height: 500, background: 'red', x: [400, 500], y: 0 },
|
||
{ width: [400, 500], compName: 'comp2', height: 500, background: '#ccdc', x: 0, y: 0 },
|
||
{ width: [400, 500], compName: 'comp3', height: 970, background: '#5E6673', x: [720, 920], y: 0 },
|
||
{ width: [400, 500], compName: 'comp4', height: 410, background: 'blue', x: 0, y: 500 },
|
||
],
|
||
},
|
||
'[1921, 3860]': {
|
||
layoutStrategy: 'strategy4',
|
||
compStyles: [
|
||
{ width: [800, 1000], compName: 'comp1', height: 600, background: 'red', x: 0, y: 0 },
|
||
{ width: [500, 600], compName: 'comp2', height: 600, background: '#ccdc', x: [800, 1000], y: 0 },
|
||
{ width: [500, 600], compName: 'comp3', height: 1070, background: '#5E6673', x: [1300, 1600], y: 0 },
|
||
{ width: [500, 600], compName: 'comp4', height: 510, background: 'blue', x: 0, y: 600 },
|
||
],
|
||
},
|
||
};
|
||
|
||
function getComponentWidthRange(screenWidth: number): ScreenResolutionMap | null {
|
||
for (const resolutionRange in screenResolutionMap) {
|
||
const [minWidth, maxWidth] = JSON.parse(resolutionRange.replace('[', '[').replace(']', ']'));
|
||
if (screenWidth >= minWidth && screenWidth <= maxWidth) {
|
||
return screenResolutionMap[resolutionRange];
|
||
}
|
||
}
|
||
return null; // 如果没有找到对应的分辨率范围,则返回null
|
||
}
|
||
|
||
const getComponentWidthRange1 = (screenWidth: number, resolution: Resolution): ComponentWidthRange[] | null => {
|
||
for (const resolutionRange in resolution) {
|
||
const [minWidth, maxWidth] = JSON.parse(resolutionRange.replace('[', '[').replace(']', ']'));
|
||
if (screenWidth >= minWidth && screenWidth <= maxWidth) {
|
||
return resolution[resolutionRange];
|
||
}
|
||
}
|
||
return null; // 如果没有找到对应的分辨率范围,则返回null
|
||
};
|
||
|
||
const loadComponentWidthRange = (layoutStrategy: LayoutStrategy, screenWidth: number): ComponentWidthRange[] => {
|
||
let layoutStyles: ComponentWidthRange[] | null = [];
|
||
for (let item of screenResolutionMap1) {
|
||
console.log('layoutStrategy', item.layoutStrategy);
|
||
if (item.layoutStrategy === layoutStrategy) {
|
||
layoutStyles = getComponentWidthRange1(screenWidth, item.resolution);
|
||
break;
|
||
}
|
||
}
|
||
if (!layoutStyles || layoutStyles.length === 0) {
|
||
layoutStyles = getComponentWidthRange1(screenWidth, screenResolutionMap1[0].resolution) as ComponentWidthRange[];
|
||
}
|
||
return layoutStyles;
|
||
};
|
||
|
||
const setComponentStyleByStrategy = (compStyles: ComponentWidthRange[]) => {
|
||
components.value = compStyles.map((item) => {
|
||
const compPosition = getRelativePosition(item, screenWidth.value);
|
||
return {
|
||
id: item.compName,
|
||
comp: layoutCompMap[item.compName],
|
||
x: compPosition.x,
|
||
y: item.y,
|
||
width: compPosition.width,
|
||
height: item.height,
|
||
zIndex: 1,
|
||
background: item.background,
|
||
};
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 计算组件相对位置和大小的函数
|
||
* @param compStyles - 组件的样式信息
|
||
* @param screenWidth - 屏幕宽度
|
||
* @returns 组件的计算位置和大小
|
||
*/
|
||
function getRelativePosition(compStyles: ComponentWidthRange, screenWidth: number): { x: string; width: string } {
|
||
const { x, width } = compStyles;
|
||
|
||
// 解析区间值
|
||
const parseRange = (value: Coordinate): { min: number; max: number } => {
|
||
if (Array.isArray(value)) {
|
||
const [min, max] = value;
|
||
return { min, max };
|
||
}
|
||
return { min: value, max: value };
|
||
};
|
||
|
||
// 转换为基于屏幕宽度的百分比
|
||
const toPercentage = (
|
||
range: { min: number; max: number },
|
||
screenWidth: number,
|
||
): { minPercentage: number; maxPercentage: number } => {
|
||
const minPercentage = (range.min / screenWidth) * 100;
|
||
const maxPercentage = (range.max / screenWidth) * 100;
|
||
return { minPercentage, maxPercentage };
|
||
};
|
||
|
||
// 解析并计算宽度和x值
|
||
const widthRange = parseRange(width);
|
||
const xRange = parseRange(x);
|
||
|
||
const widthPercentage = toPercentage(widthRange, screenWidth);
|
||
const xPercentage = toPercentage(xRange, screenWidth);
|
||
|
||
// 假设我们取区间的中间值作为最终值
|
||
const finalWidth = ((widthPercentage.minPercentage + widthPercentage.maxPercentage) / 2).toFixed(2);
|
||
const finalX = ((xPercentage.minPercentage + xPercentage.maxPercentage) / 2).toFixed(2);
|
||
|
||
return {
|
||
x: finalX,
|
||
width: finalWidth,
|
||
};
|
||
}
|
||
|
||
onMounted(async () => {
|
||
if (container.value) {
|
||
containerWidth.value = container.value.clientWidth;
|
||
containerHeight.value = container.value.clientHeight;
|
||
}
|
||
});
|
||
|
||
const switchLayout = (layoutStrategy: LayoutStrategy) => {};
|
||
|
||
function getComponentStyle(component: ComponentState) {
|
||
return {
|
||
position: 'absolute',
|
||
left: `${component.x}%`,
|
||
top: `${component.y}px`,
|
||
width: `${component.width}%`,
|
||
height: `${component.height}px`,
|
||
zIndex: component.zIndex,
|
||
background: component.background,
|
||
transition: 'all 0.25s ease',
|
||
};
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
.layout-buttons {
|
||
width: 100%;
|
||
height: 5%;
|
||
}
|
||
.components-container {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 95%;
|
||
border: 1px solid #ccc;
|
||
overflow: hidden;
|
||
.component {
|
||
background-color: lightgray;
|
||
border: 1px solid #ccc;
|
||
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
|
||
transition: all 0.25s ease;
|
||
font-size: 24px;
|
||
color: #fff;
|
||
}
|
||
}
|
||
}
|
||
</style>
|