vue实现标签云效果的方法详解


Posted in Javascript onAugust 28, 2019

本文实例讲述了vue实现标签云效果的方法。分享给大家供大家参考,具体如下:

闲扯两句

最近想给自己的博客上加上一个3D标签云的效果,用来表示自己博客文章的分组,网上找到了canvas实现的,还有a元素实现的解析3D标签云,我想让标签可以选择和点击,又不想在标签数量较多时操作a标签导致性能问题,于是svg就成了一个不错的选择。

标签初始化

这里实现的核心主要是参考了前面的那篇解析3D标签云的文章,作者给出了源码,讲解也比较通俗易懂。大体来说,整个代码分三步:

  • 根据标签的数量,算出每个标签在球面上分布的x,y,z坐标
  • 根据标签的坐标,将标签绘制出来,x,y坐标通过标签的位置来表示,z坐标通过标签字体的大小和透明度来表示
  • 通过函数根据球的旋转角速度不断计算标签新的x,y坐标,制造出旋转效果
  • 通过mousemove事件,根据鼠标坐标值,改变球旋转的角速度,做出交互效果

贴上代码:

<div id='app' >
    <svg :width='width' :height='height' @mousemove='listener($event)'>
      <a :href="tag.href" rel="external nofollow" v-for='tag in tags'>
        <text :x='tag.x' :y='tag.y' :font-size='20 * (600/(600-tag.z))' :fill-opacity='((400+tag.z)/600)'>{{tag.text}}</text>
      </a>
    </svg>
  </div>

在模板中,借用指令v-for来渲染标签,每个标签上绑定了x,y,font-size(用来表现z轴),fill-opacity(都是与z坐标有关的表达式,用来表现z轴),及text;

data: {
   width:700,//svg宽度
   height:700,//svg高度
   tagsNum:20,//标签数量
   RADIUS:200,//球的半径
   speedX:Math.PI/360,//球一帧绕x轴旋转的角度
   speedY:Math.PI/360,//球-帧绕y轴旋转的角度
   tags: []
 }
 computed:{
   CX(){//球心x坐标
     return this.width/2;
   },
   CY(){//球心y坐标
     return this.height/2;
   }
 },

做好了上面的基础,下面我们来初始化标签数据:

created(){//初始化标签位置
   let tags=[];
   for(let i = 0; i < this.tagsNum; i++){
     let tag = {};
     let k = -1 + (2 * (i + 1) - 1) / this.tagsNum;
     let a = Math.acos(k);
     let b = a * Math.sqrt(this.tagsNum * Math.PI)//计算标签相对于球心的角度
     tag.text = i + 'tag';
     tag.x = this.CX + this.RADIUS * Math.sin(a) * Math.cos(b);//根据标签角度求出标签的x,y,z坐标
     tag.y = this.CY + this.RADIUS * Math.sin(a) * Math.sin(b); 
     tag.z = this.RADIUS * Math.cos(a);
     tag.href = 'https://imgss.github.io';//给标签添加链接
     tags.push(tag);
   }
   this.tags = tags;//让vue替我们完成视图更新
 },

到了这里,我们就算了算坐标,vue完成了视图更新的工作,这时基本上就可以得到一副静态的图像了:
vue实现标签云效果的方法详解
下面就是通过改变每一个tag的x,y的值来使球旋转起来;实现方法是rotateX,rotateY函数:

rotateX(angleX){
    var cos = Math.cos(angleX);
    var sin = Math.sin(angleX);
    for(let tag of this.tags){
      var y1 = (tag.y- this.CY) * cos - tag.z * sin + this.CY;
      var z1 = tag.z * cos + (tag.y- this.CY) * sin;
      tag.y = y1;
      tag.z = z1;
    }
  },
  rotateY(angleY){
    var cos = Math.cos(angleY);
    var sin = Math.sin(angleY);
    for(let tag of this.tags){
      var x1 = (tag.x - this.CX) * cos - tag.z * sin + this.CX;
      var z1 = tag.z * cos + (tag.x - this.CX) * sin;
      tag.x = x1;
      tag.z = z1;
    }
  },

这两个函数就是根据标签原来的坐标和球旋转的角度算出新的坐标,最后在mounted钩子下面,写一个animate函数,不断调用这两个函数,实现旋转动画

mounted(){//使球开始旋转
    setInterval(() => {
      this.rotateX(this.speedX);
      this.rotateY(this.speedY);
    }, 17)
  },

全部代码如下:

<script>
    var app = new Vue({
      el: '#app',
      data: {
        width:700,
        height:700,
        tagsNum:20,
        RADIUS:200,
        speedX:Math.PI/360,
        speedY:Math.PI/360,
        tags: []
      },
      computed:{
        CX(){
          return this.width/2;
        },
        CY(){
          return this.height/2;
        }
      },
      created(){//初始化标签位置
        let tags=[];
        for(let i = 0; i < this.tagsNum; i++){
          let tag = {};
          let k = -1 + (2 * (i + 1) - 1) / this.tagsNum;
          let a = Math.acos(k);
          let b = a * Math.sqrt(this.tagsNum * Math.PI);
          tag.text = i + 'tag';
          tag.x = this.CX + this.RADIUS * Math.sin(a) * Math.cos(b);
          tag.y = this.CY + this.RADIUS * Math.sin(a) * Math.sin(b); 
          tag.z = this.RADIUS * Math.cos(a);
          tag.href = 'https://imgss.github.io';
          tags.push(tag);
        }
        this.tags = tags;
      },
      mounted(){//使球开始旋转
        setInterval(() => {
          this.rotateX(this.speedX);
          this.rotateY(this.speedY);
        }, 17)
      },
      methods: {
        rotateX(angleX){
          var cos = Math.cos(angleX);
          var sin = Math.sin(angleX);
          for(let tag of this.tags){
            var y1 = (tag.y- this.CY) * cos - tag.z * sin + this.CY;
            var z1 = tag.z * cos + (tag.y- this.CY) * sin;
            tag.y = y1;
            tag.z = z1;
          } 
        },
        rotateY(angleY){
          var cos = Math.cos(angleY);
          var sin = Math.sin(angleY);
          for(let tag of this.tags){
            var x1 = (tag.x - this.CX) * cos - tag.z * sin + this.CX;
            var z1 = tag.z * cos + (tag.x-this.CX) * sin;
            tag.x = x1;
            tag.z = z1;
          } 
        },
        listener(event){//响应鼠标移动
          var x = event.clientX - this.CX;
          var y = event.clientY - this.CY;
          this.speedX = x*0.0001>0 ? Math.min(this.RADIUS*0.00002, x*0.0001) : Math.max(-this.RADIUS*0.00002, x*0.0001);
          this.speedY = y*0.0001>0 ? Math.min(this.RADIUS*0.00002, y*0.0001) : Math.max(-this.RADIUS*0.00002, y*0.0001); 
        }
       }
     })
  </script>

