乐趣区

关于css:CSS-实现支持渐变的提示框tooltips

明天来看一种非常常见的交互:提示框(tooltips)。通常提示框都是纯色的,比方上面这个

这类布局实现还不算简单,能够用一个圆角矩形和一个小三角拼接造成,设置雷同的色彩就能够了

这个并不是本文的重点,有趣味的能够拜访 css-tips (codepen.io)

有时候,为了突出强调产品的特点或者为了追随设计的潮流,设计会用上突变背景,比方 lulu UI Edge 版本中的 Tips 组件

如果依然采纳 “拼接” 的形式,不可避免会呈现连接不上的问题,有显著的 “割裂” 感,视觉还原会大打折扣

那么,如何实现这类成果呢?一起来看看吧

一、clip-path 裁剪

clip-path 可能是很多人马上就能想到的形式。然而实际操作下来,还是会遇到很多麻烦

  1. clip-path: path 能够反对任意形态,然而却没法实现自适应宽高
  2. clip-path: polygon 能够实现小尖角,然而无奈实现圆角
  3. clip-path: inset 能够实现自适应圆角矩形,然而无奈实现下方的小尖角

如何解决这个问题呢?其实把 2 和 3 联合起来就能够了

这里须要两个雷同大小的容器,能够用 ::before::after 来代替,而后设置雷同的背景色,能够通过自定义属性定义

.tips{
   position: relative;
   --bg: linear-gradient(45deg, #ff3c41, #ff9800);
}
.tips::before,.tips::after{
  content:'';
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  background: var(--bg);/* 完全相同的背景 */
  z-index: -1;
}

为什么要用两个雷同大小的容器呢? 这是为了保障接下来突变背景在裁剪时齐全吻合

接着其中一个裁剪成圆角矩形,另外一个裁剪成小三角,而后 重叠 起来就能够了

.tips::before{clip-path: inset(0 0 5px 0 round 5px);
  /*round 能够设置圆角 */
}
.tips::after{clip-path: polygon(calc(50% - 5px) calc(100% - 5px), calc(50% + 5px) calc(100% - 5px), 50% 100%);
  /* 实现小三角,只须要 3 个点的坐标就能够了 */
}

能够看到提示框齐全是自适应的,实时成果如下

残缺代码可拜访 tooltips-clip-path (codepen.io)

二、mask 遮罩

除了 clip-pathmask 也是一种思路。如果还不相熟 mask,能够参考这一篇 微妙的 CSS MASK (juejin.im)。这里的原理如下

利用 mask,当初的问题就转变成了:如何通过 CSS 绘制这样一个图形?

1. 万能的 gradient

没有什么图形是 CSS 突变 绘制不进去的,这个也不例外。首先咱们把这个图形进行合成,这里能够分成一个 圆角矩形 和一个 三角形,三角形比拟容易,能够通过 conic-gradient 或者 linear-gradient 绘制

圆角矩形就略微有点麻烦了,不过还是能够合成的,如下

能够由 4 个径向突变和 2 个线性突变合成,用代码实现就是

tips{
  -webkit-mask-image: 
    /* 4 个径向突变和 2 个线性突变 */
    radial-gradient(circle at 5px 5px, green 5px,transparent 0),
    radial-gradient(circle at 5px 5px, green 5px,transparent 0),
    radial-gradient(circle at 5px 5px, green 5px,transparent 0),
    radial-gradient(circle at 5px 5px, green 5px,transparent 0),
    linear-gradient(red,red),
    linear-gradient(blue,blue);
  -webkit-mask-size:
    10px 10px,
      10px 10px,
    10px 10px,
    10px 10px,
    100% calc(100% - 15px),
    calc(100% - 10px) calc(100% - 5px)
  -webkit-mask-position:
    left top,
    right top,left 0 bottom 5px,
    right 0 bottom 5px,
    left 5px,
    5px top;
  -webkit-mask-repeat: no-repeat
}

只有有点急躁,都能够很顺利的写进去

然而 …

太长了,有很多反复的(4 个 radial-gradient),十分啰嗦,有没有什么方法优化呢?这里有一个技巧,碰到反复有法则的货色,能够多想想 repeat,利用背景的平铺个性,正当设置背景尺寸就能够了,如下

能够看到,背景尺寸设置成 calc(100% – 10px) 就能够达到平铺成果,代码实现就是

tips{
  -webkit-mask-image: 
    /* 只须要一个径向突变即可 */
    radial-gradient(circle at 5px 5px, green 5px,transparent 0),
    linear-gradient(red,red),
    linear-gradient(blue,blue);
  -webkit-mask-size:
    calc(100% - 10px) calc(100% - 15px),/* 圆角的尺寸,高度因为还须要减去三角形尺寸,所以多了 5px*/
    100% calc(100% - 15px),
    calc(100% - 10px) calc(100% - 5px);
  -webkit-mask-position:
    left top,
    left 5px,
    5px top;
  -webkit-mask-repeat: repeat,no-repeat,no-repeat;
}

是不是精简了许多?而后再把三角形的合过去就行了,能够失去如下成果

残缺代码可拜访 tooltips-mask-gradient (codepen.io)

2. 自适应的 svg

只管做了一些优化,下面的代码量依然十分可观,有没有更加简便的形式呢?

想到了 svg…

个别状况下,svg 门路是固定尺寸的,只能 等比缩放,无奈做到自适应。不过根本图形是反对自适应的,能够设置百分比尺寸,比方 <rect>

<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'>
   <rect rx="5" width='100%' height='100%'/>
</svg>

rx 能够设置矩形的圆角,当不设置 ry 时,默认与 rx 雷同

这样一个 svg 是能够自适应的,在扭转尺寸的状况下不会变形(留神察看圆角),如下

三角形就很容易了,能够用 <polygon> 实现

<svg xmlns='http://www.w3.org/2000/svg'>
  <polygon points='0 0,10 0,5 5' />
</svg>

而后,把两段 svg 间接用作遮罩背景就行了,能够用 mask-size 和 mask-position 别离设置 尺寸 地位

tips{-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><polygon points='0 0,10 0,5 5'/></svg>"),url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect rx='6'width='100%'height='100%'/></svg>");
  -webkit-mask-size: 10px 5px, 100% calc(100% - 5px);
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center bottom, 0 0;
}

