目录
最初其实我想用 echarts 实现词云效果的,后来放弃了,因为不知道怎么让那个词云图动起来……
踩坑记录:
<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>
<marquee>?标签是一个非标准的?HTML?标签(推荐使用?CSS?和?JavaScript?来实现滚动效果,而不是依赖?<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>
?
代码解释(样式省略,仅提供思路):
<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>
代码解释(此处只提供思路,不提供完整代码):
<!-- 组件 - 按钮 -->
<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`;
}