完整demo · vue · no vue
vue实现标签云效果的方法详解

总结

vue的数据绑定可以减少我们对dom的操作,而将关注点放在逻辑上面,vue构造函数提供的几个选项可以帮助我们更好的组织代码

希望本文所述对大家vue.js程序设计有所帮助。

Javascript 相关文章推荐
JS代码格式化和语法着色V2
Oct 14 Javascript
jqTransform美化表单
Oct 10 Javascript
js仿3366小游戏选字游戏
Apr 14 Javascript
全面解析Bootstrap中transition、affix的使用方法
May 30 Javascript
使用递归遍历对象获得value值的实现方法
Jun 14 Javascript
js中获取键盘事件的简单实现方法
Oct 10 Javascript
js实现省份下拉菜单效果
Feb 15 Javascript
浅谈vue中使用图片懒加载vue-lazyload插件详细指南
Oct 23 Javascript
JavaScript继承的特性与实践应用深入详解
Dec 30 Javascript
微信小程序网络层封装的实现(promise, 登录锁)
May 08 Javascript
简单了解vue中的v-if和v-show的区别
Oct 08 Javascript
原生js实现自定义滚动条组件
Jan 20 Javascript
微信小程序通过js实现瀑布流布局详解
Aug 28 #Javascript
TypeScript类型声明书写详解
Aug 28 #Javascript
vue服务端渲染操作简单入门实例分析
Aug 28 #Javascript
浅谈对于“不用setInterval,用setTimeout”的理解
Aug 28 #Javascript
Vue的编码技巧与规范使用详解
Aug 28 #Javascript
JS开发自己的类库实例分析
Aug 28 #Javascript
详解Vue 换肤方案验证
Aug 28 #Javascript
You might like
PHP 多维数组排序实现代码
2009/08/05 PHP
PHP 强制性文件下载功能的函数代码(任意文件格式)
2010/05/26 PHP
PHP实现PDO的mysql数据库操作类
2014/12/12 PHP
WordPress主题制作中自定义头部的相关PHP函数解析
2016/01/08 PHP
Zend Framework教程之Zend_Db_Table表关联实例详解
2016/03/23 PHP
PHP面向对象多态性实现方法简单示例
2017/09/27 PHP
jQuery的一些注意
2006/12/06 Javascript
JS解密入门 最终变量劫持
2008/06/25 Javascript
超轻量级的基于jquery的三级展开列表
2011/04/26 Javascript
实例分析javascript中的call()和apply()方法
2014/11/28 Javascript
多功能jQuery树插件zTree实现权限列表简单实例
2016/07/12 Javascript
canvas实现探照灯效果
2017/02/07 Javascript
JS实现点击链接切换显示隐藏内容的方法
2017/10/19 Javascript
JS开发中基本数据类型具体有哪几种
2017/10/19 Javascript
vue 使用element-ui中的Notification自定义按钮并实现关闭功能及如何处理多个通知
2019/08/17 Javascript
JS中this的4种绑定规则详解
2020/02/04 Javascript
浅谈vant组件Picker 选择器选单选问题
2020/11/04 Javascript
[02:18]DOTA2英雄基础教程 育母蜘蛛
2014/01/20 DOTA
[01:39]2014DOTA2国际邀请赛 Newbee经理CU专访队伍火力全开
2014/07/15 DOTA
[50:50]完美世界DOTA2联赛PWL S3 Galaxy Racer vs Phoenix 第一场 12.10
2020/12/13 DOTA
Python实现从脚本里运行scrapy的方法
2015/04/07 Python
python中threading超线程用法实例分析
2015/05/16 Python
python查看FTP是否能连接成功的方法
2015/07/30 Python
Tensorflow卷积神经网络实例进阶
2018/05/24 Python
Atom的python插件和常用插件说明
2018/07/08 Python
Python 获取中文字拼音首个字母的方法
2018/11/28 Python
使用Python给头像加上圣诞帽或圣诞老人小图标附源码
2019/12/25 Python
解决python和pycharm安装gmpy2 出现ERROR的问题
2020/08/28 Python
python 带时区的日期格式化操作
2020/10/23 Python
德国游戏机商店:Konsolenkost
2019/12/08 全球购物
财政专业求职信范文
2014/02/19 职场文书
聚美优品陈欧广告词
2014/03/14 职场文书
小学教师师德演讲稿
2014/05/06 职场文书
计算机科学与技术专业求职信
2014/09/03 职场文书
逃课打麻将检讨书
2014/10/05 职场文书
新员工入职感言范文!
2019/07/04 职场文书