3D标签云的简单实现

3D标签云的实现

  最近在做一些小Demo,看到了3D标签云觉得很酷炫,就学着做了一下。虽然看起来有点难,但是分解了看其实还是很普通的js运动的实现。
  效果预览https://chestnut647.github.io/Demo/cloudTag/


分解

  1. 球体的实现
    • 通过设置角度(α, β)确定标签的坐标位置
    • 球体的实现是通过字体大小和透明度来实现的,远处的标签更小、透明度更低
  2. 球体的旋转
    • 开启定时器,定时通过鼠标的位置来改变球体各个标签位置来达到旋转动画的效果

球体的相关公式

标签分布

https://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Spherical_with_grid.svg/600px-Spherical_with_grid.svg.png
  在 球体坐标系中, x y z是通过三个值来确定,即以下三个公式

       x = r * sinθ * cosφ y = r * sinθ * sinφz= r * cos θ

  因此我们可以通过对θ φ取随机值,从而把标签分布在球体的表面上,在标签分布的时候,不能随意分布,要均匀分布。引入以下两个公式:

      θ = arccos((2 * i - 1)/ len - 1); φ = θ * sqrt(len * PI)

  其中,(2 * i - 1)/len - 1 是-1 到 1 中均匀分布的数列, 然后用arccos值得到均匀分布的θ值,第二个式子是θ的等差数列

球体旋转

  旋转公式:

      x1 = cosθ * x - sinθ * y; y1 = cosθ * y + sinθ * x; 

  其中x y 是旋转前坐标, θ是逆时针旋转角度, x1 y1是旋转后的坐标,参考来源 http://www.cnblogs.com/ywxgod/archive/2010/08/06/1793609.html


代码实现

html部分

使用一个wrap来包裹住所有的标签
- wrap是一个相对定位,用来代表圆心
- a标签是绝对定位, 在圆上的坐标x y 即为a的left 和top 值

<div id="wrap">
<a href="#" class="tag">前端学习</a>
</div>
#wrap {
    position: relative;
    left: 50%;
    top:  50%;
    }
    #wrap .tag {
        display: inline-block;
        position:absolute;
        height: 50px;
        line-height: 50px;
        text-decoration: none;
    }

js部分

  js部分总共有两个对象
- 初始化对像: 用来对整个标签云初始化,创建所有标签对象, 同时还有控制整个标签云运动的函数
- 标签对象: 每个标签对应一个标签对象,通过初始化对象得到的x, y, 值 把自己放入标签云中适当的位置,同时通过z值设置自己的字体大小和透明度

初始化对象
function Initialization(options) {
    this.options = options;
    this.container = options.container;
    this.dataArr = options.data;
    this.init();
}

Initialization.prototype.init = function() {
        let len = this.dataArr.length;
        let newTags = [];
        for(let i = 0; i < len; i++) {
            var angleA =  Math.acos((2*(i+1) -1)/len - 1); 
            var angleB = angleA * Math.sqrt(len *Math.PI);
            var z = R * Math.cos(angleA);
            var y = R * Math.sin(angleA)*Math.sin(angleB);
            var x = R * Math.sin(angleA) *Math.cos(angleB);
            var color = '#' +Math.floor(Math.random()*0xffffff).toString(16);
            this.dataArr[i].style.color = color;
            var newtag = new Tag(this.dataArr[i], x,y, z, this.options);
            newtag.move();
            newTags.push(newtag);
            this.animate();
      }
      this.newTags = newTags;
}


Initialization.prototype.rotateX =  function() {
     let cos = Math.cos(angleX),
        sin = Math.sin(angleX);
    this.newTags.forEach((tag) => {
        let y = tag.y * cos - tag.z * sin,
            z = tag.z*cos + tag.y * sin;
        tag.y = y;
        tag.z = z;
    });

}

Initialization.prototype.rotateY = function() {
    let cos = Math.cos(angleY),
        sin = Math.sin(angleY);
    this.newTags.forEach((tag) => {
        let x = tag.x * cos - tag.z * sin,
            z = tag.z*cos + tag.x * sin;
        tag.x = x;
        tag.z = z;
    });
}
Initialization.prototype.animate = function() {
    var that = this;
    setInterval(function() {
        that.rotateX();
        that.rotateY();
        that.newTags.forEach((tag)=> {
            tag.move();

        })
    }, 20);
}
标签对象
function Tag(data, x, y, z, options) {
    this.options = options;
    this.dataArr = options.data;
    this.data = data;
    this.x = x;
    this.y = y;
    this.z = z;
}
Tag.prototype.move = function() {
    var len = this.dataArr.length;
    var scale = _focalLength /(_focalLength  - this.z);
    var alpha = (this.z +  R)/(2 * R);
    this.data.style.left =  this.x + 'px';
    this.data.style.top =  this.y + 'px';
    this.data.style.fontSize = 14 * scale + 'px';
    this.data.style.opacity = alpha + 0.5;
}
主程序
const  _baseAngle = Math.PI /  360,
        R = 200;
let speed = 1,
    angleX = speed * _baseAngle,
    angleY = -speed * _baseAngle,
    _focalLength = R * 1.5;
window.onload = function() {
    let tags = document.getElementsByTagName('a');
    let wrap = document.getElementById('wrap');

    let options = {
        data: tags,
        container: wrap
    }
    let tagCloud = new Initialization(options);
    document.addEventListener('mousemove', function(e) {

         angleY = 2 * (e.clientX/ document.body.getBoundingClientRect().width- 0.5) * speed  * _baseAngle;
         angleX = 2 * (e.clientY/ document.body.getBoundingClientRect().height - 0.5) * speed  * _baseAngle;


    })
}

代码地址

详细代码地址见我的github
https://github.com/chestnut647/Demo/tree/master/cloudTag

发布了2 篇原创文章 · 获赞 2 · 访问量 2821
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览