likes
comments
collection

[业务实践]vue 3d球体标签云

作者站长头像
站长
· 阅读数 15

前言

经过一段时间编码,我的博客终于上线了。前端用的nuxt,后端使用eggjs,其中有一个3D标签云的效果,将这个标签云的代码分享给大家

先看效果

[业务实践]vue 3d球体标签云

基础知识

拿出高中的数学知识:

[业务实践]vue 3d球体标签云

直角坐标与球面坐标的关系:

x=rsin⁡(φ)cos⁡(θ)y=rsin⁡(φ)sin⁡(θ)z=rcos⁡(φ)x=r\sin\left(\varphi\right)\cos\left(\theta\right) \\y=r\sin\left(\varphi\right)\sin\left(\theta\right) \\z=r\cos\left(\varphi\right)x=rsin(φ)cos(θ)y=rsin(φ)sin(θ)z=rcos(φ)

步骤:

1.初始化点坐标分布

  • 定义基本参数
 data() {
   return {
     fontSize: 12,//当元素在X轴上字体大小
     tagEle: null,//初始化所有未分布的元素
     paper: null,//背景元素
     RADIUS: 80,//球体半径
     fallLength: 160,//球体直径,其实用于计算字体大小,如元素在Y轴最大值即160上,字体显示最大24px
     tags: [], //经过计算沿球表面均匀分布的元素
     angleX: 0,//沿X轴旋转角度
     angleY: 0,//沿Y轴旋转角度
     x: 0,
     y: 0,
     z: 0,
     CX: 0,//球体中心x轴
     CY: 0,//球体中心y轴
   }
 }
  • 利用数学公式法均匀分布各个元素:
分别算出φ及ϕ,代码中用a,b表示\mathrm{分别算出}\varphi 及\phi,\mathrm{代码中用}a,b\mathrm{表示}\\φϕ,a,b
     for (let i = 0; i < this.tagEle.length; i++) {
        let k = (2 * (i + 1) - 1) / this.tagEle.length - 1
        let a = Math.acos(k)
        let b = a * Math.sqrt(this.tagEle.length * Math.PI)
      }
  • 计算三维坐标:
     for (let i = 0; i < this.tagEle.length; i++) {
        let k = (2 * (i + 1) - 1) / this.tagEle.length - 1
        let a = Math.acos(k)
        let b = a * Math.sqrt(this.tagEle.length * Math.PI)
        this.x = this.RADIUS * Math.sin(a) * Math.cos(b)
        this.y = this.RADIUS * Math.sin(a) * Math.sin(b)
        this.z = this.RADIUS * Math.cos(a)
      }
  • 缓存坐标,将元素移动至各个坐标轴上:
     for (let i = 0; i < this.tagEle.length; i++) {
        let k = (2 * (i + 1) - 1) / this.tagEle.length - 1
        let a = Math.acos(k)
        let b = a * Math.sqrt(this.tagEle.length * Math.PI)
        this.x = this.RADIUS * Math.sin(a) * Math.cos(b)
        this.y = this.RADIUS * Math.sin(a) * Math.sin(b)
        this.z = this.RADIUS * Math.cos(a)
        let tag = { ele: this.tagEle[i], x: this.x, y: this.y, z:this.z}
        this.tags.push(tag)
        this.move(tag)
      }
   move(tag) {
      let { ele, x, y, z } = tag
      let scale = this.fallLength / (this.fallLength - z)
      let alpha = (z + this.RADIUS) / (2 * this.RADIUS)
      //让后面的元素文字小一些突出3d感
      ele.style.fontSize = this.fontSize * scale + 'px'
      //让后面的元素淡一些突出3d感
      ele.style.opacity = alpha + 0.5  
      ele.style.filter = 'alpha(opacity = ' + (alpha + 0.5) * 100 + ')'
      ele.style.zIndex = parseInt(scale * 100)
      //将元素移动到相应的点坐标上
      ele.style.transform = `translate(${
        x + this.CX - ele.offsetWidth / 2
      }px, ${y + this.CY - ele.offsetHeight / 2}px)`
    }

初始化元素

[业务实践]vue 3d球体标签云

2.球旋转后x,y,z轴变化1

  • 定义随X,Y轴函数旋转函数(设由X轴方向旋转角度参数angleX、angleY/次):
    rotateX() {
      let _this = this
      let cos = Math.cos(this.angleX)
      let sin = Math.sin(this.angleX)
      this.tags.forEach((tag) => {
        let y1 = tag.y * cos - tag.z * sin
        let z1 = tag.z * cos + tag.y * sin
        tag.y = y1
        tag.z = z1
      })
    },
​
    rotateY() {
      let _this = this
      let cos = Math.cos(this.angleY)
      let sin = Math.sin(this.angleY)
      this.tags.forEach((tag) => {
        let x1 = tag.x * cos - tag.z * sin
        let z1 = tag.z * cos + tag.x * sin
        tag.x = x1
        tag.z = z1
      })
    }
  • 转动后重新移动元素布置,定义动画函数
   animate() {
      let _this = this
      setInterval(() => {
        _this.rotateX()
        _this.rotateY()
        _this.tags.forEach((tag) => {
          _this.move(tag)
        })
      }, 30)
    },

3.初始化vue mouted生命周期

    this.tagEle = document.querySelectorAll('.tag')
    this.paper = document.querySelector('.tagBall')
    //球中心点取背景元素的中间
    this.CX = this.paper.offsetWidth / 2
    this.CY = this.paper.offsetHeight / 2
    //初始化旋转角
    this.angleX = ((Math.random() - 0.5) * Math.PI) / 250
    this.angleY = ((Math.random() - 0.5) * Math.PI) / 250
    this.init()
    this.animate()