使用 marquee 标签实现词云滚动、使用原生 js 实现简单拖拽

目录

1. 使用 marquee 标签实现词云滚动

1.1 使用 echarts 词云图示例

1.2 marquee 基本介绍

1.3 HTML 使用 marquee 示例

1.4 Vue3 使用 marquee 示例

2. 使用原生 js 实现简单拖拽


1. 使用 marquee 标签实现词云滚动

1.1 使用 echarts 词云图示例

最初其实我想用 echarts 实现词云效果的,后来放弃了,因为不知道怎么让那个词云图动起来……

踩坑记录:

  • 我用的 echarts 版本 5.4.3,他需要的 词云图至少得是 2.0 以上的版本,词云图配置参考 github -------- echarts-wordcloud
  • 最初使用 npm 安装词云库,即使版本已经按照要求来了,仍然出现报错 Unknown series wordCloud,解决方案是 不通过 npm 下载词云依赖,直接去 github 下载 echarts-wordcloud.js 并在项目中引入
<template>
  <!-- 使用 echarts 词云图 -->
  <section class="demo__container">
    <div class="word-cloud--left"></div>
    <div class="word-cloud--right"></div>
  </section>
</template>

<script lang="ts">
import { defineComponent, nextTick, onMounted, onUnmounted, PropType, reactive, ref, toRefs, watch } from 'vue';
/*
 * 引入词云外部插件 - 使用第三方库,会报错 Unknown series wordCloud,解决方案:引入js,js顶部添加去除 eslint 校验
 * import 'echarts-wordcloud';
 */
import './echarts-wordcloud.js';
// 引入echarts
import * as echarts from 'echarts';

