用SVG實現一個優雅的提示框

前言

Tooltips常被稱爲提示框(或信息提示框),提示框可以以較強的交互性、自由度爲用戶提供相應的提示信息。今天咱們要聊的不是如何實現強大的交互行爲,而是來看看如何以最好的方式來還原他們的視覺效果,而且能適用於不一樣的場景。css

背景

上圖是從平時工做場景碰到的UI效果截圖過來的。上圖中展現的Tooltips框基本上覆蓋了常見的UI風格。簡單的來概括一下:html

  • 帶邊框的提示框
  • 純色(或帶透明度純色)的提示框
  • 帶內陰影(或外陰影)的提示框
  • 帶邊框+漸變的提示框
  • 帶邊框+透明度背景的提示框
  • 提示框三角帶圓角和陰影的提示框

可能還有我未碰到的提示框UI風格。面對這麼多的UI風格,對於前端實現上來講是具備必定的挑戰性,特別是多種效果組合在一塊兒的。好比說,帶有邊框+內外陰影+漸變(或透明度)+圓角三角等。基本上組合了上圖所提到的各類UI風格。前端

clip-path方案

一般上圖的實現是使用CSS畫個尖角來拼接上去,比較優秀的方案以下圖:css3

咱們簡單介紹下clip-path方案:canvas

把提示框分紅兩個部分,一個是四方形,一個是三角形,而後兩個拼接在一塊兒組合成一個提示框。這樣整個座標示意圖以下:markdown

假設提示框的尺寸是w x h,邊框厚度是h1,那麼繪製帶有缺口的時須要如下幾個座標點:app

``svg

  • d1座標(0, 0)
  • d2座標((50% - b), 0)((w / 2 - b), 0) 其中b是三角形對角邊長度的一半,後面會介紹到
  • d3座標((50% - b - h1), h1)((w / 2 - b - h1), h1)
  • d4座標((50% + b + h1), h1)((w / 2 + b + h1), h1)
  • d5座標((50% + b), 0)((w / 2 + b), 0)
  • d6座標(100%, 0)(w, 0)
  • d7座標(100%, 100%)(w, h)
  • d8座標(0, 100%)(0, h)

``wordpress

座標點放置到clip-pathpolygon()函數中,最終剪切以後的圖形看上去像下圖函數

clip-path: polygon(    0 0,    calc(50% - 4px) 0,    calc(50% - 7px) 2px,    calc(50% + 7px) 2px,     calc(50% + 4px) 0,     100% 0,     100% 100%,      0 100%,    0 0);
複製代碼

另外就是三角形的部分,若是咱們的三角形是一個 10px x 10px 旋轉 45deg 獲得。根據一些三角函數的公式和已知的正方形邊長就能夠計算出正方形斜對角的長度:

clip-path方案碰到問題

這個效果總體看起來仍是不錯的,可是細看就會發如今接縫處或許會存在有空隙、有重疊的問題,以下圖:

採用vw方案後這類像素對不齊的問題也算是司空見慣了,同時第一個Tooltips因爲是背景須要從左到右漸變的,此時尖角的漸變過分要和下方的漸變匹配上就更須要費力氣了。

因爲先前就遇到過此類ToolTip樣式問題,告知視覺同窗後,體貼的視覺同窗修改了一版不帶透明度的純色提示框,然而視覺效果大打折扣。

其實咱們對於原先採用CSS clip-path的方案其實也存在不少的缺陷,它在面對帶有陰影、背景透明或者漸變、帶邊框同時出現時就顯出了實現成本高和效果通常的缺點。

SVG 方案

在討論中咱們想到 SVG的path 和這個提示框的樣式自然的匹配(建議先了解下path的相關文檔),查閱了相關的文檔和資料後咱們大體獲得了使用SVG來實現的以下幾個優勢:

  • 能輕鬆知足陰影、背景透明或者漸變、帶邊框的效果,甚至更爲複雜多變的場景
  • SVG的path實現簡單,而且代碼量極小
  • 可擴展性,可維護性

參考相關文章後,咱們完善Demo工具以下:

使用Demo工具,咱們會獲得path的數據大體以下:

M 0,0 L -15,-15 H -79 Q -84,-15 -84,-20 V -85 Q -84,-90-79,-90 H 61 Q 66,-90 66,-85 V -20 Q 66,-15 61,-15 H 15 z
複製代碼

一般使用SVG畫path時用到命令以下表:

貝塞爾曲線

在SVG path 命令中我我的認爲最精髓的部分是貝塞爾曲線,貝塞爾能畫出各類使人愉悅的曲線。

貝塞爾曲線徹底由其控制點決定其形狀, n個控制點對應着n-1階的貝塞爾曲線,而且能夠**經過遞歸的方式來繪製。**咱們先看下一次和二次貝塞爾曲線如何來繪製的:

一次曲線:

一條直線上,隨着時間t的變化,紅色線段的那個點的座標公式應該以下:

二次貝塞爾曲線:

p0、p一、p2是3個不共線的點,依次用線段鏈接,此時隨意取線段p0p1上的一個點p0' , 如上圖: 咱們的p0'點在p0p1線段的0.26處(t=0.26),此刻p1p2線段相同比列取p1'點,此時p0'p1'鏈接後造成線段p0'p1', 在按照如上比列進行取值 p0'', 這時候就肯定了二次貝塞爾曲線的一個點。

