这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战
背景
首先简单的看两张图片
总结一下玻璃拟态的风格:这种把阴影、透明度以及模糊背景结合到一起的UI设计思路,因为给人以玻璃的质感,被Michal Malewicz称为Glassmorphism(玻璃拟态)。
玻璃拟态的背景具有高强度的对比性,加上背景的模糊效果使得样式更加的好看。
vue 组件
首先,我们在 Components下面创建Glassmorphism的组件
<template>
<div class="Glassmorphism relative">
<slot />
</div>
</template>
组件由一个div包裹,然后将组件的内容中心交给slot进行分发。
于是我们对Glassmorphism样式进行处理。
首先我们定义组件的属性。将组件的宽高和圆角设置成可自定义。
const props = withDefaults(
defineProps<{
width?: string | number
height?: string | number
radius?: string | number
}>(),
{
width: '100%',
height: '100%',
radius: 12
}
)
然后我们对属性值进行一个初步的校验。
const c_width = computed(() => addUnit(props.width))
const c_height = computed(() => addUnit(props.height))
const c_radius = computed(() => addUnit(props.radius))
可以看到我们使用了addUnit进行的规则校验,我们看看addUnit怎么写的吧
export const isDef = (val: any) => val !== undefined && val !== null //是否有值
export const isNumeric = (val: any) => /^\d+(\.\d+)?$/.test(val) // 是否是一个数字
export const addUnit = (val: any) => { // 默认添加px的单位
if (!isDef(val)) {
return undefined
}
val = String(val)
return isNumeric(val) ? val + 'px' : val
}
主要由上面几个工具函数组成
于是我们对css进行开发。
<style lang="scss" scoped>
$circlePath: calc(v-bind(c_radius) * 2);
$radius: v-bind(c_radius);
.Glassmorphism {
width: v-bind(c_width);
height: v-bind(c_height);
border-radius: $radius;
background: linear-gradient(
to right bottom,
rgba(255, 255, 255, 0.6),
rgba(255, 255, 255, 0.1)
);
backdrop-filter: blur(6px);
box-shadow: 0.8rem 0.8rem 1.3rem 0 rgba(0, 0, 0, 0.15);
&::before,
&::after {
position: absolute;
content: "";
display: block;
z-index: 2;
border-radius: $radius;
}
&::before {
width: calc(100% - v-bind(c_radius) * 2);
height: 1px;
top: 0;
left: $radius;
background: linear-gradient(to right, #fff, transparent);
}
&::after {
width: 1px;
height: calc(100% - v-bind(c_radius) * 2);
top: $radius;
left: 0;
background: linear-gradient(to bottom, #fff, transparent);
}
}
</style>
这里其实我们可以看到,我们对容器的上,左边框进行的渐变处理,可是渐变遇上了圆角使得渐变并没有完全被处理掉。 于是我们有可以新增一个辅助dom进行样式微调
于是有:
<div class="Glassmorphism relative">
<div class="circle absolute top-0 left-0"></div> // 新增
<slot />
</div>
.circle {
width: $circlePath;
height: $circlePath;
border-radius: 50%;
border: 1px solid #fff;
clip: rect(0, $radius, $radius, 0);
z-index: -1;
}
我们使用clip进行裁剪,尺寸为radius的大小,这样就完美了。
附上源码:
<template>
<div class="Glassmorphism relative">
<div class="circle absolute top-0 left-0"></div>
<slot />
</div>
</template>
<script lang="ts" setup>
import { addUnit } from '@/utils'
const props = withDefaults(
defineProps<{
width?: string | number
height?: string | number
radius?: string | number
}>(),
{
width: '100%',
height: '100%',
radius: 12
}
)
const c_width = computed(() => addUnit(props.width))
const c_height = computed(() => addUnit(props.height))
const c_radius = computed(() => addUnit(props.radius))
</script>
<style lang="scss" scoped>
$circlePath: calc(v-bind(c_radius) * 2);
$radius: v-bind(c_radius);
.Glassmorphism {
width: v-bind(c_width);
height: v-bind(c_height);
border-radius: $radius;
background: linear-gradient(
to right bottom,
rgba(255, 255, 255, 0.6),
rgba(255, 255, 255, 0.1)
);
backdrop-filter: blur(6px);
box-shadow: 0.8rem 0.8rem 1.3rem 0 rgba(0, 0, 0, 0.15);
> .circle {
width: $circlePath;
height: $circlePath;
border-radius: 50%;
border: 1px solid #fff;
clip: rect(0, $radius, $radius, 0);
z-index: -1;
}
&::before,
&::after {
position: absolute;
content: "";
display: block;
z-index: 2;
border-radius: $radius;
}
&::before {
width: calc(100% - v-bind(c_radius) * 2);
height: 1px;
top: 0;
left: $radius;
background: linear-gradient(to right, #fff, transparent);
}
&::after {
width: 1px;
height: calc(100% - v-bind(c_radius) * 2);
top: $radius;
left: 0;
background: linear-gradient(to bottom, #fff, transparent);
}
}
</style>
大家可以自己copy过去,本地尝试,百分百有效,我这里就暂时不运行项目给大家展示了。
并且请注意使用:因为玻璃拟态具有高强度的颜色对比性。需将页面背景设置为暖色系的渐变色效果更佳哦。