React Native之使用Animated和Art实现饼状统计图

一、前言

      对于RN图表,有许多功能非常强大的插件,,如native-echarts,react-native-charts-wrapper等,不过今天我们要用animate和art来绘制一个简单的扇形统计图。首先要求我们对Animated和Art有较好的掌握。

Animated和Art的api繁多。本文只介绍与demo相关的API,如下:

ART

  •    Path:表示一个绘制路径
  •    Surface: 相当于画板组件,其他的组件要包括在Surface
  •    Group:可以用Group来包括多个画图组件
  •    Shape: 主要负责path的绘制
  •    Text:文字需要包括在Text中

Animated

  •    Animated.parallel():同时启动多个动画,并行执行
  •    Animated.timing() :推动一个值按照一个过渡曲线而随时间变化。

   Animated.timing参数如下:

  •    duration: 动画的持续时间(毫秒)。默认值为500.
  •    easing: 一个用于定义曲线的渐变函数。默认值为Easing.inOut(Easing.ease).
  •    delay: 开始动画前的延迟时间(毫秒)。默认为0.
     

更多API详见:https://reactnative.cn/docs/animated.html#docsNav

二、绘制饼状图

扇形图的绘制
   ART绘制扇形因为涉及角度计算而比较麻烦,我从一些画图插件源码中提取了一部分代码,仅供实现本文demo:

degreesToRadians(degrees) {
    if (degrees !== 0 && degrees % 360 === 0) { // 360, 720.
      return Math.PI * 2;
    }
    return (degrees * (Math.PI / 180)) % (Math.PI * 2);
  }
//startAngle:相对于12点的起始角度
//endAngle:相对于12点的起始角度
//or:半径
 createArcPath(startAngle, endAngle, or) {
    const path = new Path();
    const linePath = new Path();

    // 弧度角度
    const sa = this._degreesToRadians(startAngle);
    const ea = this._degreesToRadians(endAngle);

    // 以弧度表示的中心弧角
    const ca = sa > ea
      ? ((Math.PI * 2) - sa) + ea
      : ea - sa;

    // 正弦和余弦值
    const ss = Math.sin(sa);
    const es = Math.sin(ea);
    const sc = Math.cos(sa);
    const ec = Math.cos(ea);

    // 对比差异
    const ds = es - ss;
    const dc = ec - sc;
    const dr = 0 - or;

    const large = ca > Math.PI;
    path.move(or + 100 + (or * ss), or - (or * sc) + 30) // 起点
      .arc(or * ds, or * -dc, or, or, large) // 外弧
      .line(dr * es, dr * -ec); 
    

    return path;
  }

注:圆的极坐标方程:x = r*cos(θ), y = r*sin(θ)

 

动态创建扇形:

由于原生的组件并不能识别Value,需要将动画元素用Animated包裹起来,在内部处理数据变更与DOM操作。

声明组件:AnimatedShape = Animated.createAnimatedComponent(Shape);

//创建扇形
  createChartItem = (arr, total, preAngle) => {
    let angle = 360 * (arr[0] / total);//计算改扇形应占比角度
    let startAngle = preAngle;//开始角应为上个扇形的结束角
    let endAngle = startAngle + angle;
    let path = this._createArcPath(startAngle, endAngle, 60, 0);
    return (<Group>
      <AnimatedShape d={path.path} stroke="#396700" strokeWidth={1} fill={arr[1]} scale={this.state.scale}>
    </AnimatedShape>
    </Group>)
  }
  
  createCharts = (data) => {
    let total = 0, //所有项目总的数量
    preAngle = 0;//上一个角度

    for (let i in data) {
      total += data[i][0];
    }

    let outerCircle = Path().moveTo(60, 20).lineTo(30, 20)
    return (<Animated.View>
      <Surface width={300} height={250} style={{ marginTop: 10 }}>
        <Group>
          {data.map((val, index, arr) => {
            if (index) {
              preAngle = preAngle + 360 * (arr[index - 1][0] / total)
            }
            return this.createChartItem(val, total, preAngle)
          })}
        </Group>
      </Surface>
    </Animated.View>)
  }

创建左上角指标图

createTags = (data) => {
    let total = 0;
    for (let i in data) {
      total += data[i][0];
    }
    return (
      <View >
        {data.map((val, index, arr) => {
          return (<View style={styles.tagging}>
            <Animated.View style={{ width: this.state.width, height: 20, backgroundColor: val[1], marginTop: 5, marginRight: 5 }} />
            <Text style={{ lineHeight: 30 }}>{val[2]}:{parseInt(val[0] / total * 100) + "%"}</Text>
          </View>)
        })}
      </View>
    )
  }

三、实现动画

  实现动画分为实现左上角指示图的渐现动画与扇形图缩放动画

toPlay = () => {
    Animated.parallel([
        //左上角指示图的渐现动画
      Animated.spring(this.state.width, {
        toValue: 50,//目标值
        duration: 1200,//时间
        easing: Easing.linear
      }),
        //图表缩放
      Animated.timing(this.state.scale, {
        toValue: 1,
        duration: 1200,
        easing: Easing.linear
      })
    ]).start();
  }

调用:

render() { 
  return (
    <View style={styles.page}>
        <View>{
          this.createTags(this.props.cdata)
        }</View>                
        {
          this.createCharts(this.props.cdata)
        }
      </View>
    )
}


// 数据
//cdata = [[100, '#FFB6C1', '西瓜'], [180, '#00BFFF', '牛奶'], [100, '#3CB371', '菠萝']];

效果图:

总结:

     RN的动画支持度还是挺广的, CSS实现的动画RN基本也可以实现。RN中的动画均为 JavaScript 动画,即通过 JavaScript 代码控制图像的各种参数值的变化,从而产生时间轴上的动画效果。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值