缩放方案
最终选的是zoom缩放方案,参考这篇文章避坑,使用Hooks实现
JS内容:
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { DESIGN_HEIGHT, DESIGN_WIDTH } from '@/views/DataScreen/config/ScreenDisignSize';
export const useScreenAutoSize = () => {
const screenWidth = ref(window.innerWidth);
const screenHeight = ref(window.innerHeight);
onMounted(async () => {
window.addEventListener('resize', handleResize);
// 初始计算一次缩放比例
calculateZoomLevel();
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
});
// 监听窗口大小变化事件
function handleResize() {
console.log('handleResize', innerContainerStyle.value);
screenWidth.value = window.innerWidth;
screenHeight.value = window.innerHeight;
calculateZoomLevel();
}
const innerContainerStyle = ref({
width: `${DESIGN_WIDTH}px` // 中间容器的宽度固定为设计稿宽度
});
// 计算缩放比例
function calculateZoomLevel() {
const widthScale = screenWidth.value / DESIGN_WIDTH;
const heightScale = screenHeight.value / DESIGN_HEIGHT;
innerContainerStyle.value.zoom = Math.min(widthScale, heightScale);
}
return {
innerContainerStyle
};
};
模版内容:
<template>
<!-- outer-container 用来实现页面内容的水平和垂直居中 -->
<div class="outer-container">
<!-- inner-container 用来实现宽高等比例缩放 -->
<div class="inner-container" :style="innerContainerStyle">
<div class="content">
<!-- 在这里放置你的大屏内容 -->
<div class="body">
<a-row style="height: 100%" gutter="2">
<a-col :span="6">
<div class="left-content-box">
</div>
</a-col>
<a-col :span="12">
<!-- screen-detail-modal这个ID是ScreenDetailModalComponent组件中通过teleport挂载用 -->
<div id="screen-detail-modal"></div>
</a-col>
<a-col :span="6">
<div class="right-content-box">
</div>
</a-col>
</a-row>
</div>
</div>
</div>
</div>
</template>
CSS内容:
.outer-container {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
height: 100vh; /* 占满整个视口高度 */
background-color: rgba(0, 16, 22, 1); /* 页面两侧留白的背景色 */
.inner-container {
position: relative;
width: 2000px; /* 中间容器的宽度固定为设计稿宽度 */
height: 1119px; /* 中间容器的高度固定为设计稿高度 */
background-color: rgba(0, 16, 22, 1);
.content {
background-image: url('@/assets/data-screen/模型 背景.png');
height: 100%;
width: 100%;
.body {
margin: 16px 16px 0 16px;
height: 1000px;
display: grid;
#screen-detail-modal {
height: 100%;
width: 100%;
}
.right-content-box {
height: 100%;
display: grid;
grid-template-rows: 2fr 1fr 1fr;
}
.left-content-box {
height: 100%;
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr;
}
}
}
}
}
布局方案
因为大屏整体是左侧+中间Modal+右侧的布局,又大多数都是等分的块盒,使用场景对浏览器兼容性要求不高,且之前没有尝试过grid布局,所以选用grid布局。一用惊为天人- . -
参考这一篇就够了。顺便找到了这样一张图:
记录一个样式难点:
有两行元素,高度不定,我想实现这两行自适应。用grid布局试了很久才成功。
grid-template-rows: repeat(auto-fit, minmax(200px, min-content));
Vue3挂载到页面任意节点的支持
由于左侧和右侧点击之后都会出现包含不同内容的中间Modal,所以考虑把Modal挂载到布局的中间元素,而不是通过定位实现,看起来比较优雅。查了API,使用vue3的teleport:
中间的盒子加上ID,方便挂载:
<div id="screen-detail-modal"></div>
之后实现自定义组件:
view部分:
<template>
<teleport to="#screen-detail-modal" v-if="visibleProps">
<div class="screen-detail-modal-component-root">
<div class="shadow" @click="closeModal()"></div>
<span @click="closeModal()">关闭</span>
<div class="screen-detail-modal-body">
<div class="screen-detail-modal-content">
<slot></slot>
</div>
</div>
</div>
</teleport>
</template>
<script>
import { toRefs } from 'vue';
export default {
name: 'ScreenDetailModalComponent',
props: {
visible: {
type: Boolean,
required: false
}
},
setup(props, { emit }) {
const newProps = toRefs(props);
return {
closeModal: () => {
emit('update:visible', false);
},
visibleProps: newProps.visible
};
}
};
</script>
<style lang="scss" scoped src="./index.scss"></style>
CSS部分:
.screen-detail-modal-component-root {
padding: 24px 48px;
height: 100%;
.shadow {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: black;
z-index: 3;
opacity: 0.3;
}
.screen-detail-modal-body {
background-image: url('@/assets/data-screen/mid-bar-body@2x.png');
background-size: 100% 100%;
z-index: 3;
height: 100%;
position: relative;
background-color: rgb(0, 16, 22);
.screen-detail-modal-content {
position: absolute;
height: 100%;
width: 100%;
padding: 12px 24px;
}
}
}
使用:
这里有一个坑,要挂载的元素分为根元素外面/里面,如果在里面,需要保证要挂载的元素先加载,所以这里要注意一下引用方式,采用组件异步引用。
还有个坑,因为挂载到了其他元素,所以CSS的写法,要先选择中间元素的选择器,之后再在子层级写内容,不能按照代码的结构去写CSS。
View:
<ScreenDetailModalComponent v-model:visible="visible">
</ScreenDetailModalComponent>
const ScreenDetailModalComponent = defineAsyncComponent(() =>
import('@/views/DataScreen/components/ScreenDetailModalComponent/index.vue')
);
Hooks:
export default () => {
const visible = ref(false);
const detailData = ref({});
const showDetailModal = () => {
visible.value = true;
//加载详情弹窗数据
console.log('showDetailModal');
};
return {
showDetailModal,
visible,
detailData
};
};
CSS:
.screen-detail-modal-content {
.elderly-info-detail-content {
display: grid;
grid-template-rows: 2fr 3fr 4fr;
grid-row-gap: 48px;
height: 100%;
}
}
动态设置style的背景图片属性
view:
<div class="screen-card-component-root" :style="backgroundImageStyle">
JS:
const backgroundImageStyle = computed(() => {
return {
backgroundImage: `url(${require('你的图片地址')})`
};
});
return {
backgroundImageStyle
}
单独设置URL或者require是不生效的。
require效果等同于new URL(url, import .meta .url).href。
代码组织结构
最后!代码组织架构如下,个人觉得非常清晰,可能这就是React后遗症吧
其他收获:
- 不要沉迷细节优化,要先开发完大模块
- 多看文档,少走弯路