仿制浏览器UI实现《外圆角Tab》

357 阅读1分钟

这几天老板丢出一个想要的效果来:

image.png

于是UI领略了老板的意图, 做出这样的样式

image.png 呵, 仰天大笑出门去, 我辈岂是蓬蒿人。该功能使用 react + styled-components 完成

相对于浏览器的 tab 页, 这个更难点:

浏览器tab的难点:外圆角的实现

我们tab实现的难点在于:

  • 每一个 tab 的颜色不同, 且偶数会压着基数《不是单纯的颜色区分》, 但是基数会填充偶数tab背景
  • 我们的 tab 有一个边框线隔离

分析

  1. 先实现外圆角《radial-gradient《镜像渐变实现外圆》, 伪元素(::after, ::before), 三部分进行拼接》
  2. 画出外圆角 border《长方形上圆角弧度 + 圆形剪切, 三部分拼接实现border》
  3. 并排《存有空隙》
  4. 找出规律, 使用定位完成《利用 z-index , width, left 实现》

实现


// 这是一个获取随机颜色的算法
const getTeamColor:() => string = () => '#f00'

<WorkFolderHeader>
  { getTeamSort().map((team, index) => (
    <OutborderBox index={index} color={getTeamColor()}>
            <div className="left"></div>
            <div className="team-name">{team.name}</div>
            <div className="right"></div>
    </OutborderBox>
  ))}

</WorkFolderHeader>

export const WIDTH = 314;

export const WorkFolderHeader = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  height: 45px;
  border-bottom: 1px solid #f0f0f0;
  position: relative;
`;

export const OutborderBox = styled.div<{ index: number; color?: string }>`
  position: absolute;
  top: 0;
  height: 100%;
  line-height: 45px;
  background-color: ${({ color }) => color};
  box-sizing: border-box;

  :nth-child(2n + 1) {
    left: ${({ index }) => index * WIDTH - 10}px;
    min-width: ${WIDTH + 20}px;
    max-width: ${WIDTH + 20}px;
    z-index: 0;
    border-radius: 0;

    > .team-name {
       padding: 0 20px;
      }
    }

    :nth-child(2n) {
      left: ${({ index }) => index * WIDTH}px;
      min-width: ${WIDTH}px;
      max-width: ${WIDTH}px;
      z-index: 1;
      border-radius: 10px 10px 0 0;

      > .team-name {
        padding: 0 10px;
      }
    }

    > .team-name {
        width: 100%;
        word-break: keep-all;
        overflow: hidden;
        text-overflow: ellipsis;

        ::before {
          content: '';
          position: absolute;
          left: -10px;
          bottom: 0;
          width: 10px;
          height: 10px;
          background: ${({ color }) =>
                      'radial-gradient(circle at 0% 0%, transparent 10px, ' + color + ' 0)'};
        }

        ::after {
          content: '';
          position: absolute;
          right: -10px;
          bottom: 0;
          width: 10px;
          height: 10px;
          background: ${({ color }) =>
                      'radial-gradient(circle at 100% 0%, transparent 10px, ' + color + ' 0)'};
            }
    }

    > .left,
    > .right {
        position: absolute;
        bottom: 0px;
        border-radius: 50%;

        width: 20px;
        height: 20px;
        border: 1px solid #f0f0f0;
        z-index: 3;
    }

    > .left {
        left: -20px;
        clip-path: circle(50% at 100% 100%);
    }

    > .right {
        right: -20px;
        clip-path: circle(50% at 0% 100%);
    }

    :not(:first-child) {
        border-left: 1px solid #f0f0f0;
        border-right: 1px solid #f0f0f0;
    }

    :last-child {
        min-width: ${WIDTH + 10}px;
        max-width: ${WIDTH + 10}px;
        overflow: hidden;
    }
`;