CSS 实现一个带弧边的区域
一、需求描述
在移动端的开发中,经常遇到下图这样的弧边设计
弧边其实有很多方案,但像上图这样的情况更适合用 border 实现,因为这样更方便封装为组件
二、初步实现
可以将页面顶部的区域看做一个大圆与页面的交集,基于这个思路就能开发出一个基本的 html 结构
<body>
<div className='arc-wrapper'>
<div className='arc-wrapper__inner'>
<h1>Hello World</h1>
</div>
</div>
<div className="content">
<h1>Best Title</h1>
<p>lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</body>
然后完善 CSS,有以下重点:
1. 外层容器通过 border 实现圆形区域,并使用 overflow: hidden 隐藏超出区域的内容;
2. 外层容器需要实现水平居中
3. 内层容器需要使用页面宽度,不能继承父级宽度,并且和外容器底对齐
.arc-wrapper {
height : 2300px;
width : 2300px;
padding : 0;
border : none;
border-radius: 2300px;
margin-top : -1980px;
// 这一行很关键, 用于容器居中
transform : translateX(calc((100vw - 2300px) / 2));
position : relative;
overflow : hidden;
&::before {
content : '';
display : block;
position : absolute;
top : 0;
left : 0;
right : 0;
bottom : 0;
background : transparent;
border-radius: 2300px;
box-shadow : 0px -1px 20px 1px rgba(0, 0, 0, 0.1) inset;
z-index : 9;
}
&__inner {
background: linear-gradient(137deg, #f6c9b9 0%, #fff7f4 30.3%, #f2d9d0 58.96%);
position : absolute;
height : 200px;
width : 100vw;
left : 0;
bottom : 0;
right : 0;
margin : 0 auto;
}
}
这样一个带弧边的区域就完成了
但这样的实现很死板,无法灵活配置弧度和内容区的高度
而且外容器的水平居中用到复杂的 calc 计算
接下来就完善以上问题
三、组件封装
分析上文的 CSS 可以看出,核心数据有两个
1. 外容器的尺寸:影响决定弧边的弧度
2. 内容器的高度:用于确定外容器的 marginTop
所以组件可以这样设计:
type ArcWrapperProps = {
/** 弧度(圆的直径) */
size?: number;
/** 内层容器(内容区域)的高度 */
height?: number;
/** 内层容器的自定义 className */
className?: string;
/** 外层容器的自定义 className */
outerClassName?: string;
}
const ArcWrapper: React.FC<ArcWrapperProps> = (props) => {
const {
children,
size = 2300,
height = 200,
className,
outerClassName
} = props;
// 动态计算外层容器的样式
const style: React.CSSProperties = useMemo(() => {
// 计算水平居中所需的偏移量
const offest = (window.outerWidth - size) / 2;
return {
width: `${size}px`,
height: `${size}px`,
borderRadius: `${size}px`,
transform: `translateX(${offest}px)`,
marginTop: `-${size - height}px`,
}
}, [size, height]);
return (
<div
className={`arc-wrapper ${outerClassName || ''}`}
style={style}
>
<div
className={`arc-wrapper__inner ${className || ''}`}
style={{ height }}
>
{children}
</div>
</div>
)
};