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 相关文章推荐
JavaScript避免代码的重复执行经验技巧分享
Apr 17 Javascript
JQuery悬停控制图片轮播——代码简单
Aug 05 Javascript
图文详解Heap Sort堆排序算法及JavaScript的代码实现
May 04 Javascript
原生js的数组除重复简单实例
May 24 Javascript
几种响应式文字详解
May 19 Javascript
微信小程序实现顶部选项卡(swiper)
Jun 19 Javascript
微信小程序实现多宫格抽奖活动
Apr 15 Javascript
仿京东快报向上滚动的实例
Dec 13 Javascript
总结js函数相关知识点
Feb 27 Javascript
js实现抽奖的两种方法
Mar 19 Javascript
Antd下拉选择,自动匹配功能的实现
Oct 24 Javascript
JavaScript async/await原理及实例解析
Dec 02 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 网页游戏开发入门教程一(webgame+design)
2009/10/26 PHP
PHP批量去除BOM头内容信息代码
2016/03/11 PHP
PHP字符串逆序排列实现方法小结【strrev函数,二分法,循环法,递归法】
2017/01/13 PHP
Yii框架日志记录Logging操作示例
2018/07/12 PHP
禁止JQuery中的load方法装载IE缓存中文件的方法
2009/09/11 Javascript
IE6下opacity与JQuery的奇妙结合
2013/03/01 Javascript
js实现横向百叶窗效果网页切换动画效果的方法
2015/03/02 Javascript
JS实现动态移动层及拖动浮层关闭的方法
2015/04/30 Javascript
基于JavaScript代码实现微信扫一扫下载APP
2015/12/30 Javascript
Js查找字符串中出现次数最多的字符及个数实例解析
2016/09/05 Javascript
jQuery 如何实现一个滑动按钮开关
2016/12/01 Javascript
angular2+node.js express打包部署的实战
2017/07/27 Javascript
Kindeditor单独调用单图上传增加预览功能的实例
2017/07/31 Javascript
React-Native使用Mobx实现购物车功能
2017/09/14 Javascript
详解webpack 最简打包结果分析
2019/02/20 Javascript
jQuery实现input[type=file]多图预览上传删除等功能
2019/08/02 jQuery
python自动化测试之连接几组测试包实例
2014/09/28 Python
详解Python编程中对Monkey Patch猴子补丁开发方式的运用
2016/05/27 Python
python实现对excel进行数据剔除操作实例
2017/12/07 Python
Python3实现的回文数判断及罗马数字转整数算法示例
2019/03/27 Python
Python 文件操作之读取文件(read),文件指针与写入文件(write),文件打开方式示例
2019/09/29 Python
python框架Django实战商城项目之工程搭建过程图文详解
2020/03/09 Python
Html5 页面适配iPhoneX(就是那么简单)
2019/09/05 HTML / CSS
remote接口和home接口主要作用
2013/05/15 面试题
酒店工作职员求职简历的自我评价
2013/10/23 职场文书
演讲比赛策划方案
2014/06/11 职场文书
计划生育证明格式范本
2014/09/12 职场文书
我的大学四年规划书范文2014
2014/09/26 职场文书
学校师德师风整改方案
2014/10/28 职场文书
家庭财产分割协议范文
2014/11/24 职场文书
工作会议简报
2015/07/20 职场文书
《悲惨世界》:比天空更广阔的是人的心灵
2020/01/16 职场文书
JavaScript实现队列结构过程
2021/12/06 Javascript
Vue.js中v-bind指令的用法介绍
2022/03/13 Vue.js
Win11怎么把合并的任务栏分开 Win11任务栏合并分开教程
2022/04/06 数码科技
解决Oracle数据库用户密码过期
2022/05/11 Oracle