如何實現(xiàn)扇形,環(huán)形進度條和環(huán)形圖

本文主要提供了:

  1. svg實現(xiàn)可交互環(huán)形圖的流程(只提供整體流程,沒有全部代碼),如果你的項目需要環(huán)形圖,你又不想引入圖表類插件,建議可以手寫一下。
  2. 純css實現(xiàn)的靜態(tài)/動態(tài) 的 扇形/環(huán)形進度條,順手用相同的方式制作了一個特別沒用的環(huán)形圖。。。真的是特別沒用。順便一提,環(huán)形進度條是 頭條前端社招 的一道面試題。。。

svg繪制環(huán)形圖和一些需要注意的點

來看看一個簡易的環(huán)形圖demo!不知道怎么把鼠標(biāo)也錄進去,提醒的浮窗在鼠標(biāo)右上角6px的位置。。。大致就是這樣吧,蠻好玩的

環(huán)形圖demo

主要也就這么些東西

  1. 對應(yīng)的DOM結(jié)構(gòu)
  2. svg扇形繪制的方法
  3. 扇形繪制時候需要注意的點
  4. 數(shù)據(jù)展示的引導(dǎo)線和文案的繪制
  5. 數(shù)據(jù)引導(dǎo)線的防重疊避讓
  6. 文案的右對齊
  7. 鼠標(biāo)懸浮數(shù)據(jù)區(qū)域后的額外繪制和懸浮提示

第一步:對應(yīng)dom結(jié)構(gòu)
用的vue+jsx,大致內(nèi)容如下,循環(huán)體沒寫,所有內(nèi)容的id隨循環(huán)體的index變化即可,這里是單個環(huán)上數(shù)據(jù)塊的代碼,path用于畫扇形,polyline用于畫數(shù)據(jù)引導(dǎo)線,text是描述文本,分為兩塊,一個用來寫數(shù)據(jù)量,一個用于描述數(shù)據(jù)內(nèi)容。最后的circle用于把扇形遮擋成環(huán)形

<svg width="300px" height="223px">
    <path
        id="ring1"
        onMouseenter={this.larger.bind(this, xxx)}
        onMouseleave={this.smaller.bind(this, xxx)}
        onClick={this.sendMessage.bind(this, xx)}
        fill="#df3849"
    />
    <polyline
         id="line1"
        onMouseenter={this.larger.bind(this, xxx)}
        onMouseleave={this.smaller.bind(this, xxx)}
        style="fill:transparent;stroke:#df3849;stroke-width:1"
    />
    {chapterContent.task.un_submit && (<text
        id="text1"
        onMouseenter={this.larger.bind(this, xxx)}
        onMouseleave={this.smaller.bind(this, xxx)}
        onClick={this.sendMessage.bind(this, xx)}
    >
        <tspan class="svg-num" fill="#df3849" id="text11">
            {chapterContent.task.un_submit}
        </tspan>
        <tspan class="svg-word" id="text12">
            未提交
        </tspan>
    </text>

    <circle cx="150" cy="115" r="37" fill="#FFF" />
</svg>
)}

第二步:svg扇形的繪制
繪制扇形的核心就是定義<path>的路徑,代碼如下

let path = document.getElementById(pathId)
/** 
    數(shù)組內(nèi)參數(shù)對應(yīng)的內(nèi)容是,
    0~2的M代表moveTo,后面跟起點坐標(biāo);
    3~5的L代表lineto,后跟某個坐標(biāo),就是劃從起點到左邊點的線;
    6~11的A代表elliptical Arc,是畫橢圓弧操作,后跟兩個半徑,對應(yīng)橢圓的xr和yr;
        然后是x-axis-rotation,對應(yīng)X軸的旋轉(zhuǎn)角度
        接著是large-arc-flag,對應(yīng)你想要拿這兩個點畫一個鈍角弧還是銳角弧
        最后是sweep-flag,順逆時針畫弧,1代表從起點到終點弧線繞中心順時針方向,0代表逆時針方向,在這里我們使用順時針就是畫一個標(biāo)準(zhǔn)的扇形,逆時針。。。大致會畫出一個缺口的圓
    12~13是弧的終點坐標(biāo)
    14的Z是closepath代表閉合路徑,會把弧的重點和起點連起來形成一個圖形
**/
let descriptions = ["M", 0, 0, "L", x, y, "A", r, r, 0, 1, 1,  x1, y1, "Z"]
path.setAttribute("d", descriptions.join(" "))

