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>
  )
};

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章