export default defineComponent({
  name: 'Demo',
  props: {
    // 输入参数
    requestParamList: {
      type: Array as PropType<any[]>,
      default: () => [],
    },
    // 输出参数
    resultDataList: {
      type: Array as PropType<any[]>,
      default: () => [],
    },
  },
  setup(props, { emit }) {
    const leftMarqueeRef: any = ref();
    const rightMarqueeRef: any = ref();

    // 响应式数据
    const state = reactive({
      // 左侧词云图数据
      leftChartData: [] as any[],
      // 右侧词云图数据
      rightChartData: [] as any[],
    });

    // 图表实例
    let leftChart: any = null;
    let rightChart: any = null;

    /**
     *  数据处理
     * @description 对传入的数组数据进行键值的处理,以符合图表数据的要求
     */
    const dataProcessing = () => {
      // 清空图表数据
      state.leftChartData = [];
      state.rightChartData = [];

      if (props.requestParamList) {
        state.leftChartData = props.requestParamList.map((item: any) => ({
          name: item?.requestParamName || '--',
          value: 1,
        }));
      }
      if (props.resultDataList) {
        state.rightChartData = props.resultDataList.map((item: any) => ({
          name: item?.nodeDesc || '--',
          value: 1,
        }));
      }
      console.log('重组词云图数据 ---', state.leftChartData, state.rightChartData);
    };

    /**
     * 绘制图形
     */
    const drawChart = () => {
      // 词云图公共配置
      const comWordOpitons = {
        type: 'wordCloud',
        gridSize: 24, // 单词之间的间隔大小,值越大间隔越大
        sizeRange: [8, 14], // 最小字体和最大字体 必须写一个范围不能直接写每个字的大小
        rotationRange: [0, 0], // 字体旋转角度的范围
        // maskImage: leftPng,
        shape: 'star', // 形状
        width: '100%',
        height: '80%',
        left: null,
        top: '10%',
        right: null,
        bottom: '10%',
        drawOutOfBound: false, // 允许文字部分在画布外绘制
        shrinkToFit: true, // 是否收缩文本。如果将其设置为false,则文本将不渲染。如果设置为true,则文本将被缩小
        textStyle: {
          padding: [10, 14, 10, 14], // 单个文字块-间距
          fontWeight: 700, // 字体粗重
          backgroundColor: '#fff', // 单个文字块-背景颜色
          borderRadius: 100, // 单个文字块-圆角
          shadowColor: 'rgba(0, 0, 0, 0.15)', // 单个文字块-阴影
          shadowBlur: 10, // 单个文字块-阴影
          color: 'rgba(0, 0, 0, 1)', // 字体颜色
          /*
           * color: (params: any) => `rgb(${[
           *   Math.round(Math.random() * 255),
           *   Math.round(Math.random() * 255),
           *   Math.round(Math.random() * 255),
           * ].join(',')})`,
           */
        },
        itemStyle: {
          emphasis: {
            label: {
              show: true, // 开启显示tooltip
              formatter: '{b} : {c}', // 自定义tooltip格式,{b}表示单词名称,{c}表示单词值
            },
          },
        },
        data: [],
      };

      // 左侧词云图配置
      const leftOption: any = {
        xAxis: {
          show: false,
        },
        yAxis: {
          show: false,
        },
        series: [
          {
            ...comWordOpitons,
            data: state.leftChartData,
          },
        ],
      };
      // 右侧侧词云图配置
      const rightOption: any = {
        xAxis: {
          show: false,
        },
        yAxis: {
          show: false,
        },
        /*
         * tooltip: {
         *   trigger: 'axis',
         *   axisPointer: {
         *     type: 'cross',
         *     label: {
         *       backgroundColor: '#6a7985',
         *     },
         *   },
         * },
         */
        series: [
          {
            ...comWordOpitons,
            textStyle: {
              ...comWordOpitons.textStyle,
              color: '#5174FF',
            },
            data: state.rightChartData,
          },
        ],
      };
      //  画布清除
      leftChart.clear();
      rightChart.clear();

      try {
        leftChart.setOption(leftOption, true);
        // console.log('设置图表数据(左) -- ', leftChart, leftOption);
        rightChart.setOption(rightOption, true);

        // 单词上下滚动动画
        setInterval(() => {
          leftChart.dispatchAction({
            type: 'downplay',
            seriesIndex: 0,
          });
          leftChart.dispatchAction({
            type: 'highlight',
            seriesIndex: 0,
            dataIndex: Math.floor(Math.random() * state.leftChartData.length),
          });
        }, 2000); // 每隔2秒触发一次动画
      } catch (error) {
        console.log(error);
      }
    };

    /**
     * 重新设置图表的尺寸
     */
    const resize = () => {
      nextTick(() => {
        leftChart.resize();
        rightChart.resize();
      });
    };

    onMounted(() => {
      // 实例化 echarts 对象、处理数据、绘制图表
      leftChart = echarts.init(document.querySelector('.word-cloud--left'));
      rightChart = echarts.init(document.querySelector('.word-cloud--right'));

      // 数据处理
      dataProcessing();
      // 绘制图表
      drawChart();
      // 重置图表大小
      resize();

      // 监听视窗尺寸变化 重置图表尺寸
      window.addEventListener('resize', resize);
    });

    onUnmounted(() => {
      // 移除监听事件
      window.removeEventListener('resize', resize);
    });

    /**
     *  监听options、词云样式变化 重绘词云
     */
    watch(
      () => [props.requestParamList, props.resultDataList],
      () => {
        // 数据处理
        dataProcessing();
        // 绘制图表
        drawChart();
        // 重置图表大小
        resize();
      },
      {
        deep: true,
      },
    );

    return {
      ...toRefs(state),
      leftMarqueeRef,
      rightMarqueeRef,
    };
  },
});
</script>

1.2 marquee 基本介绍

<marquee> 标签是一个非标准的 HTML 标签(推荐使用 CSS 和 JavaScript 来实现滚动效果,而不是依赖 <marquee> 标签),其中的文本或图像会在页面上水平或垂直滚动,可以通过设置不同的属性,来控制滚动的速度、方向和行为。

 

