Skip to content

如何用 CSS3 做环图

代码地址 css3 环图.zip 小程序组件

js
let getStyleByObj = (obj) => {
  let result = "";
  for (const key in obj) {
    result += `${key}:${obj[key]};`;
  }
  return result;
};
function computedRate(accuracy) {
  return Math.round((accuracy * 100).toFixed(2));
}
function computedColor(rate) {
  rate = rate * 100;
  var green = "#0FD36C",
    blue = "#0089FF",
    yellow = "#FFCB00",
    red = "#FE5F69";
  if (rate >= 80) {
    return green;
  }

  if (rate >= 60 && rate < 80) {
    return blue;
  }

  if (rate > 20 && rate < 60) {
    return yellow;
  }

  if (rate <= 20) {
    return red;
  }

  return blue;
}
Component({
  options: {
    addGlobalClass: true,
  },
  observers: {
    percent: function(num) {
      this.initVar().then(() => {
        let rate = computedRate(num);
        this.setData({
          rate,
        });
        let color = computedColor(rate);
        /* 超过边界 */
        if (rate < 0) {
          this.data.progress["border-color"] = this.data.bgColor;
          this.data.before["border-color"] = this.data.bgColor;
          this.data.after["border-color"] = this.data.bgColor;
        }
        if (rate > 100) {
          this.data.progress["border-color"] = color;
          this.data.before["border-color"] = color;
          this.data.after["border-color"] = color;
        }
        if (rate > 50 && rate <= 100) {
          let deg = (rate - 50) * 3.6;
          this.data.after.transform = `rotate(${deg}deg)`;
          this.data.after["border-color"] = color;
          this.data.progress["border-color"] = color;
          this.data.before["border-color"] = this.data.bgColor;
          if (rate == 100) {
            this.data.before["border-color"] = color;
          }
        }
        if (rate >= 0 && rate <= 50) {
          let deg = rate * 3.6;
          this.data.progress.transform = `rotate(${deg}deg)`;
          this.data.progress["border-color"] = this.data.bgColor;
          this.data.before["border-color"] = this.data.bgColor;
          this.data.after["border-color"] = color;
          if (rate == 0) {
            this.data.after["border-color"] = this.data.bgColor;
          }
        }
        this.setData({
          color,
        });
        this.init();
      });
    },
  },
  data: {
    color: "",
    rate: "0",
    bgColor: "#EDEFF3",
    chartStyle: "",
    beforeStyle: "",
    afterStyle: "",
    progressStyle: "",
    chart: {
      width: "80px",
      height: "80px",
      float: "left",
      position: "relative",
      "text-align": "center",
    },
    before: {
      position: "absolute",
      top: 0,
      right: 0,
      bottom: 0,
      left: 0,
      border: "6px solid",
      "border-color": "#ccc",
      "border-radius": "50%",
      "z-index": 1,
    },
    after: {
      transform: "",
      position: "absolute",
      top: 0,
      right: 0,
      bottom: 0,
      left: 0,
      border: "6px solid",
      "border-color": "#999",
      "border-radius": "50%",
      clip: "rect(0,auto,auto,40px)",
      "z-index": 2,
    },
    progress: {
      transform: "",
      position: "absolute",
      top: 0,
      right: 0,
      bottom: 0,
      left: 0,
      border: "6px solid",
      "border-color": "#999",
      "border-radius": "50%",
      clip: "rect(0,auto,auto,40px)",
      "z-index": 3,
    },
  },
  properties: {
    percent: {
      type: Number,
      value: 0,
    },
  },
  methods: {
    initVar() {
      return new Promise((resolve, reject) => {
        this.setData(
          {
            chart: {
              width: "80px",
              height: "80px",
              float: "left",
              position: "relative",
              "text-align": "center",
            },
            before: {
              position: "absolute",
              top: 0,
              right: 0,
              bottom: 0,
              left: 0,
              border: "6px solid",
              "border-color": "#ccc",
              "border-radius": "50%",
              "z-index": 1,
            },
            after: {
              transform: "",
              position: "absolute",
              top: 0,
              right: 0,
              bottom: 0,
              left: 0,
              border: "6px solid",
              "border-color": "#999",
              "border-radius": "50%",
              clip: "rect(0,auto,auto,40px)",
              "z-index": 2,
            },
            progress: {
              transform: "",
              position: "absolute",
              top: 0,
              right: 0,
              bottom: 0,
              left: 0,
              border: "6px solid",
              "border-color": "#999",
              "border-radius": "50%",
              clip: "rect(0,auto,auto,40px)",
              "z-index": 3,
            },
          },
          () => {
            resolve();
          }
        );
      });
    },
    init() {
      this.setData({
        chartStyle: getStyleByObj(this.data.chart),
        beforeStyle: getStyleByObj(this.data.before),
        afterStyle: getStyleByObj(this.data.after),
        progressStyle: getStyleByObj(this.data.progress),
      });
    },
  },
});
wxml
<view style="corlor:red;{{chartStyle}}">
  <view style="{{beforeStyle}}"></view>
  <view style="{{progressStyle}}"></view>
  <view class="component-rate-panel">
    <view class="rate" style="color:{{color}};">{{rate}}%</view>
    <view class="text">正确率</view>
  </view>
  <view style="{{afterStyle}}"></view>