展示下我自己寫的垃圾函數(shù),大致邏輯就是函數(shù)的參數(shù)為pathId,lineId,textId,或者這里可以換成一個統(tǒng)一的id,然后用字符串操作拼接,我這是在函數(shù)外面就拼接了。。。degree是當(dāng)前數(shù)據(jù)塊應(yīng)該占用的角度,percentage是當(dāng)前數(shù)據(jù)塊所占比例,r是扇形的半徑,offsetR是圖像在svg上的偏移量,還會根據(jù)全局的offsetX和offsetY具體調(diào)整,x和y是上個數(shù)據(jù)塊畫完的終點坐標(biāo),用于當(dāng)前數(shù)據(jù)塊的起點,當(dāng)前數(shù)據(jù)塊畫完后也會return出去一對xy,用于下個環(huán)形的繪制。

    drawSector(id, lineId, textId, degree, percentage, r, offsetR, x, y) {
      // 如果沒有占比,返回當(dāng)前繪畫的起點
      if (percentage == 0) {
        return {
          x: x,
          y: y
        }
      }
      let path = document.getElementById(id)
      path.setAttribute(
        "transform",
        "translate(" +
          (offsetR + this.offsetX) +
          "," +
          (offsetR + this.offsetY) +
          ")"
      )
      // 如果占比100%。走額外的圓形繪畫邏輯,結(jié)束后續(xù)的扇形繪畫邏輯
      // 這里有個問題就是起點終點重合了似乎畫不出圓,我想的解決方案就是在同一個path內(nèi)畫半圓然后再畫一個半圓
      if (percentage == 1) {
        let descriptions = [
          "M", 0, 0, "L", x, y, "A", r, r, 0, 1, 1, x, -y, "L", x, -y, "A", r, r, 0, 1, 1, x, y,  "Z"
        ]
        // 并在90度的位置劃線
        this.drawLines(lineId, textId, r, offsetR, 270 - (percentage * 360) / 2)
        path.setAttribute("d", descriptions.join(" "))
        return false
      }
      // 正常的扇形   計算第一個點的終點坐標(biāo)
      let lenghty = window.Number(percentage * 360 > 180)
      let PIdegree = (degree / 180) * Math.PI
      // calculatePositions是一個計算坐標(biāo)的函數(shù),其實就是Math.sin和Math.cos然后返回xy坐標(biāo)
      let { x1, y1 } = this.calculatePositions(r, PIdegree)
      // 畫引導(dǎo)線的函數(shù)
      this.drawLines(lineId, textId, r, offsetR, degree - (percentage * 360) / 2)
      // y往下是正,x往右是正
      let descriptions = ["M", 0, 0, "L", x, y, "A", r, r, 0, lenghty, 1,  x1, y1, "Z"]
      path.setAttribute("d", descriptions.join(" "))
      return {
        x: x1,
        y: y1
      }
    }

扇形繪制時候需要注意的點
1.占比為0,把自己的起點直接return出去給下一個環(huán)用
2.占比100%,似乎是需要畫2個半圓,用這種方法起點終點相同畫不出來東西
3.計算環(huán)形占用的角度,并把角度中點坐標(biāo)傳遞給劃線函數(shù),讓引導(dǎo)線從環(huán)形的中間往外指