1.3 HTML 使用 marquee 示例

使用效果:下方代码展示了 marquee 标签使用不同参数后的各种效果,直接cv预览即可

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>marquee</title>
</head>

<body>
    <marquee direction="up">我向上滚动</marquee>
    <hr>
    <marquee direction="down">我向下滚动</marquee>
    <hr>
    <marquee direction="left">我向左滚动</marquee>
    <hr>
    <marquee direction="right">我向右滚动</marquee>
    <hr>
    <marquee scrollamount="10">我速度很慢</marquee>
    <hr>
    <marquee scrollamount="100">我速度很快</marquee>
    <hr>
    <marquee scrolldelay="30">我小步前进。</marquee>
    <hr>
    <marquee scrolldelay="1000" scrollamount="100">我大步前进。</marquee>
    <hr>
    <marquee loop="1">我滚动一次</marquee>
    <hr>
    <marquee loop="2">我滚动两次</marquee>
    <hr>
    <marquee loop="infinite">我无限循环滚动</marquee>
    <hr>
    <marquee behavior="alternate">我来回滚动</marquee>
    <hr>
    <marquee behavior="scroll">我单方向循环滚动</marquee>
    <hr>
    <marquee behavior="scroll" direction="up" height="30">我单方向向上循环滚动</marquee>
    <hr>
    <marquee behavior="slide">我只滚动一次</marquee>
    <hr>
    <marquee behavior="slide" direction="up">我向上只滚动一次</marquee>
    <hr>
    <marquee behavior=="slide" direction="left" bgcolor="red">我的背景色是红色的</marquee>
    <hr>
    <marquee width="600" height="50" bgcolor="red">我宽300像素,高30像素。</marquee>
    <hr>
    <marquee width="300" height="30" vspace="10" hspace="10" bgcolor="red">我矩形边缘水平和垂直距周围各10像素。</marquee>
    <hr>
    <marquee width="300" height="30" vspace="50" hspace="50" bgcolor="red">我矩形边缘水平和垂直距周围各50像素。</marquee>
    <hr>
    <marquee align="absbottom">绝对底部对齐</marquee>
    <hr>
    <marquee align="absmiddle">绝对中央对齐</marquee>
    <hr>
    <marquee align="baseline">底线对齐</marquee>
    <hr>
    <marquee align="bottom">底部对齐(默认)</marquee>
    <hr>
    <marquee align="left">左对齐</marquee>
    <hr>
    <marquee align="middle"> 中间对齐</marquee>
    <hr>
    <marquee align="right">右对齐</marquee>
    <hr>
    <marquee align="texttop">顶线对齐</marquee>
    <hr>
    <marquee align="top">顶部对齐</marquee>
</body>

</html>

 

1.4 Vue3 使用 marquee 示例

代码解释(样式省略,仅提供思路):

  • 这是个 vue3 组件,接收输入、输出参数
  • 以左侧面板为例,当接收的参数大于6时,采用 marquee 标签,实现词云滚动,否则不滚动
  • 当鼠标移入词云时,停止滚动,当鼠标移出词云时,开启滚动
  • 每个词云左右间距采用随机数
<template>
  <div class="demo__container-marquee">
    <!-- 左侧 -->
    <div class="scroll-outer">
      <template v-if="leftChartData.length > 6">
        <marquee
          ref="leftMarqueeRef"
          behavior="scroll"
          direction="up"
          height="50%"
          loop="infinite"
          class="scroll-container"
          :scrollamount="15"
          :scrolldelay="200"
          @mouseover="stopScroll('left')"
          @mouseleave="startScroll('left')"
        >
          <div
            v-for="(item, index) of leftChartData"
            :key="index"
            class="scroll-item"
            :style="{
              'margin-left': `${Math.floor(Math.random() * 60 + 5)}%`,
              'margin-bottom': `${Math.floor(Math.random() * 24 + 12)}px`,
            }"
            :title="item?.name || '-'"
          >
            {{ item?.name || '-' }}
          </div>
        </marquee>
      </template>

      <template v-else>
        <div class="scroll-container">
          <div
            v-for="(item, index) of leftChartData"
            :key="index"
            class="scroll-item"
            :style="{
              'margin-left': `${Math.floor(Math.random() * 60 + 5)}%`,
              'margin-bottom': `${Math.floor(Math.random() * 24 + 12)}px`,
            }"
            :title="item?.name || '-'"
          >
            {{ item?.name || '-' }}
          </div>
        </div>
      </template>
    </div>
    <!-- 右侧 -->
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, PropType, reactive, ref, toRefs, watch } from 'vue';

