封装的three.js饼图vue组件

基于three.js的3d饼图

使用

<template>
  <div id="app">
    <three-pie
      ref="pie"
      :data="pieData"
      :delay="3000"
      :colors="[0x3da6ff, 0x2ceef5, 0xf2c62a, 0x26c7b7, 0xff823b]"
      :opacity="0.7"
      class="pieCanvas"
    >
      <template v-slot:default="slotProps">
        <div class="pieCanvas-content">
          <div class="pieCanvas-content-value">
            <count-to
              ref="countTo"
              :startVal="0"
              :endVal="getNumber(slotProps)"
              :decimals="2"
              :duration="1000"
              :autoplay="true"
            ></count-to>
            %
          </div>
          <div class="pieCanvas-content-name">{{ slotProps.data.name }}</div>
        </div>
      </template>
    </three-pie>
    
  </div>
</template>
<script>
import ThreePie from '@/components/threePie'
import CountTo from '@/components/CountTo'
export default {
  components: {
    ThreePie,
    CountTo,
  },
  data() {
    return {
      pieData: [
        {
          name: '搜索引擎',
          value: 22,
        },
        {
          name: '邮件',
          value: 23,
        },
        {
          name: '广告',
          value: 27,
        },
        {
          name: '视频广告',
          value: 19,
        },
        {
          name: '地推',
          value: 25,
        },
      ],
    }
  },
  methods: {
    getNumber(slotProps) {
      return Number(((slotProps.data.value / slotProps.data.count) * 100).toFixed(2))
    }
  },
}
</script>
<style lang="scss">
* {
  padding: 0;
  margin: 0;
}
html,
body,
#app {
  background-color: #000;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#app {
  display: flex;
  justify-content: center;
  align-items: center;
}
.pieCanvas {
  background-color: #111;
  width: 400px;
}
.pieCanvas-content {
  width: 100px;
  height: 100px;
  margin-bottom: 30px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #fff;
  font-size: 14px;
  &-value {
    font-size: 24px;
    font-weight: bold;
    text-shadow: 0 0 10px rgb(0 0 0);
  }
  &-name {
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: #999;
    text-align: center;
  }
}
.top-tools {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  background: #222;
  color: #fff;
  a {
    padding: 10px 10px;
  }
}
</style>

部分代码解析

1.圆环旋转

外层装饰是由3个贴图组合而成,中间圆环旋转使用了TWEEN,沿着z轴旋转

// 创建饼图外层3个圆环
this.createPlane({
        url: 'texture/ring2.png',
        width: 5,
        position: new THREE.Vector3(0, 0, -0.01),
        color: '#00ffff',
      })
      this.createPlane({
        url: 'texture/ring3.png',
        width: 6.5,
        position: new THREE.Vector3(0, 0, -0.02),
        color: '#00ffff',
      })
      this.createPlane({
        url: 'texture/ring4.png',
        width: 5.5,
        position: new THREE.Vector3(0, 0, -0.03),
        animate: true,
        color: '#00ffff',
      })


createPlane(opt) {
      let defaultOpt = {
        url: 'texture/ring1.png',
        width: 5.5,
        z: 0,
        position: new THREE.Vector3(0, 0, 0),
        animate: false,
        color: null,
      }
      let options = Object.assign(defaultOpt, opt)
      const geometry = new THREE.PlaneBufferGeometry(options.width, options.width)
      const material = new THREE.MeshBasicMaterial({
        map: this.getTexture(options.url),
        transparent: true,
        side: THREE.DoubleSide,
        depthTest: false,
      })
      if (options.color) {
        material.color = new THREE.Color(options.color)
      }
      const mesh = new THREE.Mesh(geometry, material)
      mesh.position.copy(options.position)
      mesh.rotation.x = (-1 * Math.PI) / 2
       // 如果设置了动画,沿Z轴旋转
      if (options.animate) {
        new TWEEN.Tween({ z: 0 })
          .to({ z: 2 * Math.PI })
          .repeat(Infinity)
          .onUpdate(obj => {
            mesh.rotation.z = obj.z
          })
          .start()
      }
      scene.add(mesh)
    },

2.饼图

// 通过数据,计算出百分比,再计算出每个环的开始角度和起始角度
createPie() {
      let startAngle = 0
      let endAngle = 0

      for (var i = 0; i < this.data.length; i++) {
        let percent = this.data[i].value / this.count
       
        if (i == 0) {
          startAngle = 0
        } else {
          startAngle = endAngle + 0.0001
        }
        endAngle = endAngle + 2 * Math.PI * percent - 0.0001

        let ring = this.addRing({
          startAngle: startAngle,
          endAngle: endAngle,
          color: this.colors[i % this.colors.length],
        })
        ring.name = 'ring' + i
        pieGroup.add(ring)
      }
      scene.add(pieGroup)
      this.chooseRing(this.activeIndex, true)

      this.timer = setInterval(() => {
        this.loopChange()
      }, this.delay)
    },

获取完整源码可咸鱼搜索:three.js饼图

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hi-ff

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值