vue3 词云
<script setup lang="ts"> defineOptions({ name: 'rightTop', }); let timer: any = null; const wrapperRef = ref() const tagRef: any = ref([]) const rotateAngleX = ref() const rotateAngleY = ref() const option = ref({ radius: 80, // 滚动半径,单位px maxFont: 24, // 最大字体大小 color: 'white', // 字体颜色。为空时随机 rotateAngleXbase: 600, // 默认旋转速度基数,数越小速度越快 rotateAngleYbase: 600, backgrounds: ['0 0 24px 3px #b7a47b inset', '0 0 24px 3px rgba(46, 144, 255.2) inset', '0 0 24px 3px #218a88 inset', '0 0 24px 3px #0065a9 inset'], hover: true, // 是否开启悬浮联动 }) const tagList = ref([]) const data = ref([ { name: '基层减负', num: 100, }, { name: '收费政策', num: 20, }, { name: '面子工程', num: 88, }, { name: '形式主义', num: 30, }, { name: '节庆活动', num: 100, }, { name: '一刀切', num: 20, }, { name: '展会', num: 20, }, { name: '一票否决', num: 20, }, { name: '文件多', num: 20, } ]) const getMineRef = (el: any, index: number) => { if (el) { tagRef.value[index] = el; } }; function _initTags() { rotateAngleX.value = Math.PI / option.value.rotateAngleXbase; rotateAngleY.value = Math.PI / option.value.rotateAngleYbase; // 鼠标悬浮改变转速和方向 if (option.value.hover) { nextTick(() => { wrapperRef.value.onmousemove = function (e: any) { rotateAngleY.value = (e.pageX - this.offsetLeft - this.offsetWidth / 2) / 10000; rotateAngleX.value = -(e.pageY - this.offsetTop - this.offsetHeight / 2) / 10000; }; }) } else { wrapperRef.value.onmousemove = null; } for (let i = 0, length = data.value.length; i < length; i++) { // 获取球面上均匀的点的经纬度 θ = arccos( ((2*num)-1)/all - 1); Φ = θ*sqrt(all * π); let angleX = Math.acos((2 * (i + 1) - 1) / length - 1); let angleY = angleX * Math.sqrt(length * Math.PI); // 根据经纬度获取点的坐标,球中心的点坐标是 (0,0,0) x=r*sinθ*cosΦ y=r*sinθ*sinΦ z=r*cosθ; const x = option.value.radius * Math.sin(angleX) * Math.cos(angleY); const y = option.value.radius * Math.sin(angleX) * Math.sin(angleY); const z = option.value.radius * Math.cos(angleX); nextTick(() => { if (option.value.color) { tagRef.value[i].style.color = option.value.color; } else { // 随机颜色 tagRef.value[i].style.color = 'rgb(' + Math.round(255 * Math.random()) + ',' + Math.round(255 * Math.random()) + ',' + Math.round(255 * Math.random()) + ')'; } // 每个标签对象都有四对值 let tag: any = { x: x, y: y, z: z, ele: tagRef.value[i], }; tagList.value.push(tag); }) } timer = setInterval(function () { for (var i = 0; i < tagList.value.length; i++) { rotateX(tagList.value[i]); rotateY(tagList.value[i]); setPosition( tagList.value[i], option.value.radius, option.value.maxFont ); setBoxShadow(tagList.value[i], i) } }, 20); } _initTags() function setBoxShadow(tag: any, i: any) { let length = option.value.backgrounds.length tag.ele.style.boxShadow = option.value.backgrounds[i % length] } function setPosition(tag: any, r: any, maxFont: any) { // 设置每个标签的坐标位置和字体大小以及透明度 if (wrapperRef.value) { tag.ele.style.transform = 'translate(' + (tag.x + wrapperRef.value.offsetWidth / 2 - tag.ele.offsetWidth / 2) + 'px,' + (tag.y + wrapperRef.value.offsetHeight / 2 - tag.ele.offsetHeight / 2) + 'px)'; tag.ele.style.opacity = tag.z / r / 2 + 0.7; // tag.ele.style.fontSize = (tag.z / r / 2 + 0.5) * maxFont + 'px'; } } function rotateX(tag: any) { var cos = Math.cos(rotateAngleX.value); var sin = Math.sin(rotateAngleX.value); var y1 = tag.y * cos - tag.z * sin; var z1 = tag.y * sin + tag.z * cos; tag.y = y1; tag.z = z1; } function rotateY(tag: any) { var cos = Math.cos(rotateAngleY.value); var sin = Math.sin(rotateAngleY.value); var x1 = tag.z * sin + tag.x * cos; var z1 = tag.z * cos - tag.x * sin; tag.x = x1; tag.z = z1; } function dbclickTag() { if (timer) { clearInterval(timer); timer = null; } else { timer = setInterval(function () { for (var i = 0; i < tagList.value.length; i++) { rotateX(tagList.value[i]); rotateY(tagList.value[i]); setPosition( tagList.value[i], option.value.radius, option.value.maxFont ); } }, 20); } } function clickTag(item: any) { // this.$emit('clickTag', item); } onUnmounted(() => { if (timer) { clearInterval(timer); } }); </script> <template> <div class="cont-box w-full h-full"> <div class="tag-cloud" ref="wrapperRef"> <p v-for="(item, index) in data" :key="index" :ref="el => getMineRef(el, index)" @click="clickTag(item)" @dblclick="dbclickTag()"> <!-- {{ item.num }}家 <br /> <br /> --> <span class="num">{{ item.num }}</span> 家 <br /> {{ item.name }} </p> </div> </div> </template> <style lang="less" scoped> .cont-box { background: url("@/assets/images/home/hot-bg.png") no-repeat center center; background-size: contain; .tag-cloud { width: 100%; height: 100%; position: relative; color: #333; margin: 0 auto; text-align: center; } .tag-cloud p { position: absolute; top: 0px; left: 0px; color: white; font-family: Arial; text-decoration: none; margin: 0 10px 15px 0; text-align: center; font-size: 15px; padding: 4px 9px; display: inline-block; border-radius: 50%; width: 80px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; height: 80px; line-height: 18px; background: rgba(50, 85, 113, 0.3); box-shadow: 0 0 24px 3px rgba(46, 144, 255.2) inset; padding-top: 19px; } .num { font-size: 20px; padding-top: 10px; font-weight: bold; } } </style>