Vue三种3D动态词云实现
点击打开视频讲解,及效果展示
效果图:
一、Vue实现球体词云方式
<template>
<section class="cloud-bed">
<div class="cloud-box">
<span
v-for="(item, index) in dataList"
:key="index"
@click="getDataInfo(item)"
:style="{color:item.color,background:item.bgColor}"
>
{{ item.name }}
</span>
</div>
</section>
</template>
<script>
export default {
name: "word-cloud",
data() {
return {
timer: 50, // 球体转动速率
radius: 0, // 词云球体面积大小
dtr: Math.PI/180, //鼠标滑过球体转动速度
active: false, // 默认加载是否开启转动
lasta: 0, // 上下转动
lastb: 0.5, // 左右转动
distr: true,
tspeed: 0, // 鼠标移动上去时球体转动
mouseX: 0,
mouseY: 0,
tagAttrList: [],
tagContent: null,
cloudContent: null,
sinA: '',
cosA: '',
sinB: '',
cosB: '',
sinC: '',
cosC: '',
dataList: [
{
name: '页面卡顿\白屏',
value: '1',
bgColor:'rgb(57, 193, 207,0.12)',
color:'#39c1cf',
},
{
name: '闪退',
value: '8',
bgColor:'rgb(66, 105, 245,0.12)',
color:'#4269f5',
},
{
name: '登录问题',
value: '9',
bgColor:'rgb(184, 107, 215,0.12)',
color:'#b86bd7',
},
{
name: '功能bug',
value: '3',
bgColor:'rgb(243, 84, 83,0.12)',
color:'#f35453',
},
{
name: '无法收到短信',
value: '6',
bgColor:'rgb(250, 116, 20,0.12)',
color:'#FA7414',
},
{
name: '人脸/指纹认证失败',
value: '10',
bgColor:'rgb(255, 171, 30,0.12)',
color:'#FFAB1E',
},
{
name: '功能建议',
value: '2',
bgColor:'rgb(136, 104, 217,0.12)',
color:'#8868D9',
},
{
name: 'UI/UX',
value: '5',
bgColor:'rgb(42, 184, 230,0.12)',
color:'#2AB8E6',
},
{
name: '导航性',
value: '7',
bgColor:'rgb(117, 133, 162,0.12)',
color:'#7585A2',
},
]
}
},
mounted () {
this.$nextTick(() => {
this.radius = document.querySelector('.cloud-box').offsetWidth / 2
this.initWordCloud()
})
},
beforeDestroy () {
clearInterval(this.timer)
},
methods:{
// 获取点击文本信息
getDataInfo (item) {
console.log(item, 'item')
},
initWordCloud () {
this.cloudContent = document.querySelector('.cloud-box');
this.tagContent = this.cloudContent.getElementsByTagName('span');
for (let i = 0; i < this.tagContent.length; i++) {
let tagObj = {};
tagObj.offsetWidth = this.tagContent[i].offsetWidth;
tagObj.offsetHeight = this.tagContent[i].offsetHeight;
this.tagAttrList.push(tagObj);
}
this.sineCosine(0, 0, 0);
this.positionAll();
this.cloudContent.onmouseover = () => {
this.active=true;
};
this.cloudContent.onmouseout = () => {
this.active=false;
};
this.cloudContent.onmousemove = (ev) => {
let oEvent = window.event || ev;
this.mouseX = oEvent.clientX - (this.cloudContent.offsetLeft + this.cloudContent.offsetWidth/2);
this.mouseY = oEvent.clientY - (this.cloudContent.offsetTop + this.cloudContent.offsetHeight/2);
this.mouseX/= 5;
this.mouseY/= 5;
};
setInterval(this.update, this.timer);
},
positionAll () {
let phi = 0;
let theta = 0;
let max = this.tagAttrList.length;
let aTmp = [];
let oFragment = document.createDocumentFragment();
//随机排序
for (let i=0; i < this.tagContent.length; i++) {
aTmp.push(this.tagContent[i]);
}
aTmp.sort(() => {
return Math.random() < 0.5 ? 1 : -1;
});
for (let i = 0; i < aTmp.length; i++) {
oFragment.appendChild(aTmp[i]);
}
this.cloudContent.appendChild(oFragment);
for(let i = 1; i < max + 1; i++){
if (this.distr) {
phi = Math.acos(-1 + (2 * i - 1) / max);
theta = Math.sqrt(max * Math.PI) * phi;
} else {
phi = Math.random() * (Math.PI);
theta = Math.random() * (2 * Math.PI);
}
//坐标变换
this.tagAttrList[i-1].cx = this.radius * Math.cos(theta) * Math.sin(phi);
this.tagAttrList[i-1].cy = this.radius * Math.sin(theta) * Math.sin(phi);
this.tagAttrList[i-1].cz = this.radius * Math.cos(phi);
this.tagContent[i-1].style.left = this.tagAttrList[i-1].cx + this.cloudContent.offsetWidth / 2 - this.tagAttrList[i-1].offsetWidth / 2 + 'px';
this.tagContent[i-1].style.top = this.tagAttrList[i-1].cy + this.cloudContent.offsetHeight / 2 - this.tagAttrList[i-1].offsetHeight / 2 + 'px';
}
},
update () {
let angleBasicA;
let angleBasicB;
if (this.active) {
angleBasicA = (-Math.min(Math.max(-this.mouseY, -200 ), 200) / this.radius) * this.tspeed;
angleBasicB = (Math.min(Math.max(-this.mouseX, -200 ), 200) / this.radius) * this.tspeed;
} else {
angleBasicA = this.lasta * 0.98;
angleBasicB = this.lastb * 0.98;
}
//默认转动是后是否需要停下
// lasta=a;
// lastb=b;
// if(Math.abs(a)<=0.01 && Math.abs(b)<=0.01)
// {
// return;
// }
this.sineCosine(angleBasicA, angleBasicB, 0);
for(let j = 0; j < this.tagAttrList.length; j++) {
let rx1 = this.tagAttrList[j].cx;
let ry1 = this.tagAttrList[j].cy * this.cosA + this.tagAttrList[j].cz * (-this.sinA);
let rz1 = this.tagAttrList[j].cy * this.sinA + this.tagAttrList[j].cz * this.cosA;
let rx2 = rx1 * this.cosB + rz1 * this.sinB;
let ry2 = ry1;
let rz2 = rx1 * (-this.sinB) + rz1 * this.cosB;
let rx3 = rx2 * this.cosC + ry2 * (-this.sinC);
let ry3 = rx2 * this.sinC + ry2 * this.cosC;
let rz3 = rz2;
this.tagAttrList[j].cx = rx3;
this.tagAttrList[j].cy = ry3;
this.tagAttrList[j].cz = rz3;
let per = 350 / (350 + rz3);
this.tagAttrList[j].x = rx3 * per - 2;
this.tagAttrList[j].y = ry3 * per;
this.tagAttrList[j].scale = per;
this.tagAttrList[j].alpha = per;
this.tagAttrList[j].alpha = (this.tagAttrList[j].alpha - 0.6) * (10/6);
}
this.doPosition();
this.depthSort();
},
doPosition() {
let len = this.cloudContent.offsetWidth/2;
let height = this.cloudContent.offsetHeight/2;
for (let i=0;i < this.tagAttrList.length;i++) {
this.tagContent[i].style.left = this.tagAttrList[i].cx + len - this.tagAttrList[i].offsetWidth/2 + 'px';
this.tagContent[i].style.top = this.tagAttrList[i].cy + height - this.tagAttrList[i].offsetHeight/2 + 'px';
// this.tagContent[i].style.fontSize = Math.ceil(12 * this.tagAttrList[i].scale/2) + 8 + 'px';
this.tagContent[i].style.fontSize = Math.ceil(12 * this.tagAttrList[i].scale/2) +2 + 'px';
this.tagContent[i].style.filter = "alpha(opacity="+100 * this.tagAttrList[i].alpha+")";
this.tagContent[i].style.opacity = this.tagAttrList[i].alpha;
}
},
depthSort(){
let aTmp = [];
for (let i = 0; i < this.tagContent.length; i++) {
aTmp.push(this.tagContent[i]);
}
aTmp.sort((item1, item2) => item2.cz - item1.cz);
for (let i = 0; i < aTmp.length; i++) {
aTmp[i].style.zIndex=i;
}
},
sineCosine (a, b, c) {
this.sinA = Math.sin(a * this.dtr);
this.cosA = Math.cos(a * this.dtr);
this.sinB = Math.sin(b * this.dtr);
this.cosB = Math.cos(b * this.dtr);
this.sinC = Math.sin(c * this.dtr);
this.cosC = Math.cos(c * this.dtr);
}
}
};
</script>
<style scoped>
.cloud-bed {
width: 200px;
height: 200px;
margin: auto;
}
.cloud-box{
position:relative;
margin:20px auto 0px;
width: 100%;
height: 100%;
background: #00000000;
}
.cloud-box span{
position: absolute;
padding: 3px 6px;
top: 0px;
font-weight: bold;
text-decoration:none;
left:0px;
background-image: linear-gradient(to bottom, red, #fff);
background-clip: text;
color: transparent;
width: 50px;
height: 50px;
border-radius: 50%;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
/* line-height: 50px;
overflow:hidden;
white-space: nowrap;
text-overflow: ellipsis; */
}
</style>
二、Vue实现词云
<template>
<div id="app">
<svg style="background-color: black" :width="width" :height="height">
<a class="fontA" v-for="(tag, index) in tags" :key="`tag-${index}`">
<text
:id="tag.id"
:x="tag.x"
:y="tag.y"
:font-size="20 * (600 / (600 - tag.z))"
:fill-opacity="(400 + tag.z) / 600"
@mousemove="listenerMove($event)"
@mouseout="listenerOut($event)"
@click="clickToPage"
>
{{ tag.text }}
</text>
</a>
</svg>
<!-- <svg style="background-color: black" :width="width" :height="height">
<a class="fontA" v-for="(tag, index) in tags" :key="`tag-${index}`">
<text
:id="tag.id"
:x="tag.x"
:y="tag.y"
:font-size="20 * (600 / (600 - tag.z))"
:fill-opacity="(400 + tag.z) / 600"
@mousemove="listenerMove($event)"
@mouseout="listenerOut($event)"
@click="clickToPage"
>
{{ tag.text }}
</text>
</a>
</svg> -->
</div>
</template>
<script>
export default {
name: "word-cloud",
//数据,宽,高,半径,半径一般位宽高的一半。
// props: ["data", "width", "height", "RADIUS"],
data() {
return {
width: 600, //svg宽度
height: 600, //svg高度
tagsNum: 0, //标签数量
RADIUS: 300, //球的半径
speedX: Math.PI / 360 / 1.5, //球一帧绕x轴旋转的角度
speedY: Math.PI / 360 / 1.5, //球-帧绕y轴旋转的角度
tags: [],
data: [
"金晨",
"昆凌",
"李冰冰",
"刘诗诗",
"刘雯",
"刘亦菲",
"林心如",
"林志玲",
"李湘",
"李亚男",
"李若彤",
"李沁",
"李嘉欣",
"林依晨",
"刘嘉玲",
"闰妮",
"李宇春",
"李晟",
"罗震环",
"刘雨欣",
"李波儿",
"黎姿",
"张敏",
"梁小冰",
"黎美娴",
"李彩桦",
"林允儿",
"米雪",
"李菲儿",
"娄艺潇",
"李金铭",
"李萌萌",
],
timer: null,
};
},
computed: {
CX() {
//球心x坐标
return this.width / 2;
},
CY() {
//球心y坐标
return this.height / 2;
},
},
created() {
this.initData();
},
methods: {
// 初始化数据
initData() {
//初始化标签位置
let tags = [];
this.tagsNum = this.data.length;
for (let i = 0; i < this.data.length; 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 = this.data[i];
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.id = i; // 给标签添加id
tags.push(tag);
console.log(tag);
}
this.tags = tags; //让vue替我们完成视图更新
},
// 纵向旋转
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;
}
},
// 运动函数
runTags() {
if (typeof this.timer === "number") {
clearInterval(this.timer);
this.timer = null;
}
let timer = setInterval(() => {
this.rotateX(this.speedX);
this.rotateY(this.speedY);
}, 17);
this.timer = timer;
},
// 监听移入事件
listenerMove(e) {
if (e.target.id) {
clearInterval(this.timer);
}
},
// 监听移出事件
listenerOut(e) {
if (e.target.id) {
this.runTags();
}
},
// 点击事件
clickToPage() {},
},
mounted() {
this.runTags();
},
};
</script>
<style scoped>
.fontA {
fill: #60cae9;
font-weight: bold;
}
.fontA:hover {
fill: #ffffff;
cursor: pointer;
}
</style>
三、jquery实现词云文章来源:https://www.toymoban.com/news/detail-505682.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>3D词云</title>
<style>
.wordCloud__tagBall {
margin: 50px auto;
position: relative;
}
.wordCloud__tag {
display: block;
position: absolute;
left: 0px;
top: 0px;
color: green;
text-decoration: none;
font-size: 15px;
font-family: '微软雅黑';
font-weight: bold;
cursor: pointer;
}
.wordCloud__tag:hover {
color: red !important;
}
.wordCloud__home {
display: flex;
justify-content: center;
}
</style>
</head>
<body>
<div id="main">
<div class="wordCloud__tagBall" style="width: 500px; height: 500px ">
</div>
<div class="wordCloud__home">
<button class="btn0" type="button">降低速度</button>
<button class="btn1" type="button">横向顺时针</button>
<button class="btn2" type="button">横向逆时针</button>
<button class="btn3" type="button">纵向顺时针</button>
<button class="btn4" type="button">纵向逆时针</button>
<button class="btn5" type="button">增加速度</button>
</div>
</div>
<script src = "../node_modules/jquery/dist/jquery.js"></script>
<script src = "../node_modules/jquery/dist/jquery-ui.min.js"></script>
<script>
$(function () {
var myval
var width = 500
var height = 500
var contentEle = []
var data = ['云图', '是个啥', '他啥都不是', '他就是词云', '就是他呆子', '傻子和疯子', '营养快线', '哈哈哈到家', '瑞士军刀', 'DW情侣对表', '清风抽纸', '这一刻更清晰', '债券评级', '呵呵旧宫style', '云图', '是个啥', '他啥都不是', '他就是词云', '就是他呆子', '傻子和疯子', '营养快线', '哈哈哈到家', '瑞士军刀', 'DW情侣对表', '清风抽纸', '这一刻更清晰', '债券评级', '呵呵旧宫style']
var direction = '-1'
var speed = 3000
var color = ['#2D4DB6', '#04B67C', '#D1AF07', '#E27914', '#CB4A4D', '#B02690']
function innit() {
const RADIUSX = (width - 50) / 2
const RADIUSY = (height - 50) / 2
contentEle = []
for (let i = 0; i < data.length; i += 1) {
const k = -1 + (2 * (i + 1) - 1) / data.length
const a = Math.acos(k)
const b = a * Math.sqrt(data.length * Math.PI)
const x = RADIUSX * Math.sin(a) * Math.cos(b)
const y = RADIUSY * Math.sin(a) * Math.sin(b)
const z = RADIUSX * Math.cos(a)
const singleEle = {
x,
y,
z,
style: {},
}
contentEle.push(singleEle)
}
animate()
}
function animate() {
rotateX()
rotateY()
move()
window.requestAnimationFrame(animate)
}
function rotateX() {
const angleX = ['-1', '1'].includes(direction)
? Math.PI / Infinity
: Math.PI / ((Number(direction) / 2) * Number(speed))
const cos = Math.cos(angleX)
const sin = Math.sin(angleX)
contentEle = contentEle.map((t) => {
const y1 = t.y * cos - t.z * sin
const z1 = t.z * cos + t.y * sin
return {
...t,
y: y1,
z: z1,
}
})
}
function rotateY() {
const angleY = ['-2', '2'].includes(direction)
? Math.PI / Infinity
: Math.PI / (Number(direction) * Number(speed))
const cos = Math.cos(angleY)
const sin = Math.sin(angleY)
contentEle = contentEle.map((t) => {
const x1 = t.x * cos - t.z * sin
const z1 = t.z * cos + t.x * sin
return {
...t,
x: x1,
z: z1,
}
})
}
function move() {
const CX = width / 2
const CY = height / 2
contentEle = contentEle.map((singleEle) => {
const { x, y, z } = singleEle
const fallLength = 500
const RADIUS = (width - 50) / 2
const scale = fallLength / (fallLength - z)
const alpha = (z + RADIUS) / (2 * RADIUS)
const left = `${x + CX - 15}px`
const top = `${y + CY - 15}px`
const transform = `translate(${left}, ${top}) scale(${scale})`
const style = {
...singleEle.style,
opacity: alpha + 0.5,
zIndex: parseInt(scale * 100, 10),
transform,
}
return {
x,
y,
z,
style,
}
})
}
function handleRotate(value) {
direction = value
}
function handleSpeed(value) {
const speedObj = {
fast: -50,
slow: 50,
}
speed += speedObj[value]
if (speed === 0) {
speed = 50
}
}
function init() {
var html_ = ''
for (var i = 0; i < data.length; i++) {
let color_ = color[i % color.length]
html_ += '<span class="wordCloud__tag" style="color:' + color_ + ';opacity:' + contentEle[i].style.opacity + ';transform:' + contentEle[i].style.transform + ';zIndex:' + contentEle[i].style.zIndex + '">' + data[i] + '</span>'
}
$('.wordCloud__tagBall').html(html_)
}
innit()
myval = setInterval(() => {
init()
}, 10)
//横向顺时针
$('.btn1').on('click', function () {
handleRotate('-1')
})
//横向逆时针
$('.btn2').on('click', function () {
handleRotate('1')
})
//纵向顺时针
$('.btn3').on('click', function () {
handleRotate('-2')
})
//纵向逆时针
$('.btn4').on('click', function () {
handleRotate('2')
})
//增加速度
$('.btn5').on('click', function () {
handleSpeed('fast')
})
//增加速度
$('.btn0').on('click', function () {
handleSpeed('slow')
})
$('.wordCloud__tagBall').on('mouseenter', function () {
clearInterval(myval)
})
$('.wordCloud__tagBall').on('mouseleave', function () {
myval = setInterval(() => {
init()
}, 10)
})
$('body').on('click', 'span', function () {
console.log($(this)[0].innerHTML);
})
})
</script>
</body>
</html>
若对您有帮助,请点击跳转到B站一键三连哦!感谢支持!!!文章来源地址https://www.toymoban.com/news/detail-505682.html
到了这里,关于Vue三种3D动态词云实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!