vue3+css3 实现 一个不一样的词云效果

一、效果展示

啦啦啦~ 终于把这个散开的词云效果给搞出来了,废话不多说,上效果;

二、技术栈

vue3+vue-cli+css3

用css3 的animation动画 实现往外扩展循环播放的效果

随机生成每个元素的动画时间和延迟时间以及缩放大小

利用css3的animation-play-state: paused实现鼠标悬浮动画停止的效果

三、实现过程

mouseenter 鼠标移入添加了stop类名停止动画,mouseleave鼠标移出动画继续

html 复制代码
 <ul id="cloudCon">
        <li
          :style="{ animation: item.styleCon }"
          v-for="(item, index) in hotWord"
          :key="`${currtIndex}_${index}`"
          :class="{ stop: item.isStop }"
          @click="handleMes(item.name)"
          @mouseenter="item.isStop = true"
          @mouseleave="item.isStop = false"
        >
         {{ item.name }}
        </li>
  </ul>
js 复制代码
// 热搜词
let hotWord = reactive([
  { name: "万事如意万事如意万事如意" },
  { name: "事事如意" },
  { name: "万事亨通" },
  { name: "一帆风顺" },
  { name: "万事大吉" },
  { name: "吉祥如意吉祥如意" },
  { name: "步步高升" },
  { name: "步步登高" },
  { name: "三羊开泰" },
  { name: "得心应手" },
  { name: "财源广进" },
  { name: "阖家安康阖家安康" },
  { name: "万事如意" },
  { name: "事事如意 " },
  { name: "万事亨通万事亨通" },
]);
// 随机生成的
let moveAry = reactive([]); 
onMounted(() => {
  setStyle();
  setCla();
  window.addEventListener("resize", () => {
    // 监听页面尺寸变化
    setStyle();
    setCla();
  });
});
//随机生成每个标签的移动样式并动态添加到页面中
 const setStyle = () => {
  let runkeyframes = "";
  // 往不同的方向
  let d = ["+", "-"];
  // 动画范围
  let w = (document.getElementById("cloudCon").offsetWidth - 100) / 2;
  let h = (document.getElementById("cloudCon").offsetHeight - 40) / 2;
  
  for (var i = 0; i < hotWord.length; i++) {
    let ts = (Math.random() + 0.4).toFixed(2); 
    let th = d[Math.round(Math.random())]; //随机取符号
    let x = Math.ceil(Math.random() * w);
    let x1 = x;
    x1 = th == "+" && x + 200 > w ? x - 200 : x;
    let tx = th + x1;//横向平移translateX
    let y = Math.ceil(Math.random() * h).toFixed(2);
    let y1 = y - 87 * ts > h ? h : y;
    let ty = d[Math.round(Math.random())] + y1; //纵向平移translateY
    
    runkeyframes += ` @keyframes move${i + 1}{
          0% {
            opacity: 0;
            transform: translateX(0) translateZ(-300px) scale(0.2);
          }
          25% {
            opacity: 1;
          }
          50% {
             opacity: 1;
            transform: translateX(${tx}px) translateY(${ty}px) translateZ(0)
          scale(${ts});
          }
         90% {
            opacity: 1;
            transform: translateX(${tx}px) translateY(${ty}px) translateZ(0)
          scale(${ts});
          }
           100% {
        opacity: 0;
        transform: translateX(${tx}px) translateY(${ty}px) translateZ(0)
          scale(${ts});
      }
    }`;
  }

  // 创建style标签
  const style = document.createElement("style");
  // 设置style属性
  style.type = "text/css";
  // 将 keyframes样式写入style内
  style.innerHTML = runkeyframes;
  // 将style样式存放到head标签
  document.getElementsByTagName("head")[0].appendChild(style);
};
// 给hotWord添加styleCon
const setCla = () => {
  filterItem();
  hotWord.map((item,index) => {
    let ts = (Math.random() * 10).toFixed(2);
    item.styleCon = ` move${moveAry[index]} 10s ease-in-out infinite ${ts}s`;
  });
};
//随机生成 hotWord.length个不同的随机整数,以免位置重复相互覆盖
const filterItem = () => {
  var count = hotWord.length;
  var originalArray = new Array();
  for (var i = 0; i < count; i++) {
    originalArray[i] = i + 1;
  }
  for (var num, i = 0; i < count; i++) {
    do {
      num = Math.floor(Math.random() * count);
    } while (originalArray[num] == null);
    moveAry.push(originalArray[num]);
    originalArray[num] = null;
  }
};
less 复制代码
 #cloudCon {
    width: 100%;
    height: calc(~"100% - 84px");
    margin-top: 84px;
    position: relative;
    li {
      background: rgba(255, 255, 255, 0.76) !important;
      padding: 6px 9px 6px 19.5px;
      color: #1135f1;
      font-size: 16px;
      display: inline-block;
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      transform-style: preserve-3d;
      max-width: 200px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;

      opacity: 0;

      &::after {
        content: "";
        width: 6px;
        height: 6px;
        background: #fea41d;
        position: absolute;
        left: 9px;
        top: 12px;
      }
      &.stop {
        animation-play-state: paused !important; /* 暂停动画 */
      }
    }
  }
}
相关推荐
sunshine6411 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻2 小时前
Vue(四)
前端·javascript·vue.js
不是鱼3 小时前
构建React基础及理解与Vue的区别
前端·vue.js·react.js
开心工作室_kaic3 小时前
springboot476基于vue篮球联盟管理系统(论文+源码)_kaic
前端·javascript·vue.js
川石教育3 小时前
Vue前端开发-缓存优化
前端·javascript·vue.js·缓存·前端框架·vue·数据缓存
搏博3 小时前
使用Vue创建前后端分离项目的过程(前端部分)
前端·javascript·vue.js
lv程序媛3 小时前
css让按钮放在最右侧
前端·css
isSamle4 小时前
使用Vue+Django开发的旅游路书应用
前端·vue.js·django
温轻舟4 小时前
前端开发 之 12个鼠标交互特效上【附完整源码】
开发语言·前端·javascript·css·html·交互·温轻舟
ss2734 小时前
基于Springboot + vue实现的汽车资讯网站
vue.js·spring boot·后端