最近项目有一个需求
这个项目类似大屏可视化,用到了不规则图片的div 背景,部分UI如图
遇到的问题
因为项目内容比较多,这种卡片的情况不同,宽高都不是固定的,所以直接把图片动态宽高的话会导致图片拉伸变形,根据每个宽高让UI老师切图也是不现实的,效率很低!
解决办法 - 做一个组件
- 首先找UI老师要一张这个背景图片 最高 和 最宽 的可能的背景图片,我要的是 960px * 960px 的,4个角固定,不随整体宽高做适配 dialog.jpg
2.新建 div 的四个角的子div,用 css 的 background-position
css 的 background-position 切出 四个角
background-position 用来设置背景图像起始位置的CSS样式属性
background-position 配合 最高 最宽的背景图片,可以使四个角的样式不变形
- 新建 TopLeft、TopRight、BottomRight、BottomLeft 四个角的div,分别定位到父div 的四个角
- 然后根据 background-position 分别从不同位置开始延伸宽高来拼凑样式,
height: calc(100% - 100px);
width: calc(100% - 100px);
延伸的顺序不能重了,要不会出现样式重叠的情况
错误的
正确代码
<template>
<div id="CustomDialog" :class="data.dialogClass" class="CustomDialog" :style="props.style">
<div v-if="data.dialogClass === 'upgradeClass'" class="background_style">
<div class="TopLeft"></div>
<div class="TopRight"></div>
<div class="BottomRight"></div>
<div class="BottomLeft"></div>
</div>
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, reactive, defineProps } from 'vue'
interface IProps {
style: {
width: string
height: string
}
}
const props = defineProps<IProps>()
const data = reactive({
dialogClass: '',
})
onMounted(() => {
let dom = document.getElementById('CustomDialog') as HTMLElement
let width = parseInt(dom.style.width.replace('px', ''))
let height = parseInt(dom.style.height.replace('px', ''))
if (width < 340 && height < 145) {
data.dialogClass = 'simpleClass'
} else {
data.dialogClass = 'upgradeClass'
}
})
</script>
<style lang="less" scoped>
.simpleClass {
background: rgba(6, 21, 43, 0.75) !important;
border: 1px solid #1fb8c5 !important;
box-shadow: 0px 2px 8px 0px rgba(161, 234, 240, 0.44) !important;
border-radius: 3px;
.content {
padding: 8px !important;
}
}
.CustomDialog {
position: relative;
.background_style {
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
.TopLeft {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: top left;
top: 0;
left: 0;
width: 100px;
height: 100px;
}
.TopRight {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: top right;
top: 0;
right: 0;
width: calc(100% - 100px);
height: calc(100% - 100px);
}
.BottomRight {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: bottom right;
bottom: 0;
right: 0;
width: calc(100% - 100px);
height: 100px;
}
.BottomLeft {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: bottom left;
bottom: 0;
left: 0;
width: 100px;
height: calc(100% - 100px);
}
}
.content {
color: #1ae7f8;
text-align: left;
padding: 16px;
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
}
}
</style>
3.使用组件
<template>
<div>
<CustomDialog :style="{ width: '340px', height: '145px' }">
<div>123</div>
</CustomDialog>
</div>
</template>
<script lang="ts" setup>
import { onMounted, reactive } from 'vue'
import CustomDialog from '/@/components/CustomDialog.vue'
const data = reactive({})
onMounted(() => {})
</script>
<style scoped></style>
- 上面我又多做了一个判断,如果宽高小于 340 和 145 ,也会导致 样式变形,340 和 145 是根据 右上角最小的宽度 + 左上角的宽度算出的,宽度也是如此
所以在 小于 指定宽高之后就取消背景图显示,给他用css 代码来实现简单的样式
.simpleClass {
background: rgba(6, 21, 43, 0.75) !important;
border: 1px solid #1fb8c5 !important;
box-shadow: 0px 2px 8px 0px rgba(161, 234, 240, 0.44) !important;
border-radius: 3px;
.content {
padding: 8px !important;
}
}
完整代码
<template>
<div>
<CustomDialog :style="{ width: '300px', height: '120px' }"><div>123</div> </CustomDialog>
</div>
</template>
<script lang="ts" setup>
import { onMounted, reactive } from 'vue'
import CustomDialog from '/@/components/CustomDialog.vue'
const data = reactive({})
onMounted(() => {})
</script>
<style scoped></style>
<template>
<div id="CustomDialog" :class="data.dialogClass" class="CustomDialog" :style="props.style">
<div v-if="data.dialogClass === 'upgradeClass'" class="background_style">
<div class="TopLeft"></div>
<div class="TopRight"></div>
<div class="BottomRight"></div>
<div class="BottomLeft"></div>
</div>
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, reactive, defineProps } from 'vue'
interface IProps {
style: {
width: string
height: string
}
}
const props = defineProps<IProps>()
const data = reactive({
dialogClass: 'upgradeClass',
})
onMounted(() => {
let dom = document.getElementById('CustomDialog') as HTMLElement
let width = parseInt(dom.style.width.replace('px', ''))
let height = parseInt(dom.style.height.replace('px', ''))
if (width < 340 && height < 145) {
data.dialogClass = 'simpleClass'
} else {
data.dialogClass = 'upgradeClass'
}
})
</script>
<style lang="less" scoped>
.simpleClass {
background: rgba(6, 21, 43, 0.75) !important;
border: 1px solid #1fb8c5 !important;
box-shadow: 0px 2px 8px 0px rgba(161, 234, 240, 0.44) !important;
border-radius: 3px;
.content {
padding: 8px !important;
}
}
.CustomDialog {
position: relative;
.background_style {
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
.TopLeft {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: top left;
top: 0;
left: 0;
width: 100px;
height: 100px;
}
.TopRight {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: top right;
top: 0;
right: 0;
width: calc(100% - 100px);
height: calc(100% - 100px);
//width: 300px;
//height: 100px;
}
.BottomRight {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: bottom right;
bottom: 0;
right: 0;
width: calc(100% - 100px);
height: 100px;
}
.BottomLeft {
background-image: url('/@/assets/dialog.jpg');
position: absolute;
background-position: bottom left;
bottom: 0;
left: 0;
width: 100px;
height: calc(100% - 100px);
//height: 100px;
}
}
.content {
color: #1ae7f8;
text-align: left;
padding: 16px;
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
}
}
</style>