</view>
wxss
.component-rate-panel {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0rpx;
  top: 0rpx;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.component-rate-panel .rate {
  font-family: PingFangSC-Semibold;
  font-size: 35rpx;
  color: #0089ff;
  letter-spacing: 0.16rpx;
  position: relative;
  left: 3rpx;
}

.component-rate-panel .text {
  font-family: PingFangSC-Regular;
  font-size: 18rpx;
  color: #9ea7b7;
  margin-top: 1rpx;
}
json
{
  "component": true
}

Canvas 画一个半圆

function drawScreen(myCanvas, r, lw, lr, rr, g) {
	// x,y => 圆心坐标点
	// r => 圆弧半径
	var arc = {
		x: myCanvas.width / 2,
		y: myCanvas.height / 2 + 16,
		r: (myCanvas.height - r) / 2
	}
	var ctx = myCanvas.getContext('2d')
	ctx.save()
	ctx.lineWidth = lw

	if (g) {
		//计算渐变起始坐标
		let L = Math.sqrt(Math.pow(arc.r, 2) / 2)
		let gx0 = arc.x - L
		let gy0 = arc.y + L
		/* 指定渐变区域 */
		var grad = ctx.createLinearGradient(
			arc.x - L,
			arc.y + L,
			arc.x - L,
			arc.y
		)
		grad.addColorStop(1, '#FEE891')
		grad.addColorStop(0, '#FEB832')
		ctx.strokeStyle = grad
	} else {
		ctx.strokeStyle = '#F2F3F8'
	}

	// 顺时针旋转
	ctx.beginPath()
	ctx.arc(arc.x, arc.y, arc.r, getRads(lr), getRads(rr))
	ctx.stroke()
}
function getRads(degrees) {
	return Math.PI * degrees / 180
}
function getDegrees(rads) {
	return rads * 180 / Math.PI
}
export default {
	mounted: () => {
		//获取根元素字体大小计算rem
		let fontSize = document.documentElement.style.fontSize
		CONST.fontSize = parseInt(fontSize.substring(0, fontSize.length - 2))
		CONST.width = document.body.clientWidth
		CONST.widthRem = CONST.width / CONST.fontSize
		drawScreen(document.getElementById('circle'), 0, 6, 135, 45) //绘制外层灰色
		drawScreen(document.getElementById('circle'), 36, 1.5, 132, 48) //绘制内层灰色
		drawScreen(document.getElementById('circle'), 0, 6, 135, 200, true) //绘制渐变
	}
}

flex 横向 margin-right 最后一个不生效

<div class="relation-reason-item-panel">
				<div class="relation-reason-item" v-for="">
					<div class="top">
						<img :src="$_img + 'page_book_detail_v2_icon_question.png'" alt class="icon" />
						<div class="question-title"></div>
					</div>
					<div class="nums">234个方案 · 超2345人已阅读过</div>
					<img :src="$_img + 'page_book_detail_v2_bg_question.png'" alt class="bg" />
				</div>

				<div style="width:0.1vw;flex-shrink:0"></div>
			</div>

在最后加一个 div shrink:0 width 有一点就好了

flip动画

link