阅读 974

【d3js】d3js实现一个围绕圆运动的动画效果

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

前言

发表了3篇文章吧

  • 1、svg 基础svg图形属性
  • 2、d3(一)基础语法api
  • 3、d3(二)实现一个数据绑定的一套绘制方案

那在这个基础上能实现什么呢?

今天就来实现一个简单的关于圆动画!! 效果图如下

效果呈现:

zoo1m.gif

代码实现

首先咱们可以分析下,就是外面一个大圆(圆行线),然后圆上面有3个小圆,然后中间一个圆,视图结构大概就是这个样,一共5个圆构成。

实现一

其实大圆线边框线和内部的圆实现起来比较简单,咱们先实现这个

代码如下(运用的都是之前文章分享过的,大家可以去查阅):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="main"></div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>

<script>


 let svg = d3.select('#main')
             .append('svg')
             .attr('width', 500)
             .attr('height', 500);


drawCircle(250,250,30, '#c63f5a');
drawCircle(250,250,90, '#f649647e', true);


 function drawCircle(x,y,r,color='#e3e3e3', isDash) {
    const circle = svg.append('circle')
        .attr('cx', x)
        .attr('cy', y)
        .attr('r', r)
        .attr('stroke', color)
        .attr('cursor', 'pointer')
        .attr('fill', 'none');
        
        
    if(isDash) {
        circle.style('stroke-dasharray', '3 3')
    }else {
        circle.attr('fill', color).attr('fill-opacity', 0.2);
    }
 }

</script>
复制代码

代码解释:

  • 1、再x=250,y=250画了一个小圆(半径30);
  • 2、再x=250,y=250画了一个没有颜色填充的大圆(半径90),边框虚线;

效果图呈现:

d6aff493bdcb734b7a853f1795231ff.png

实现二

怎么实现一个再[250, 250], 半径90的边上画3个圆呢? 利用数学的三角函数

代码如下:

**
* 获取一个圆上的节点坐标
* @param {Array} origin 坐标[x,y]
* @param {Number} r  半径
* @param {Number} n 数量
* @param {Number} offset 偏移角度
* @returns 
*/
function getVertices(origin, r, n, offset = 90) {
    let ox = origin[0];
    let oy = origin[1];
    let angle = 360 / n;
    let i = 0;
    let points = [];
    let tempAngle = 0;
    
    while (i < n) {
      tempAngle = ((i * angle + offset) * Math.PI) / 180;
      points.push({
        x: ox + r * Math.sin(tempAngle),
        y: oy - r * Math.cos(tempAngle)
      });
      i++;
    }
    return points;
}
复制代码

结合上面的方法咱们把剩余的三个边上的圆给绘制出来代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="main"></div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>

<script>
/**
* 获取一个圆上的节点左边
* @param {Array} origin 坐标[x,y]
* @param {Number} r  半径
* @param {Number} n 数量
* @param {Number} offset 偏移角度
* @returns 
*/
function getVertices(origin, r, n, offset = 90) {
    let ox = origin[0];
    let oy = origin[1];
    let angle = 360 / n;
    let i = 0;
    let points = [];
    let tempAngle = 0;
    // 依次算出各个元素圆上的坐标
    while (i < n) {
      tempAngle = ((i * angle + offset) * Math.PI) / 180;
      points.push({
        //
        x: ox + r * Math.sin(tempAngle),
        //
        y: oy - r * Math.cos(tempAngle)
      });
      i++;
    }
    return points;
}

 let svg = d3.select('#main')
             .append('svg')
             .attr('width', 500)
             .attr('height', 500);



drawCircle(250,250,30, '#c63f5a');
drawCircle(250,250,90, '#f649647e', true);
//新增
const points = getVertices([250, 250], 90, 3);
points.forEach((item) => {
  drawCircle(item.x, item.y, 30, '#c68242');
})
//


 function drawCircle(x,y,r,color='#e3e3e3', isDash) {
    const circle = svg.append('circle')
        .attr('cx', x)
        .attr('cy', y)
        .attr('r', r)
        .attr('stroke', color)
        .attr('cursor', 'pointer')
        .attr('fill', 'none');
        
        
    if(isDash) {
        circle.style('stroke-dasharray', '3 3')
    }else {
        circle.attr('fill', color).attr('fill-opacity', 0.2);
    }
 }

</script>
复制代码

代码解释:

  • 1、新增getVertices()算圆上坐标的方法;
  • 2、得到坐标绘制剩余的圆;

效果呈现:

788b0d4f99796c24923d6b502e2813b.png

实现三

动画让圆滚动起来? 其实大家可以想想其实圆滚动起来是不是就是角度不同罢了? 那咱们结合昨天的数据绑定把代码改改让实现成数据驱动的绘制方法

代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="main"></div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>

