vue3封装简易的vue-echarts问题
作者:TwoKe
这篇文章主要介绍了vue3封装简易的vue-echarts问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
vue3封装简易的vue-echarts
项目场景
数据可视化开发,采用的技术栈是vue3+echarts+router。
问题描述
在vue2中,才开始开发数据可视化大屏,都是用echarts,之后改用为vue-echarts组件,但是到了vue3之后,组件会有一些小问题,所以准备自己封装一套简易的vue-echarts组件,其他的功能之后再迭代上去,足够项目使用即可。
代码封装
<template>
<div class="echarts" :id="id"></div>
</template>这里的echarts组件的id应该每个组件不同,因此id值为动态设置的。
<script>
import { onMounted } from 'vue'
import { uuid } from '../../utils/index'
import Echarts from 'echarts'
export default {
name: 'TwokeVueEcharts',
props: {
options: {
type: Object,
default: () => ({})
}
},
setup (ctx) {
const id = `vue-echarts-${uuid()}`
let chart = null
const initEcharts = () => {
if (!chart) {
const dom = document.getElementById(id)
chart = Echarts.init(dom)
}else {
return
}
if(!ctx.options) return
chart.setOption(ctx.options)
}
onMounted( () => {
initEcharts()
})
return {
id
}
}
}
</script>这里可以看到我引入了uuid的工具类,是为了生成唯一的id值,这里也可用时间戳搭配前缀来实现。
返回id值以供视图渲染。
引入的echarts为echarts官方组件
<style lang="scss" scoped>
.echarts {
width: 100%;
height: 100%;
}
</style>这里是让echarts组件可以根据外面的容器大小,铺满展示。
组件使用
两种方式:
全局安装
main.js中
import TwokeVueEcharts from './TwokeVueEcharts.vue'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).component("vue-echarts",TwokeVueEcharts).mount("#app")组件中引入
<script>
import TwokeVueEcharts from './TwokeVueEcharts.vue'
export default {
components: {
TwokeVueEcharts
}
}
</script><template>
<div style="width:300px;height:300px">
<twoke-vue-echarts :options="options"></twoke-vue-echarts>
</div>
</template>
<script>
import TwokeVueEcharts from './TwokeVueEcharts.vue'
export default {
components: { TwokeVueEcharts },
setup () {
return {
options: {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 10,
data: ['事件一 名称', '事件二 名称', '事件三 名称', '事件四 名称'],
padding: [250, 6, 7, 8]
},
grid: {
top: 0,
left: 0,
right: 0,
bottom: 0
},
series: [
{
name: '访问来源',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '30',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 335, name: '事件一 名称' },
{ value: 310, name: '事件二 名称' },
{ value: 234, name: '事件三 名称' },
{ value: 135, name: '事件四 名称' }
]
}
]
}
}
}
}
</script>
vue3 echarts多图自适应封装
npm install echarts -S
封装echarts插件
src/components/echarts.vue
<template>
<div :id="echartsDomId"></div>
</template>
<script>
import * as echarts from 'echarts';
import {EleResize} from "@/assets/js/esresize";
import {watch, onMounted, ref,onUnmounted} from 'vue';
export default {
name: "rxp-echarts",
props: {
option: {
type: Object,
required: true,
}
},
setup(props) {
//存储echarts的实例
var instance = null;
// 监听props是否改变
watch(props, (newValue, oldValue) => {
instance.dispose(); //先销毁
init(); //再创建好了
//重绘不生效啊
// instance.setOption(newValue);
},{deep:true})
//计算属性 自动分配echarts的ID 。防止一个页面的echarts出现相同id
const echartsDomId = 'echarts' + Math.random() * 100000;
function init() {
instance = echarts.init(document.getElementById(echartsDomId));
instance.setOption(props.option); //设置option
//添加监听事件,如果页面宽度和高度变化, 重新绘制echarts
EleResize.on(document.getElementById(echartsDomId), () => {
instance.resize();
})
}
//组件一旦挂载,运行echarts初始化
onMounted(() =>init());
//组件卸载,销毁echarts实例
onUnmounted(()=>instance.dispose());
/**
* 父节点通过ref拿到此组件的实例,调用downloadPic函数,触发图片下载。
*/
const downloadPic = (name)=>{
let content = instance.getDataURL(); // 这个方法是经过封装之后的,id就是我们最终经过init(),setOptions的echart图表,它具有getDataURL()方法
let aLink = document.createElement('a');
let blob = base64ToBlob(content);
let evt = document.createEvent("HTMLEvents");
evt.initEvent("click", true, true);
aLink.download = `${name}.png`;
aLink.href = URL.createObjectURL(blob);
aLink.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
}
function base64ToBlob(code) {
let parts = code.split(';base64,');
let contentType = parts[0].split(':')[1];
let raw = window.atob(parts[1]);
let rawLength = raw.length;
let uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
}
return {
echartsDomId,
//父节点通过ref拿到此组件的实例,调用downloadPic函数,触发图片下载。
downloadPic
}
}
}
</script>
<style scoped>
div{
width: 100%;
height: 100%;
}
</style>再封装监听窗口变化触发方法
src/assets/js/esresize.js
//EleResize('domId',回调函数)
//echarts resize
var EleResize = {
_handleResize: function (e) {
var ele = e.target || e.srcElement
var trigger = ele.__resizeTrigger__
if (trigger) {
var handlers = trigger.__z_resizeListeners
if (handlers) {
var size = handlers.length
for (var i = 0; i < size; i++) {
var h = handlers[i]
var handler = h.handler
var context = h.context
handler.apply(context, [e])
}
}
}
},
_removeHandler: function (ele, handler, context) {
var handlers = ele.__z_resizeListeners
if (handlers) {
var size = handlers.length
for (var i = 0; i < size; i++) {
var h = handlers[i]
if (h.handler === handler && h.context === context) {
handlers.splice(i, 1)
return
}
}
}
},
_createResizeTrigger: function (ele) {
var obj = document.createElement('object')
obj.setAttribute('style',
'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden;opacity: 0; pointer-events: none; z-index: -1;')
obj.onload = EleResize._handleObjectLoad
obj.type = 'text/html'
ele.appendChild(obj)
obj.data = 'about:blank'
return obj
},
_handleObjectLoad: function () {
this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__
this.contentDocument.defaultView.addEventListener('resize', EleResize._handleResize)
}
}
if (document.attachEvent) { // ie9-10
EleResize.on = function (ele, handler, context) {
var handlers = ele.__z_resizeListeners
if (!handlers) {
handlers = []
ele.__z_resizeListeners = handlers
ele.__resizeTrigger__ = ele
ele.attachEvent('onresize', EleResize._handleResize)
}
handlers.push({
handler: handler,
context: context
})
}
EleResize.off = function (ele, handler, context) {
var handlers = ele.__z_resizeListeners
if (handlers) {
EleResize._removeHandler(ele, handler, context)
if (handlers.length === 0) {
ele.detachEvent('onresize', EleResize._handleResize)
delete ele.__z_resizeListeners
}
}
}
} else {
EleResize.on = function (ele, handler, context) {
var handlers = ele.__z_resizeListeners
if (!handlers) {
handlers = []
ele.__z_resizeListeners = handlers
if (getComputedStyle(ele, null).position === 'static') {
ele.style.position = 'relative'
}
var obj = EleResize._createResizeTrigger(ele)
ele.__resizeTrigger__ = obj
obj.__resizeElement__ = ele
}
handlers.push({
handler: handler,
context: context
})
}
EleResize.off = function (ele, handler, context) {
var handlers = ele.__z_resizeListeners
if (handlers) {
EleResize._removeHandler(ele, handler, context)
if (handlers.length === 0) {
var trigger = ele.__resizeTrigger__
if (trigger) {
trigger.contentDocument.defaultView.removeEventListener('resize', EleResize._handleResize)
ele.removeChild(trigger)
delete ele.__resizeTrigger__
}
delete ele.__z_resizeListeners
}
}
}
}
export {EleResize}使用echarts (vue2搬来的,vue3使用同理)
<template>
<div style="width: 100%;height: 100%;">
<div style="width: 80%;height: 70%;background-color: #888888">
<!--
注意:option是echarts的配置选项,
在为空时,不要初始化组件,所以添加v-if判断option的值存在
-->
<echarts v-if="config && flag" :option="config"></echarts>
</div>
<button @click="flag=!flag">隐藏echarts</button>
<button @click="update">修改option参数</button>
<button @click="upData">修改option中的数组</button>
</div>
</template>
<script>
import echarts from "@/components/echarts";
export default {
name: "helloWord",
components:{echarts},
data() {
return {
config:null, //echarts中的配置参数
flag:true, //手动切换是否隐藏
}
},
created() {
this.reset();
},
methods:{
reset(){
setTimeout(()=>{
this.config={
title: {
text: "我是初始化数据",
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [500, 230, 224, 218, 135, 147, 260],
type: 'line'
}
]
}
},2000)
},
update(){
this.config={
title: {
text: "我是修改后的数据",
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [ //随机数据
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
],
type: 'line'
}
]
}
},
upData(){
this.config.series={
data: [ //随机数据
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
parseInt(Math.random()*100),
],
type: 'line'
}
}
}
}
</script>
<style scoped>
</style>
``
vue echarts多图自适应封装总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