export default defineComponent({
  name: 'demo',
  props: {
    // 输入参数
    requestParamList: {
      type: Array as PropType<any[]>,
      default: () => [],
    },
    // 输出参数
    resultDataList: {
      type: Array as PropType<any[]>,
      default: () => [],
    },
  },
  setup(props, { emit }) {
    const leftMarqueeRef: any = ref();
    const rightMarqueeRef: any = ref();

    // 响应式数据
    const state = reactive({
      // 左侧词云图数据
      leftChartData: [] as any[],
      // 右侧词云图数据
      rightChartData: [] as any[],
    });

    /**
     * 数据处理
     * @description 对传入的数组数据进行处理
     */
    const dataProcessing = () => {
      // 清空图表数据
      state.leftChartData = [];
      state.rightChartData = [];

      if (props.requestParamList) {
        state.leftChartData = props.requestParamList.map((item: any) => ({
          name: item?.requestParamName || '--',
          value: 1,
        }));
      }
      if (props.resultDataList) {
        state.rightChartData = props.resultDataList.map((item: any) => ({
          name: item?.nodeDesc || '--',
          value: 1,
        }));
      }
      console.log('重组词云图数据 ---', state.leftChartData, state.rightChartData);
    };

    const stopScroll = (type = 'left') => {
      if (type === 'left') {
        leftMarqueeRef.value.stop();
      } else {
        rightMarqueeRef.value.stop();
      }
    };

    const startScroll = (type = 'left') => {
      if (type === 'left') {
        leftMarqueeRef.value.start();
      } else {
        rightMarqueeRef.value.start();
      }
    };

    onMounted(() => {
      // 数据处理
      dataProcessing();
    });

    /**
     *  监听options、词云样式变化 重绘词云
     */
    watch(
      () => [props.requestParamList, props.resultDataList],
      () => {
        // 数据处理
        dataProcessing();
      },
      {
        deep: true,
      },
    );

    return {
      ...toRefs(state),
      leftMarqueeRef,
      rightMarqueeRef,
      stopScroll,
      startScroll,
    };
  },
});
</script>

2. 使用原生 js 实现简单拖拽

代码解释(此处只提供思路,不提供完整代码):

  • 目标效果:用户拖动后,按钮能在用户拖动后的位置固定
  • 给按钮加下面四个参数:draggable="true"、style、dragstart、dragend
  • 按钮必须是绝对定位,并且有初始 top left
  • 结束拖动后,修改 top、left
    <!-- 组件 - 按钮 -->
    <teleport to="body">
      <my-button
        draggable="true"
        :style="`left:${elLeft}; top:${elTop}`"
        @dragstart="dragstart($event)"
        @dragend="dragend($event)"
      ></my-button>
    </teleport>



    // 实现数据蓝拖拽
    const elLeft = ref('89.5%');
    const elTop = ref('40%');

    // 拖拽开始事件
    function dragstart(e: any) {
      // console.log('开始拖拽 ---', e);
    }

    // 拖拽完成事件
    function dragend(e: any) {
      // console.log('结束拖拽 ---', e);
      elLeft.value = `${e.pageX}px`; // 实现拖拽元素随偏移量移动
      elTop.value = `${e.pageY}px`;
    }

  • 20
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值