使用D3 V4版本绘制
嗯…最终要实现的效果:
效果实现主要分为以下几个部分,其中主要介绍中间的镂空渐变圆环如何实现。
镂空圆环的实现有很多种,最简单的一种设置一个宽高相同的块状元素, background 为 transparent ,设置 border 及 border-radius 即可。但是如果添加渐变颜色,就会变成下面这个样子。
所以本篇的渐变部分使用了添加 svg 元素的方法,主要思路为:
- 绘制圆环 circle ,设置圆环填充 fill:none; stroke-width:34;
- 为圆环 stroke 添加渐变颜色;
- 使用 clip-path 截取需要的颜色部分。
因使用 d3 控制 svg 比较方便,故选择使用了 d3.js 绘制需要的 svg 元素。当然,也可以选择直接创建svg文件。
完整代码:
项目使用 react + antd 框架
index.js
import React, { Component } from 'react';
import styles from './index.less';
import renderGraph from './render';
export default class extends Component {
constructor(props) {
super(props);
this.state = {};
this.graphNode = null;
}
componentDidMount() {
renderGraph(this.graphNode);
}
render() {
return (
<div className={styles.container} >
<div className={styles.group} >
<div className={styles.con} >
<div className={styles.dashLine} >
<p></p><p></p><p></p>
</div >
<div className={styles.graphWrp} ref={(node) => { this.graphNode = node; }}></div>
<div className={styles.icon}>
<img src={require('../../assets/plant.svg')} alt="" />
</div>
<div className={styles.outerLine}></div>
</div>
</div >
<svg width="0" height="0" >
<defs >
<linearGradient x1="1" y1="0" x2="0" y2="1" id='gradientRing'>
<stop offset="0%" stopColor='#76c0ff' stopOpacity="1"> </stop>
<stop offset="40%" stopColor='#76c0ff' stopOpacity="0.5"> </stop>
<stop offset="74%" stopColor='#76c0ff' stopOpacity="0"> </stop>
</linearGradient>
</defs>
</svg>
</div>
);
}
}
render.js
import * as d3 from 'd3';
export default function graphs(node) {
(() => {
d3.select(node).selectAll('svg').remove();
})();
// ***************数据初始化*******************
const width = node.offsetWidth; // width包括 paddingLeft 和 paddingRight
const height = node.offsetHeight;
// const colorConfig = ['#76c0ff', '#6ce6a2', '#6ff8c6'];
const colorConfig = '#76c0ff';
const circleR = [66, 80, 114];
// 绘制
const svg = d3.select(node).append('svg')
.attr('width', width)
.attr('height', height);
// 画圆
const circleGroup = svg.append('g').attr('class', 'circleGroup');
circleGroup.selectAll('.circleItem')
.data(circleR)
.enter()
.append('circle')
.attr('class', 'circleItem')
.attr('cx', width / 2)
.attr('cy', height / 2)
.attr('fill', 'none')
.attr('r', (d) => d)
.attr('stroke-width', 2)
.attr('stroke', (d, i) => {
const rgb = d3.rgb(colorConfig);
const opacity = !i ? 0.3 : 0.9;
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${opacity}`;
});
// 渐变弧形
svg.append('circle')
.attr('class', 'gradientCircle')
.attr('cx', width / 2)
.attr('cy', height / 2)
.attr('fill', 'none')
.attr('r', 97)
.attr('stroke-width', 34)
.attr('stroke', 'url(#gradientRing)')
.attr('clip-path', 'url(#circle-clip)');
svg.append('clipPath') // 过渡显示效果 .attr({ 'clip-path': 'url(#circle-clip)' })
.attr('id', 'circle-clip')
.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', width / 2)
.attr('height', (height / 2) + 20);
}
index.less
.container {
box-sizing: border-box;
height: 100% ;
padding: 20px;
.group {
height: 100% ;
background: #01080f;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
.con {
position: relative;
margin-bottom: 46px;
.dashLine {
position: absolute;
top: 0;
left: 0;
width: 250px;
height: 250px;
p {
position: absolute;
left: 0;right: 0;
top: 0;bottom:0;
margin: auto;
width: 0px;
height: 230px;
border-right: dashed 2px #ffffff;
opacity: 0.11;
transform-origin: center;
}
p:nth-child(2) {
transform: rotate(60deg);
}
p:nth-child(3) {
transform: rotate(120deg);
}
}
.graphWrp {
width: 250px;
height: 250px;
animation: rotateAnimate 3s linear infinite;
}
.icon {
position: absolute;
top: 0;
left: 0;
width: 250px;
height: 250px;
display: flex;
justify-content: center;
align-items: center;
& img {
width: 100px;
}
}
.outerLine {
position: absolute;
top: 0;
left: 0;
width: 250px;
height: 250px;
border: 2px solid #76c0ff;
border-radius: 200%;
border-right-color: transparent;
border-left-color: transparent;
animation: antiAotateAnimate 5s linear infinite;
}
}
}
}
@keyframes rotateAnimate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes antiAotateAnimate {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}