今天來看一種十分常見的交互:提示框(tooltips)。一般提示框都是純色的,好比下面這個css
這類佈局實現還不算複雜,能夠用一個圓角矩形和一個小三角拼接造成,設置相同的顏色就能夠了html
這個並非本文的重點,有興趣的能夠訪問 css-tips (codepen.io)web
有時候,爲了突出強調產品的特色或者爲了跟隨設計的潮流,設計會用上漸變背景,好比 lulu UI Edge 版本中的 Tips 組件canvas
若是仍然採用「拼接」的方式,不可避免會出現銜接不上的問題,有明顯的「割裂」感,視覺還原會大打折扣api
那麼,如何實現這類效果呢?一塊兒來看看吧svg
clip-path 多是不少人立刻就能想到的方式。可是實際操做下來,仍是會遇到不少麻煩wordpress
如何解決這個問題呢?其實把 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; }
爲何要用兩個相同大小的容器呢? 這是爲了保證接下來漸變背景在裁剪時徹底吻合post
接着其中一個裁剪成圓角矩形,另一個裁剪成小三角,而後重疊起來就能夠了
.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)
除了 clip-path ,mask 也是一種思路。若是還不熟悉 mask,能夠參考這一篇 奇妙的 CSS MASK (juejin.im) 。這裏的原理以下
利用 mask ,如今的問題就轉變成了:如何經過 CSS 繪製這樣一個圖形?
沒有什麼圖形是 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)
儘管作了一些優化,上面的代碼量仍然很是可觀,有沒有更加簡便的方式呢?
想到了 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)
再來介紹一種將來的解決方式, CSS paint 。關於 CSS paint,又稱 「CSS 界的繪圖板」,簡單來講,就是用 canvas 繪圖的方式來繪製背景,canvas 幾乎什麼都能繪製吧,因此這是一種更爲通用的解決方案。想快速瞭解 CSS paint 的能夠參考這一篇入門文章:CSS屆的繪圖板CSS Paint API簡介,不過目前僅支持 Chrome,兼容性以下
不過並不影響咱們的學習,畢竟是將來的解決方案,先看看大體的語法,以下
// paint-tips.js registerPaint('tips-bg', class { paint(ctx, size, properties) { // 在這裏繪製背景,語法和canvas相似 } });
if (window.CSS) { CSS.paintWorklet.addModule('paint-tips.js'); }
tips{ -webkit-mask-image: paint(tips-bg); /*這裏做爲遮罩背景使用*/ }
下面就來繪製提示框了,若是仍然藉助 mask ,那麼問題就變成了:如何經過 canvas 繪製這樣一個圖形?
在 canvas 中,相對於 CSS 來講, 這類圖形簡直就是小兒科,只須要使用 lineTo 和 arc 兩個指令就能夠繪製了。最關鍵的一點是,這裏的尺寸是實時渲染的,能夠經過 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-path 和 mask 都沒法實現描邊,不過有一個邊框生成方案能夠參考:有意思!不規則邊框的生成方案 (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-path、mask、CSS paint。其中 mask 的實現重點實際上是CSS圖形的繪製,主要有 漸變 和 svg 兩種,雖然 漸變 的寫法稍微複雜一點,可是最爲通用,其餘方式可能換一種佈局就不適用了。如今總結一下要點:
固然,這些方式不只僅是實現本文的佈局而已,更多的是提供一種思路,下次碰到其餘的「異形佈局」也能立刻聯想出相應的解決方案,而不是選擇「切圖.png」。若是以爲還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