第三步: 數(shù)據(jù)展示引導(dǎo)線的繪制
大致思路就是從環(huán)形數(shù)據(jù)塊的中點引出去一條線

    drawLines(lineId, textId, r, offsetR, halfDegree) {
      let path = document.getElementById(lineId)
      path.setAttribute(
        "transform",
        "translate(" +
          (offsetR + this.offsetX) +
          "," +
          (offsetR + this.offsetY) +
          ")"
      )
      let descriptions
      // 這里會做一些特殊數(shù)據(jù)的特殊待遇。。。比如我這里有一個穩(wěn)定出現(xiàn)在第一個數(shù)據(jù)塊內(nèi)部的子數(shù)據(jù)塊我把他的引導(dǎo)線寫在了右上角。。。
      if (lineId == "line5") {
        let temp = halfDegree > 15 ? 15 : halfDegree
        let { x1, y1 } = this.calculatePositions(r, (temp / 180) * Math.PI)
        descriptions = [
          `${x1},${y1}`,
          `${x1 + 6},${y1 - 55}`,
          `${x1 + 110},${y1 - 55}`
        ]
        path.setAttribute("points", descriptions.join(" "))
        this.drawText(textId, x1 + 50, y1 - 55, offsetR)
        return false
      }
      let { x1, y1 } = this.calculatePositions(r, (halfDegree / 180) * Math.PI)
      // 此處省略1w字的引導(dǎo)線避讓規(guī)則,封裝的很差,沒臉展示
      // ...
      // 第1,2,3,4象限的普通引導(dǎo)線繪制規(guī)則
      if (halfDegree < 90) {
        descriptions = [
          `${x1},${y1}`,
          `${x1 + 10},${y1 - 6}`,
          `${x1 + 65},${y1 - 6}`
        ]
        path.setAttribute("points", descriptions.join(" "))
        this.drawText(textId, x1 + 30, y1 - 6, offsetR)
        this.beforeYPosition = y1 - 6
      } else if (halfDegree < 180) {
        descriptions = [
          `${x1},${y1}`,
          `${x1 + 10},${y1 + 6}`,
          `${x1 + 65},${y1 + 6}`
        ]
        path.setAttribute("points", descriptions.join(" "))
        this.drawText(textId, x1 + 30, y1 + 6, offsetR)
        this.beforeYPosition = y1 + 6
      } else if (halfDegree < 270) {
        descriptions = [
          `${x1},${y1}`,
          `${x1 - 10},${y1 + 6}`,
          `${x1 - 65},${y1 + 6}`
        ]
        path.setAttribute("points", descriptions.join(" "))
        this.drawText(textId, x1 - 65, y1 + 6, offsetR)
        this.beforeYPosition = y1 + 6
      } else {
        descriptions = [
          `${x1},${y1}`,
          `${x1 - 10},${y1 - 6}`,
          `${x1 - 65},${y1 - 6}`
        ]
        path.setAttribute("points", descriptions.join(" "))
        this.drawText(textId, x1 - 65, y1 - 6, offsetR)
        this.beforeYPosition = y1 - 6
      }
    }

引導(dǎo)線繪制時候需要注意的點
1.從環(huán)形中點開始畫引導(dǎo)線,第1象限往右上偏移,第2象限往右下偏移,第3象限往左上偏移,第4象限往左上偏移,
2.引導(dǎo)線和文本如何避免重疊,這里我省略掉了我不太完善的防重疊代碼,大致思路就是,每次畫完一個引導(dǎo)線,用this.beforeYPosition記錄y軸坐標(biāo)位置,再記錄一個象限狀態(tài)或者角度值,下次繪制的時候發(fā)現(xiàn)y軸坐標(biāo)相差小于某一閾值(比如我這里是35)并且在同一象限,我們就會讓線繞的遠(yuǎn)一點,每個象限會有自己獨特的繞遠(yuǎn)過程,比如下面這段代碼

// 第三象限避讓示例
else if (halfDegree < 270) {
  descriptions = [
    `${x1},${y1}`, // 正常的起點
    `${(x1 - 68) / 2},${y1}`, // 先向左走一截,是為了線不會和環(huán)形重疊
    `-68,${y1 - 35}`, // 再向上35進行避讓
    `-118,${y1 - 35}` // 往外延伸用于展示數(shù)據(jù)的部分
  ]
  path.setAttribute("points", descriptions.join(" "))
  this.drawText(textId, -118, y1 - 35, offsetR)
  this.beforeYPosition = y1 - 35
}

