D3.js 实现镂空渐变圆环

使用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);
  }
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值