經過一番巴拉巴拉牛逼的推導後,二次貝塞爾曲線公式爲:

N次貝塞爾能夠認爲是如上取值方式的迭代過程,能夠經過下圖直觀的感覺到1~4次曲線隨着時間t的變化過程,具體N次貝塞爾曲線的公式能夠參考下方關於曲線的文章

SVG中的Q命令

回到咱們的ToolTips 話題, 其中的圓角是能夠經過二次貝塞爾曲線來實現,SVG中 Q 命令就是來實現二次貝塞爾曲線的,SVG中 Q 命令的示例圖以下:

對應的指令,其中x1,y1就是咱們上面提到的p1點:

Q x1 y1, x y
複製代碼

二次貝塞爾曲線 Q 示例以下:

<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">   <path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/></svg>
複製代碼

經過設置起始點和調整控制點p1 咱們能獲得咱們想要的圓角,以下圖所示,小圓點爲咱們的p1控制點

樣式設置

實現了上方的SVG後接下來的透明、背景漸變、陰影、邊框的設置就都不成問題了。

背景透明

path {  fill: rgba(0,0,0, .3);  storke: #ffffff;  storke-width: 1px}
複製代碼

陰影

svg {  filter:drop-shadow(2px 4px 6px black)}
複製代碼

關於爲什麼使用drop-shadow來實現陰影,能夠看下圖使用了box-shadowdrop-shadow效果區別,

使用box-shadow的時候咱們的尖角部分沒有陰影,氣泡框部分是有陰影的,就會出現下圖所示的狀況,而使用drop-shadow 就能符合咱們尖角和睦泡框都有陰影的要求。

背景漸變

SVG不只支持簡單的填充,還支持線性漸變和徑向漸變以及圖形紋理等。爲了讓漸變能被重複使用,漸變內容須要定義在標籤內部。

以下圖是徑向漸變的演示:

<svg width="120" height="240" version="1.1" xmlns="http://www.w3.org/2000/svg">  <defs>      <linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">        <stop offset="0%" stop-color="red"/>        <stop offset="50%" stop-color="black" stop-opacity="0"/>        <stop offset="100%" stop-color="blue"/>      </linearGradient>  </defs>  <rect x="10" y="120" rx="15" ry="15" width="100" height="100" fill="url(#Gradient2)"/>  </svg>
複製代碼

將這個漸變做用到咱們提示框後能夠看到以下圖的效果,終於不用辛辛苦苦的處理尖角的漸變銜接問題了。

更多

SVG同時也支持紋理疊加效果,具體感興趣的能夠自行去研究下。

需求還沒完

上面方案落地到項目中後,多是咱們不經意感動了設計師,最近的需求視覺稿中咱們發現其中涉及到的Tooltips樣式已經愈加使人驚豔。簡單列舉以下兩個樣式:

初版方案咱們基於Demo工具演示咱們已經產出了ToolTips的SDK, 咱們使用的單個參數arrowHeight傳入來生成尖角。在應付上方兩個樣式是不可能的,尖角樣式多變,如何來擴展性和易用性成爲了一個問題,不可能多變的尖角樣式都開發一個SDK。

方案改進

要應付多變的氣泡尖角必定要想辦法把尖角抽離出原先的氣泡外層路徑,生成尖角路徑後在整合到氣泡上造成一個完整的閉合路徑。

爲了簡單處理數值,我將原先的尖角 (0,0) 座標定義更換到下方圖示點:

因此接下來尖角能夠自由設計了,只要保證從**(0,0)**出發最後回到**(-arrowWidth,0)**就好了,以下是一個尖角的路徑:(M 0 0 C -10 0 -8 5 -12 5 S -14 0 -24 0

經過設計不一樣的尖角路徑咱們就能組合成不一樣的氣泡樣式:

上方右側的尖角氣泡最終給出的路徑字符串以下,其中Q -2 7 -9 10 Q -6 5 -7 0 這一段即爲咱們的尖角路徑:

M 0 0 Q -2 7 -9 10 Q -6 5 -7 0H -110Q -116,0 -116,-6V -56Q -116,-62 -110,-62H 101Q 107,-62 107,-56V -6Q 107,0 101,0H 0 z

從上方簡短的路徑能看出,咱們的尖角路徑是完整的整合在整個SVG氣泡路徑中的,因此就不會擔憂會出現CSS的 clip-path 方案的問題。
複製代碼

可視化工具

方案看起來好像已經搞定了需求中的尖角樣式,然而你可能會說這尖角路徑是如何產生,難道須要經過強大的數學能力推導出來?以下三次貝塞爾曲線就已經不敢直視了,更況且四次、五次...

因此想配合的咱們必定要產出可視化工具來實現這路徑生成過程,得益於D3.js工具庫操做SVG方面的強大功能,咱們開發完的 生成工具地址 (market.m.taobao.com/app/fdilab/…) 以下:

對於熟悉SVG的 path命令的同窗來講這個操做不難,若是不熟悉的推薦看下下方的參考文章,瞭解了曲線命令後就能畫出圓滑的曲線。

總結

至此在ToolTips這塊基本已經知足了設計的需求,同時也沉澱了SVG路徑生成工具。使用SVG來實現ToolTips能覆蓋 CSS clip-path不能完美解決的幾個場景。在此特別感謝大漠老師的指導。

參考文章

相關文章
相關標籤/搜索