第四步: 數(shù)據(jù)展示文案的繪制
在線的上方寫數(shù)字,在下面寫文字,因為文案一般都是固定的,所以位置很好定,但是數(shù)字是后端返回的,長度會變,所以右對齊的時候需要點特殊手段,大致代碼:

    drawText(textId, x, y, offsetR) {
      let path = document.getElementById(textId)
      path.setAttribute(
        "transform",
        "translate(" +
          (offsetR + this.offsetX) +
          "," +
          (offsetR + this.offsetY) +
          ")"
      )
      let path1 = document.getElementById(textId + "2")
      this.drawNum(textId, x, y, offsetR)
      this.drawPureText(path1, x, y, offsetR)
    },

    drawPureText(path, x, y) {
      path.setAttribute("x", x)
      path.setAttribute("y", y + 12)
    },

    drawNum(textId, x, y) {
      let number
      let {
        un_submit,
        wait,
        revising,
        done,
        wanke_unsubmit
      } = this.chapterContent.task
      switch (textId) {
        case "text1":
          number = un_submit
          break
        case "text2":
          number = wait
          break
        case "text3":
          number = revising
          break
        case "text4":
          number = done
          break
        case "text5":
          number = wanke_unsubmit
          x += 25
          break
        default:
          break
      }
      if (x > 0) {
        // 第1、2象限右對齊
        if (number >= 100) {
          x += 8
        } else if (number >= 10) {
          x += 18
        } else {
          x += 28
        }
      }
      let path = document.getElementById(textId + "1")
      path.setAttribute("x", x)
      path.setAttribute("y", y - 4)
    },

文本繪制時候需要注意的點似乎也就只有1,2象限的右對齊問題

最后一步:鼠標(biāo)懸浮數(shù)據(jù)區(qū)域后的額外繪制和懸浮提示
我這里是監(jiān)聽MouseEenter和MouseLeave然后對相關(guān)的部分進行重新繪制,代碼如下:

  larger(index) {
    this.showFloatRemind = 1
    let { total, un_submit, wait, wanke_unsubmit } = this.chapterContent.task
    if (index == 1) {
      window.cancelAnimationFrame(this.firstAnimation)
      let animation = () => {
        this.onlyDrawSector(
          "ring" + index,
          (un_submit / total) * 360,
          un_submit / total,
          this.first_range,
          65,
          0,
          -this.first_range
        )
        this.first_range += 0.5
        if (this.first_range < 70) {
          this.firstAnimation = window.requestAnimationFrame(animation)
        }
      }
      animation()
    }
    // 省略其他index
  },
  smaller(index) {
    this.showFloatRemind = 0
    let { total, un_submit, wait, wanke_unsubmit } = this.chapterContent.task
    if (index == 1) {
      window.cancelAnimationFrame(this.firstAnimation)
      let animation = () => {
        this.onlyDrawSector(
          "ring" + index,
          (un_submit / total) * 360,
          un_submit / total,
          this.first_range,
          65,
          0,
          -this.first_range
        )
        this.first_range -= 0.5
        if (this.first_range > 65) {
          this.firstAnimation = window.requestAnimationFrame(animation)
        }
      }
      animation()
    }
}

需要注意的點就是利用requestAnimationFrame,和在切換狀態(tài)時記得清除掉無用的requestAnimationFrame狀態(tài),防止出現(xiàn)bug

扇形

今年3月份還是5月份看了個挺有趣的技術(shù)分享,講的是css變量的應(yīng)用,里面主要講的是如何用css變量+opacity+rotate實現(xiàn)一個扇形,具體方式如下

<!DOCTYPE html>
<html>
<head>
    <style>
        .pie-simple {
            width: 128px;
            height: 128px;
            background-color: white;
            border-radius: 50%;
            overflow: hidden;
        }
        .pie-left,
        .pie-right {
            width: 50%;
            height: 100%;
            float: left;
            position: relative;
            overflow: hidden;
        }
        .pie-left::before,
        .pie-right::before,
        .pie-right::after {
            content: '';
            position: absolute;
            width: 100%; height: 100%;
            background-color: red;
        }
        .pie-left::before {
            left: 100%;
            transform-origin: left;
            transform: rotate(calc(3.6deg * (var(--percent) - 50)));
            opacity: calc(99999 * (var(--percent) - 50));
        }
        .pie-right::before {
            right: 100%;
            transform-origin: right;
            transform: rotate(calc(3.6deg * var(--percent)));
        }
        .pie-right::after {
            opacity: calc(99999 * (var(--percent) - 50));
        }
    </style>
