子组件文件:clipPath.vue
<template>
<div class="bottom-box" :style="{width: state.width - state.borderRadius + 'px', height: state.height - state.borderRadius + 'px'}">
<div class="title" :style="{width: state.titleWidth - 20 + 'px', height: state.titleHeight - 20 + 'px'}">
<span class="title-text">{{ state.title }}</span>
</div>
<div class="content" :style="{width: state.width + 'px', height: state.height + 'px'}">
<div class="clip-box" :style="{clipPath: state.path}"></div>
</div>
<slot class="slot"></slot>
</div>
</template>
<script setup>
import { onMounted, reactive, watch } from "vue";
const props = defineProps({
width: {
type: Number,
default: 320,
},
height: {
type: Number,
default: 320,
},
borderRadius: {
type: Number,
default: 10,
},
titleWidth: {
type: Number,
default: 148,
},
titleHeight: {
type: Number,
default: 50,
},
title: {
type: String,
default: '',
}
});
const state = reactive({
path: `path('M148 0 H300 A10 10 0 0 1 310 10 V300 A10 10 90 0 1 300 310 H10 A10 10 0 0 1 0 300 V60 Q0 50, 20 50 H120 Q130 50, 132 40 L138 10 Q140 0, 148 0 Z')`,
width: props.width,
height: props.height,
borderRadius: props.borderRadius,
titleWidth: props.titleWidth,
titleHeight: props.titleHeight,
title: props.title,
});
onMounted(() => {
let width = state.width - state.borderRadius;
let height = state.height - state.borderRadius;
state.path = `path('M${state.titleWidth} 0 H${width} A${state.borderRadius} ${state.borderRadius} 0 0 1 ${state.width} ${state.borderRadius} V${height} A${state.borderRadius} ${state.borderRadius} 90 0 1 ${width} ${state.height} H${state.borderRadius} A${state.borderRadius} ${state.borderRadius} 0 0 1 0 ${height} V${state.titleHeight} Q0 ${state.titleHeight - 10}, 20 ${state.titleHeight - 10} H${state.titleWidth - 28} Q${state.titleWidth - 18} ${state.titleHeight - 10}, ${state.titleWidth - 16} ${state.titleHeight - 15} L${state.titleWidth - 10} 10 Q${state.titleWidth - 8} 0, ${state.titleWidth} 0 Z')`;
});
</script>
<style scoped>
.bottom-box {
position: absolute;
margin: 100px;
width: 300px;
height: 300px;
background-color: #e0ebfe;
position: absolute;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.5);
border-radius: 10px;
}
.content {
width: 320px;
height: 320px;
position: absolute;
top: -10px;
left: -10px;
filter: drop-shadow(0px 0px 3px rgba(0, 0, 0, 0.5));
}
.clip-box {
width: 100%;
height: 100%;
background: #3775ff;
/* clip-path: path(
"M148 0 H300 A10 10 0 0 1 310 10 V300 A10 10 90 0 1 300 310 H10 A10 10 0 0 1 0 300 V60 Q0 50, 20 50 H120 Q130 50, 132 40 L138 10 Q140 0, 148 0 Z"
); */
}
.title {
position: absolute;
background-color: transparent;
top: 0;
left: 0;
color: #3775ff;
display: flex;
align-items: center;
justify-content: center;
}
</style>
父组件应用:
<ClipPath width="500" height="400" title-width="120" title-height="60" title="标题">
<div class="ccontent" >自定义内容</div>
</ClipPath>
// ccontent 是自定义内容,具体位置由样式控制
.ccontent {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 100px;
background-color: bisque;
}