<script>
/**
* 获取一个圆上的节点左边
* @param {Array} origin 坐标[x,y]
* @param {Number} r  半径
* @param {Number} n 数量
* @param {Number} offset 偏移角度
* @returns 
*/
function getVertices(origin, r, n, offset = 90) {
    let ox = origin[0];
    let oy = origin[1];
    let angle = 360 / n;
    let i = 0;
    let points = [];
    let tempAngle = 0;
    // 依次算出各个元素圆上的坐标
    while (i < n) {
      tempAngle = ((i * angle + offset) * Math.PI) / 180;
      points.push({
        //
        x: ox + r * Math.sin(tempAngle),
        //
        y: oy - r * Math.cos(tempAngle)
      });
      i++;
    }
    return points;
}


 let svg = d3.select('#main')
             .append('svg')
             .attr('width', 500)
             .attr('height', 500);

let sourceData = [{
    x: 250,
    y: 250,
    r: 30,
    color: '#c63f5a',
    dash: false
}, {
    x: 250,
    y: 250,
    r: 90,
    color: '#f649647e',
    dash: true
}];

const circleTemplate = {
    x: 0,
    y: 0,
    r: 30,
    color: '#c68242',
    dash: false
};
const points = getVertices([250, 250], 90, 3).map(item => {
    return Object.assign({}, circleTemplate, item)
});

const circleData = [...sourceData, ...points];

 
drawCircle();

    

 function drawCircle() {
    const update = svg.selectAll('circle').data(circleData);

    const enter = update.enter();


    enter.append('circle')
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)
        .attr('r', d => d.r)
        .attr('stroke',d => d.color)
        .attr('cursor', 'pointer')
        .attr('fill', (d) => d.dash ? 'none' : d.color)
        .attr('fill-opacity', (d) => d.dash ? 'none' : 0.2)
        .attr('stroke-dasharray', d => d.dash ? '3 3' : 'none');

     //修改层——其实咱们动画的话就修改cx,cy的
    update.attr('cx', d => d.x)
          .attr('cy', d => d.y);
        
 }

</script>
复制代码

代码解释:

  • 1、咱们把咱们的数据放在一个数组circleData里面
  • 2、用数据绑定的方式是西安绘制函数drawCircle,不懂得可以看看d3(二)数据绑定

效果呈现:

实现四

让动画动起来,就是改变offset, 然后调用咱们得绘制函数就行了

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="main"></div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>

<script>
/**
* 获取一个圆上的节点左边
* @param {Array} origin 坐标[x,y]
* @param {Number} r  半径
* @param {Number} n 数量
* @param {Number} offset 偏移角度
* @returns 
*/
function getVertices(origin, r, n, offset = 90) {
    let ox = origin[0];
    let oy = origin[1];
    let angle = 360 / n;
    let i = 0;
    let points = [];
    let tempAngle = 0;
    // 依次算出各个元素圆上的坐标
    while (i < n) {
      tempAngle = ((i * angle + offset) * Math.PI) / 180;
      points.push({
        x: ox + r * Math.sin(tempAngle),
        y: oy - r * Math.cos(tempAngle)
      });
      i++;
    }
    return points;
}


 let svg = d3.select('#main')
             .append('svg')
             .attr('width', 500)
             .attr('height', 500);



let sourceData = [{
    x: 250,
    y: 250,
    r: 30,
    color: '#c63f5a',
    dash: false
}, {
    x: 250,
    y: 250,
    r: 90,
    color: '#f649647e',
    dash: true
}];

const circleTemplate = {
    x: 0,
    y: 0,
    r: 30,
    color: '#c68242',
    dash: false
};

let i = 0;

function getCircleData() {
    i++;
    const points = getVertices([250, 250], 90, 3, i * 90).map(item => {
        return Object.assign({}, circleTemplate, item)
    });
    return [...sourceData, ...points];
}


let circleData = getCircleData();

 
drawCircle();

    

 function drawCircle() {
    const update = svg.selectAll('circle').data(circleData);
    const enter = update.enter();


    enter.append('circle')
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)
        .attr('r', d => d.r)
        .attr('stroke',d => d.color)
        .attr('cursor', 'pointer')
        .attr('fill', (d) => d.dash ? 'none' : d.color)
        .attr('fill-opacity', (d) => d.dash ? 'none' : 0.2)
        .attr('stroke-dasharray', d => d.dash ? '3 3' : 'none');

     //修改层——其实咱们动画的话就修改cx,cy的
    update.attr('cx', d => d.x)
          .attr('cy', d => d.y);
 }



setInterval(() => {
    circleData = getCircleData();
    drawCircle()
}, 2000)
</script>
复制代码

代码解释:

  • 1、 新增getCircleData()方法获取数据;
  • 2、定时器触发drawCircle() 进行绘制;

呈现效果如下: zoo1m.gif

总结

用到得都是之前分享的,动画其实看起来并不是运动的太好,有个过渡效果就更好了,等我分享d3.transition再来完善下动画效果

结束语

  • 大家好 我是三原,多谢您的观看,我会更加努力(๑•̀ㅂ•́)و✧多多总结。
  • 每个方法都是敲出来验证过通过的,有需要可以放心复制。
  • 如果您给帮点个赞👍就更好了 谢谢您~~~~~
  • 期待您的关注
文章分类
前端
文章标签