</head>
<body>
    <div class="pie-item">
        <p>10%大小</p>
        <div class="pie-simple" style="--percent: 10;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
    </div>
    <div class="pie-item">
        <p>40%大小</p>
        <div class="pie-simple" style="--percent: 40;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
    </div>
    <div class="pie-item">
        <p>80%大小</p>
        <div class="pie-simple" style="--percent: 80;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
    </div>
    <div class="pie-item">
        <p>99%大小</p>
        <div class="pie-simple" style="--percent: 99;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
    </div>
</body>
</html>

這只是一個靜態(tài)的扇形展示,核心思想是利用css變量控制opacity,opacity為負(fù)數(shù)被認(rèn)為是0,大于1被認(rèn)為是1。
效果圖大致就是這樣的:


image.png

總結(jié)一下發(fā)生了啥:

  1. 正方形被一分為二,左邊一個白色矩形div,右邊一個白色矩形div,超出正方形的部分overflow掉
  2. 左邊div的:before 有背景色有透明度且轉(zhuǎn)動 ,負(fù)責(zé)控制扇形超過50%時,左側(cè)部分的顯示
  3. 右邊div的:after 有背景色有透明度不轉(zhuǎn)動,負(fù)責(zé)控制扇形超過50%后,右側(cè)部分全部顯示
  4. 右邊div的:before 有背景色不透明且轉(zhuǎn)動 ,負(fù)責(zé)控制扇形小于50%時,右側(cè)部分的顯示

關(guān)于扇形的動畫放在環(huán)形進度條里一起再說

環(huán)形進度條

環(huán)形進度條最直觀的實現(xiàn)方式其實就是一個大的扇形扣上個一個小圓,代碼和上面的差不多,不過扇形/餅狀圖一般可以是靜態(tài)的,但是進度條普遍都是是動態(tài)的,如果讓css變量實現(xiàn)的環(huán)形進度條動起來?主要介紹下面兩個方案:

1. 保留css變量,使用animation來實現(xiàn)動畫
css變量并不支持平滑的過度,但是你可以在固定幀規(guī)定變量的值,你可以寫的足夠細(xì),動畫就足夠順化,但是平時也會經(jīng)??吹侥欠Nduangduangduang~一截一截往前蹦的進度條。

.pie-simple {
    animation: round 10s linear infinite;
}
@keyframes round{
            0%{--percent: 0;}
            10%{--percent: 10;}
            20%{--percent: 20;}
            30%{--percent: 30;}
            40%{--percent: 40;}
            50%{--percent: 50;}
            60%{--percent: 60;}
            70%{--percent: 70;}
            80%{--percent: 80;}
            90%{--percent: 90;}
            100%{--percent: 100;}
        }

順帶一說只有Chrome能像上面那么寫,火狐需要把變量和使用到變量的屬性都塞到keyframe里,ie和safari干脆就直接不支持了。。。

我在DOM.style里找不到css變量對應(yīng)的屬性,所以似乎沒法直接通過requestAnimationFrame控制--percent來實現(xiàn)動畫,但是可以在CSSRuler里用js手撕關(guān)鍵幀。下圖是示例

css變量控制的環(huán)形進度條

2. 移除css變量,使用animation來實現(xiàn)動畫
因為兼容性和幀的復(fù)雜性問題,實現(xiàn)動畫還是放棄css變量比較好,最直觀的影響就是不能讓opacity實現(xiàn)有效的突變,把opacity放在動畫里會有漸變的過程,所以這里又出現(xiàn)了兩種解決方案:
a.給左側(cè)矩形div增加一個偽類,用于沒有超過50%左側(cè)的白色遮蓋,利用animation的steps()實現(xiàn)opacity的突變,代碼如下

