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; /* 暂停动画 */
      }
    }
  }
}
相关推荐
Dignity_呱4 分钟前
vue3对组件通信做了哪些升级?
前端·vue.js·面试
独立开阀者_FwtCoder44 分钟前
最全301/302重定向指南:从SEO到实战,一篇就够了
前端·javascript·vue.js
普宁彭于晏1 小时前
元素水平垂直居中的方法
前端·css·笔记·css3
云浪1 小时前
元素变形记:CSS 缩放函数全指南
前端·css
kite01218 小时前
浏览器工作原理06 [#]渲染流程(下):HTML、CSS和JavaScript是如何变成页面的
javascript·css·html
超级土豆粉11 小时前
CSS3 的特性
前端·css·css3
TE-茶叶蛋12 小时前
Vue Fragment vs React Fragment
javascript·vue.js·react.js
Angindem12 小时前
从零搭建uniapp项目
前端·vue.js·uni-app
前端小白从0开始14 小时前
Vue3项目实现WPS文件预览和内容回填功能
前端·javascript·vue.js·html5·wps·文档回填·文档在线预览
難釋懷15 小时前
Vue解决开发环境 Ajax 跨域问题
前端·vue.js·ajax