svg 用作背景须要在后面增加 data:image/svg+xml,并且内容须要本义,具体可参考这篇文章:学习了,CSS 中内联 SVG 图片有比 Base64 更好的模式

还是挺不错的,代码量也不多,也比拟容易了解,实时成果如下

残缺代码可拜访 tooltips-mask-svg (codepen.io)

三、paint 绘制

再来介绍一种将来的解决形式,CSS paint。对于 CSS paint,又称“CSS 界的绘图板”,简略来说,就是用 canvas 绘图的形式来绘制背景,canvas 简直什么都能绘制吧,所以这是一种更为通用的解决方案。想疾速理解 CSS paint 的能够参考这一篇入门文章:CSS 届的绘图板 CSS Paint API 简介,不过目前仅反对 Chrome,兼容性如下

不过并不影响咱们的学习,毕竟是将来的解决方案,先看看大抵的语法,如下

  1. 首先,JS 注册模块 registerPaint
// paint-tips.js
registerPaint('tips-bg', class {paint(ctx, size, properties) {// 在这里绘制背景,语法和 canvas 相似}
});
  1. 接着,JS 增加模块 CSS.paintWorklet.addModule
if (window.CSS) {CSS.paintWorklet.addModule('paint-tips.js');
}
  1. 最初,CSS 中应用 paint(tips-bg)
tips{-webkit-mask-image: paint(tips-bg); /* 这里作为遮罩背景应用 */
}

上面就来绘制提示框了,如果依然借助 mask,那么问题就变成了:如何通过 canvas 绘制这样一个图形?

在 canvas 中,绝对于 CSS 来说,这类图形几乎就是小儿科,只须要应用 lineToarc 两个指令就能够绘制了。最要害的一点是,这里的尺寸是实时渲染的,能够通过 size 来获取

对于 canvas 学习,这里举荐一下张鑫旭老师的 Canvas API 中文文档,api 和 案例比 mdn 文档清晰太多

绘制代码如下(上面就是很一般的 canvas 代码了,其实就是几段线段连接起来,而后填充纯色)

registerPaint('tips-bg', class {paint(ctx, size) { // ctx 为绘制上下文,size 为容器尺寸
      const {width,height} = size; // 容器尺寸
      const radius = 5; // 圆角大小
      const deg = Math.PI / 2;
      const edge = 5; // 三角形大小
      const pos = width / 2; // 三角形地位
      ctx.beginPath();
      ctx.moveTo(radius,0);
      ctx.lineTo(width-2*radius,0);
      ctx.arc(width-radius,radius,radius,-deg,0);
      ctx.lineTo(width,height-2*radius-edge);
      ctx.arc(width-radius,height-radius-edge,radius,0,deg);
      ctx.lineTo(pos+edge,height-edge);
      ctx.lineTo(pos,height);
      ctx.lineTo(pos-edge,height-edge);
      ctx.lineTo(radius,height-edge);
      ctx.arc(radius,height-radius-edge,radius,deg,2*deg);
      ctx.lineTo(0,radius-edge);
      ctx.arc(radius,radius,radius,-2*deg,-deg);
      ctx.closePath();
      ctx.fillStyle = '#000';
      ctx.fill();}
});

实时成果如下

残缺代码可拜访 tooltips-mask-paint (codepen.io)

另外,也能够通过 CSS 变量进行自定义,比方定义一个 --r 为圆角大小,--t为三角形大小

<tips style="--r:5;--t:5"></tips>
registerPaint('tips-bg', class {static get inputProperties() { // 定义容许的自定义属性
    return [
      '--r',
      '--t'
    ]
  }
  paint(ctx, size, properties) { // properties 为自定义属性
    const radius = Number(properties.get('--r'));
    const edge = Number(properties.get('--t'));
    // ...
  }
})

能够看到绘制是实时更新的(扭转圆角),无需 JS 额定解决,实时成果如下

残缺代码可拜访 tooltips-mask-paint-var (codepen.io)

四、描边成果

有时候提示框可能还会有描边的成果,比方这样的

这类带描边的其实以上形式都不太实用,clip-pathmask 都无奈实现描边,不过有一个边框生成计划能够参考:有意思!不规则边框的生成计划 (juejin.cn),惋惜成果不是特地完满(稍微含糊)

如果尺寸固定,那么能够间接应用 svg 形式,参考这篇文章:用 SVG 实现一个优雅的提示框 (juejin.cn)

就目前而言,的确没有比拟好的实现计划(有更好的实现形式欢送补充😂,我临时想不进去了),不过如果借助 CSS paint,那所有就都有可能了!只须要在 paint 函数中绘制边框和背景就行了

绘制代码如下

registerPaint('tips-bg', class {paint(ctx, size) {const { width,height} = size; // 容器尺寸
      const radius = 5; // 圆角大小
      const deg = Math.PI / 2;
      const edge = 5; // 三角形大小
      const pos = width / 2; // 三角形地位
      const lineWidth = 2; // 描边宽度
      ctx.beginPath();
      ctx.moveTo(radius+lineWidth,lineWidth);
      ctx.lineTo(width-2*radius-lineWidth,lineWidth);
      ctx.arc(width-radius-lineWidth,radius+lineWidth,radius,-deg,0);
      ctx.lineTo(width-lineWidth,height-2*radius-edge-lineWidth);
      ctx.arc(width-radius-lineWidth,height-radius-edge-lineWidth,radius,0,deg);
      ctx.lineTo(pos+edge,height-edge-lineWidth);
      ctx.lineTo(pos,height-lineWidth);
      ctx.lineTo(pos-edge,height-edge-lineWidth);
      ctx.lineTo(radius+lineWidth,height-edge-lineWidth);
      ctx.arc(radius+lineWidth,height-radius-edge-lineWidth,radius,deg,2*deg);
      ctx.lineTo(lineWidth,radius+lineWidth);
      ctx.arc(radius+lineWidth,radius+lineWidth,radius,-2*deg,-deg);
      ctx.closePath();
      const gradient = ctx.createLinearGradient(0, 0, width, 0); // 突变背景
      gradient.addColorStop(0, '#F57853');
      gradient.addColorStop(1, '#F8B578');
      ctx.fillStyle = gradient; 
      ctx.fill();
      ctx.strokeStyle = '#FBF8F8'; // 绘制边框
      ctx.lineWidth = lineWidth;
      ctx.lineCap = 'round';
      ctx.stroke();}
});
tips{/* -webkit-mask-image: paint(tips-bg); */
  background: paint(tips-bg); /* 不再借助 mask,纯 js 绘制背景,包含突变 */
}

实时成果如下

残缺代码可拜访 tooltips-paint-stroke (codepen.io)

五、总结和阐明

以上针对 tooltips 布局 共介绍了 3 种不同类型的实现形式,别离是 clip-pathmaskCSS paint。其中 mask 的实现重点其实是 CSS 图形的绘制,次要有 突变 svg 两种,尽管 突变 的写法略微简单一点,然而最为通用,其余形式可能换一种布局就不实用了。当初总结一下要点:

  1. 能够用多个容器重叠配合 clip-path 实现简单的自适应成果
  2. 在应用 CSS 突变绘制图形时,雷同的形态充分利用平铺个性
  3. svg 根本形态反对百分比尺寸,用作背景同样无效,能够应用多张背景来组合
  4. CSS paint 是将来的最佳解决形式,也能轻易的实现描边成果
  5. CSS paint 惟一的缺点是兼容性不够好(现仅反对 Chrome 65+),然而值得学习

当然,这些形式不仅仅是实现本文的布局而已,更多的是提供一种思路,下次碰到其余的 “异形布局” 也能马上联想出相应的解决方案,而不是抉择“切图.png”。如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤

退出移动版