blob: 172e20f9e619e13e528f435190d1b59c22ef3437 [file] [log] [blame]
<template>
<div v-loading="isWebFontLoading" element-loading-text="字体加载中">
<div
class="chart"
ref="chartRef"
v-bind:style="{
width: width + 'px',
height: height + 'px'
}"
></div>
</div>
</template>
<script setup lang="ts">
import { onMounted, shallowRef, ref } from 'vue';
import * as echarts from 'echarts';
import 'echarts-wordcloud';
import Color from 'color';
const chart = shallowRef<echarts.ECharts | null>(null);
const chartRef = ref<HTMLElement | null>(null);
const isWebFontLoading = ref(false);
const width = ref(800);
const height = ref(600);
defineExpose({
run,
setLoading,
resize,
download
});
type Config = {
bgColor: string;
themeColors: string[];
saturation: number[];
lightness: number[];
alpha: number[];
fontFamily: string;
fontSize: number[];
rotate: number[];
width: number;
height: number;
shape: string;
shapeRatio: boolean;
};
function setLoading(isLoading: boolean) {
isWebFontLoading.value = isLoading;
}
function resize(w: number, h: number) {
w = Math.max(100, w);
h = Math.max(100, h);
if (width.value !== w || height.value !== h) {
width.value = w;
height.value = h;
}
setTimeout(() => {
chart.value!.resize();
});
}
function download() {
if (chart.value) {
try {
const url = chart.value.getDataURL();
const a = document.createElement('a');
a.href = url;
a.download = 'wordcloud.png';
a.click();
} catch (e) {
console.error(e);
alert('保存出错了,可以尝试在右图中点击鼠标右键,并选择“图片储存为”');
}
}
}
function run(data?: [], fillSmall?: boolean, config?: Config) {
const chartData = (data ? data.slice() : []) as {
name: string;
value: number;
}[];
// if (fillSmall) {
// const smallData = chartData.filter((d) => d.value < 10);
// const maxData = 400;
// for (let i = chartData.length; i < maxData; ++i) {
// for (let j = 0; j < smallData.length && i < maxData; ++j) {
// chartData.push({
// name: smallData[j].name,
// value: smallData[j].value
// });
// }
// }
// }
const hues = config
? config.themeColors.map((color) => Color(color).hsl().object().h)
: [];
function getHue() {
const index = Math.floor(Math.random() * hues.length);
return hues[index];
}
function getRandom(minMax: number[] | undefined) {
if (!minMax) {
return 0;
}
const max = minMax[1] == null ? 1 : minMax[1];
const min = minMax[0] == null ? 0 : minMax[0];
const range = max - min || 1;
return Math.random() * range + min;
}
function render(maskImage?: HTMLImageElement) {
chart.value!.setOption({
backgroundColor: config!.bgColor,
series: [
{
type: 'wordCloud',
data: chartData,
gridSize: 6,
sizeRange: config?.fontSize,
rotationRange: config?.rotate,
maskImage,
width: config?.width + '%',
height: config?.height + '%',
layoutAnimation: true,
keepAspect: config?.shapeRatio,
textStyle: {
color: (param: any) => {
const value = param.value;
const h = getHue();
const s = getRandom(config?.saturation);
const l = getRandom(config?.lightness);
const color = Color(`hsl(${h}, ${s}%, ${l}%)`);
return color.toString();
}
}
}
],
textStyle: {
fontFamily: config?.fontFamily
}
});
}
let maskImage: HTMLImageElement;
if (config) {
maskImage = new Image();
maskImage.src = config.shape.startsWith('blob:')
? config.shape
: config.shape + '.png';
maskImage.onload = () => {
render(maskImage);
};
maskImage.onerror = () => {
render();
};
} else {
render();
}
}
onMounted(() => {
chart.value = echarts.init(chartRef.value!);
});
</script>
<style scoped lang="scss">
.chart {
border: 1px solid #f2eef2;
width: 800px;
height: 600px;
}
</style>