<!DOCTYPE html>
<html>
<head>
    <style>
        .pie-simple {
            position: relative;
            width: 128px;
            height: 128px;
            background-color: white;
            border-radius: 50%;
            overflow: hidden;
        }
        .pie-left,
        .pie-right {
            width: 50%;
            height: 100%;
            float: left;
            position: relative;
            overflow: hidden;
        }
        .pie-left::before,
        .pie-right::before,
        .pie-left::after,
        .pie-right::after{
            content: '';
            position: absolute;
            width: 100%; height: 100%;
            background-color: red;
        }
        .pie-left::after{
            background: #fff
        }
        .pie-left::before {
            left: 100%;
            transform-origin: left;
            animation: fuspin 10s linear infinite;
        }
        .pie-left::after {
            animation: second-half-hide 10s steps(1, end) infinite;
        }
        .pie-right::before {
            right: 100%;
            transform-origin: right;
            animation: spin 10s linear infinite;
        }
        .pie-right::after {
            animation: second-half-show 10s steps(1, end) infinite;
        }
        @keyframes spin {
        0%   { transform: rotate(0); }
        100% { transform: rotate(360deg); }
        }
        @keyframes fuspin {
        0%   { transform: rotate(180deg); }
        100% { transform: rotate(540deg); }
        }
        @keyframes second-half-hide {
        0%        { opacity: 1; }
        50%, 100% { opacity: 0; }
        }
        @keyframes second-half-show {
        0%        { opacity: 0; }
        50%, 100% { opacity: 1; }
        }
        .cover{
            width: 120px;
            height: 120px;
            border-radius: 50%;
            background: #fff;
            position: absolute;
            top: 0;
            bottom: 0;
            right: 0;
            left: 0;
            margin: auto;
        }
    </style>
</head>
<body>
    <div class="pie-item">
        <div class="pie-simple">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
            <div class="cover"></div>
        </div>
    </div>
</body>
</html>

效果圖如下,gif顯得有點卡,實際是很流暢的,在mac的chrome下左側(cè)會有個縫隙,白色的:after并不能完全蓋住紅色的:before。。。在window的chrome下就沒有

b.只使用2個帶背景色的偽類,利用超出overflow+兩者的動畫錯開實現(xiàn)空白占位和有色占位代碼如下

<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        .pie-spin {
            position: relative;
            width: 128px; height: 128px;
            background-color: white;
            border-radius: 50%;
            overflow: hidden;
        }
        .pie-spin-left,
        .pie-spin-right {
            width: 50%; height: 100%;
            float: left;
            position: relative;
            overflow: hidden;
        }
        .pie-spin-left::before,
        .pie-spin-right::before {
            content: '';
            position: absolute;
            width: 100%; height: 100%;
            background-color: red;
        }
        .pie-spin-left::before {
            left: 100%;
            transform-origin: left;
            animation: spinWait 3.2s infinite linear;
        }
        .pie-spin-right::before {
            right: 100%;
            transform-origin: right;
            animation: spinWait 3.2s infinite linear;
        }
        @keyframes spinWait {
            0%        { transform: rotate(0deg); }
            50%, 100%  { transform: rotate(180deg); }
        }
        @keyframes spinWait {
            0%, 50%  { transform: rotate(0deg); }
            100%     { transform: rotate(180deg); }
        }
        .cover{
            width: 120px;
            height: 120px;
            border-radius: 50%;
            background: #fff;
            position: absolute;
            top: 0;
            bottom: 0;
            right: 0;
            left: 0;
            margin: auto;
        }
    </style>
</head>
<body>
    <div class="pie-spin">
        <div class="pie-spin-left"></div>
        <div class="pie-spin-right"></div>
        <div class="cover"></div>
    </div>
</body>
</html>

效果圖如下,因為只有兩個塊,沒有遮罩層,不顯示的時候躲在對邊等待轉(zhuǎn)動,所以也不會出現(xiàn)那一個小小的縫隙


環(huán)形圖

上面的idea過于舒暢,導(dǎo)致我當(dāng)初第一反應(yīng)就是用css變量或者偽類的旋轉(zhuǎn)和遮蓋來先出一版,調(diào)試了大概一個下午,最后也只能是正常的展示吧,因為一層一層的遮蓋導(dǎo)致懸浮點擊的交互根本無從入手,最后5個數(shù)據(jù)塊,20個偽類層巒疊嶂弄得我十分難受,如果你只是想要一個不能動的環(huán)形圖,也是能夠?qū)崿F(xiàn)的,注意后出現(xiàn)的部分的:before和:after的z-index高于前者就行,代碼如下

