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; /* 暂停动画 */
      }
    }
  }
}
相关推荐
开心工作室_kaic5 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿24 分钟前
webWorker基本用法
前端·javascript·vue.js
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
getaxiosluo2 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v2 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
栈老师不回家3 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙3 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
小远yyds4 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
程序媛小果4 小时前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
小光学长4 小时前
基于vue框架的的流浪宠物救助系统25128(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库·vue.js·宠物