<!DOCTYPE html>
<html>
<head>
    <style>
.round-container{
    position: relative;
    height: 150px;
}
.pie-simple {
    position: absolute;
    width: 130px;
    height: 130px;
    top: 10px;
    left: 60px;
    background-color: white;
    border-radius: 50%;
    overflow: hidden;
    /*box-shadow: 1px 1px 1px 3px rgba(0,0,0,0.1)*/
}
.pie-left,
.pie-right {
    position: absolute;
    width: 50%;
    height: 100%;
    float: left;
    overflow: hidden;
}
.pie-left{
    left: 0;
    top: 0
}
.pie-right {
    left: 50%;
    top: 0;
}
.pie-left::before,
.pie-right::before,
.pie-right::after {
    content: '';
    position: absolute;
    width: 100%; height: 100%;
    background-color: #df3849;
}
.pie-left::before {
    left: 100%;
    transform-origin: left;
    transform: rotate(calc(3.6deg * (var(--percent) - 50)));
    opacity: calc(99999 * (var(--percent) - 50));
}
.pie-right::before {
    right: 100%;
    transform-origin: right;
    transform: rotate(calc(3.6deg * var(--percent)));
}
.pie-right::after {
    opacity: calc(99999 * (var(--percent) - 50));
}

.a .pie-left::before{
    z-index: 300;
}
.b .pie-left::before{
    : 200;
    background-color:#6434e5;
}
.c .pie-left::before{
    z-index: 100;
    background-color:#4d8ef6;
}
.d .pie-left::before{
    z-index: 50;
    background-color:#69a42f;
}

.a .pie-right::before{
    z-index: 300;
}

.b .pie-right::before{
    z-index: 200;
    background-color:#6434e5;
}

.c .pie-right::before{
    z-index: 100;
    background-color:#4d8ef6;
}
.d .pie-right::before{
    z-index: 50;
    background-color:#69a42f;
}


.a .pie-right::after{
    z-index: 300;
}
.b .pie-right::after{
    z-index: 200;
    background-color:#6434e5;
}
.c .pie-right::after{
    z-index: 100;
    background-color:#4d8ef6;
}
.d .pie-right::after{
    z-index: 50;
    background-color:#69a42f;
}
.b{
    width: 129px;
    height: 129px;
    top: 10.5px;
    left: 60.5px;

}
.c{
    width: 128px;
    height: 128px;
    top: 11px;
    left: 61px;

}
.e {
    width: 100px;
    height: 100px;
    top: 25px;
    left: 75px;
}
.e .pie-right::after{
    z-index: 400;
    background-color:#ff8d05;

}
.e .pie-right::before{
    z-index: 400;
    background-color:#ff8d05;

}
.e .pie-left::before{
    z-index: 400;
    background-color:#ff8d05;
}


.cover{
    position: absolute;
    top: 40px;
    left: 90px;
    width: 70px;
    height: 70px;
    z-index: 9999;
    background-color: white;
    border-radius: 50%;
    overflow: hidden;
}
    </style>
</head>
<body>
    <div class="round-container">
        <div class="pie-simple a" style="--percent: 20;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
        <div class="pie-simple b" style="--percent: 60;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
        <div class="pie-simple c" style="--percent: 80;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
        <div class="pie-simple d" style="--percent: 100;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
        <div class="pie-simple e" style="--percent: 10;">
            <div class="pie-left"></div>
            <div class="pie-right"></div>
        </div>
        <div class="cover"></div>
      </div>
</body>
<script type="text/javascript">
 
</script>
</html>

靜態(tài)的效果如下:



因為這個是一層一層蓋住的,你想讓一個環(huán)放大,之前被擋住的地方就漏出來了~所以解決方案是讓每一個的環(huán)都是從上一個環(huán)結(jié)束的位置開始,但是這樣有感覺好麻煩,而且你把鼠標(biāo)浮上去。。。你根本拿不到你想要的event.target[手動捂臉],所以我只能用svg去畫了。。。

最后,感謝看官看到最后~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容