vue3.0 使用echarts与echarts-gl 实现3D饼图

news2024/12/25 12:51:55

效果

安装echarts

npm install echarts
npm install echarts-gl

3d饼图组件:

<template>
    <div style="width: 100%; height: 100%" ref="echart"></div>
</template>

<script setup>
import { reactive, ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'
import 'echarts-gl'

const boxHeight = ref([])
const legendData = ref([])
const echart = ref()

const props = defineProps({
    optionsData: []
})

const echartInit = () => {
    var myChart = echarts.init(echart.value)
    const series = getPie3D(props.optionsData, 0.7)
    series.push({
        center: ['10%', '90%'],
        name: 'pie2d',
        type: 'pie',
        label: {
        show: false,
        opacity: 1,
        fontSize: 12,
        lineHeight: 10,
        textStyle: {
            fontSize: 12,
            color: '#fff',
        },
        },
        labelLine: {
        length: 30,
        length2: 30,
        },
        startAngle: -30, //起始角度,支持范围[0, 360]。
        clockwise: false, //饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
        radius: ['40%', '60%'],
        //data: props.optionsData,
        data: props.optionsData.map(item => {
          item.itemStyle.opacity = 0
          return item
        }),

        itemStyle: {
        opacity: 0,
        
        }
    })
    // 准备待返回的配置项,把准备好的 legendData、series 传入。
    let option = {
        legend: {
        show: true,
        tooltip: {
            show: true,
        },
        orient: 'vertical',
        //data: ['待办', '已办', '未处理'],
        data: legendData.value,
        top: 'center',
        // 设置图例为矩形
        itemWidth: 14,   // 图例标记的宽度,默认为14
        itemHeight: 14,  // 图例标记的高度,默认为12
        itemStyle: {
            // 设置边框圆角,可以设置为 0 实现正方形
            borderRadius: 5
        },
        itemGap: 12,
        right: '2%',
        textStyle: {
            color: '#fff',
            fontSize: 12,
        },
        formatter: (param)=> {
            let item = legendData.value.filter(item => item.name == param)[0];
            let bfs = fomatFloat(item.value * 100, 2) + "%";
            let v = item.value2;
            //return `${item.name}  ${bfs}`;
            return `${item.name}  ${v}`;
        }
        },
        animation: true,
        tooltip: {
        formatter: (params) => {
            if (
            params.seriesName !== 'mouseoutSeries' &&
            params.seriesName !== 'pie2d'
            ) {
            return `${params.seriesName}<br/>
            <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${
                option.series[params.seriesIndex].pieData.value + '台'
            }`
            }
        },
        textStyle: {
            fontSize: 12,
        },
        },
        title: {
        x: 'center',
        top: '20',
        textStyle: {
            color: '#fff',
            fontSize: 12,
        },
        },
        // backgroundColor: '#FFF',
        labelLine: {
        show: false,
        lineStyle: {
            color: '#7BC0CB',
        },
        normal: {
            show: false,
            length: 10,
            length2: 10,
        },
        },
        label: {
        show: false,
        position: 'outside',
        formatter: '{b} \n{d}%',
        textStyle: {
            color: '#fff',
            fontSize: '14px',
        },
        },
        xAxis3D: {
        min: -1,
        max: 1,
        },
        yAxis3D: {
        min: -1,
        max: 1,
        },
        zAxis3D: {
        min: -1,
        max: 1,
        },
        grid3D: {
        show: false,
        //boxHeight: 0.01,
        boxHeight: boxHeight.value,
        //top: '30%',
        bottom: '50%',
        left: '-18%',
        // environment: "rgba(255,255,255,0)",
        viewControl: {
            distance: 180,//这个数值越大图就越小
            alpha: 25,//倾斜角度
            beta: 60,//起始渲染角度
            autoRotate: false, // 自动旋转
            rotateSensitivity: 1,//旋转灵敏度,鼠标按住不放可进行角度偏移
            zoomSensitivity: 1,//缩放灵敏度,鼠标滚轮可修改大小
            panSensitivity: 0,// 平移操作的灵敏度,值越大越灵敏。默认为1,设置为0后无法平移。支持使用数组分别设置横向和纵向的平移灵敏度
        },
        },
        series: series,
    }
    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option)
    }
    
    function fomatFloat(num, n) {
    var f = parseFloat(num);
    if (isNaN(f)) {
        return false;
    }
    f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); // n 幂   
    var s = f.toString();
    var rs = s.indexOf('.');
    //判定如果是整数,增加小数点再补0
    if (rs < 0) {
        rs = s.length;
        s += '.';
    }
    while (s.length <= rs + n) {
        s += '0';
    }
    return s;
}

// 获取3d饼图的最高扇区的高度
function getHeight3D(series, height) {
    series.sort((a, b) => {
        return (b.pieData.value - a.pieData.value);
    })
    return height * 25 / series[0].pieData.value;
}
function getParametricEquation(
    startRatio,
    endRatio,
    isSelected,
    isHovered,
    k,
    height,
) {

    // 计算
    let midRatio = (startRatio + endRatio) / 2

    let startRadian = startRatio * Math.PI * 2
    let endRadian = endRatio * Math.PI * 2
    let midRadian = midRatio * Math.PI * 2

    // 如果只有一个扇形,则不实现选中效果。
    if (startRatio === 0 && endRatio === 1) {
        isSelected = false
    }

    // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
    k = typeof k !== 'undefined' ? k : 1 / 3

    // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
    let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
    let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0

    // 计算高亮效果的放大比例(未高亮,则比例为 1)
    let hoverRate = isHovered ? 1.05 : 1

    // 返回曲面参数方程
    return {
        u: {
        min: -Math.PI,
        max: Math.PI * 3,
        step: Math.PI / 32,
        },

        v: {
        min: 0,
        max: Math.PI * 2,
        step: Math.PI / 20,
        },

        x: function (u, v) {
        if (u < startRadian) {
            return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
        }
        if (u > endRadian) {
            return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
        }
        return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
        },

        y: function (u, v) {
        if (u < startRadian) {
            return  offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
        }
        if (u > endRadian) {
            return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
        }
        return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
        },

        z: function (u, v) {
        if (u < -Math.PI * 0.5) {
            return Math.sin(u)
        }
        if (u > Math.PI * 2.5) {
            return Math.sin(u)
        }
        return Math.sin(v) > 0 ? 1 * height : -1
        },
    }
    }
    // 生成模拟 3D 饼图的配置项
    function getPie3D(pieData, internalDiameterRatio) {
    let series = []
    let sumValue = 0
    let startValue = 0
    let endValue = 0
    //let legendData = []

    let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3

    pieData.sort((a, b) => {
        return (b.value - a.value);
    });

    // 为每一个饼图数据,生成一个 series-surface 配置
    for (let i = 0; i < pieData.length; i++) {
        sumValue += pieData[i].value
        let seriesItem = {
        name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
        type: 'surface',
        parametric: true,
        wireframe: {
            show: false,
        },
        pieData: pieData[i],
        pieStatus: {
            selected: false,
            hovered: false,
            k: k,
        },
        }

        if (typeof pieData[i].itemStyle != 'undefined') {
        let itemStyle = {}
        typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null
        typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null
        seriesItem.itemStyle = itemStyle
        }
        series.push(seriesItem)
    }

    // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
    // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
    for (let i = 0; i < series.length; i++) {
        endValue = startValue + series[i].pieData.value
        series[i].pieData.startRatio = startValue / sumValue
        series[i].pieData.endRatio = endValue / sumValue
        series[i].parametricEquation = getParametricEquation(
        series[i].pieData.startRatio,
        series[i].pieData.endRatio,
        false,
        false,
        k,
        series[i].pieData.value,
        )
        boxHeight.value = getHeight3D(series, 2);//通过传参设定3d饼/环的高度,26代表26px

        startValue = endValue

        //legendData.value.push(series[i].name)
        let bfb = fomatFloat(series[i].pieData.value / sumValue, 4);
        legendData.value.push({
        name: series[i].name,
        value: bfb,
        value2: series[i].pieData.value
    });
    }
    return series
    }

watch(
    () => [props.optionsData],
    () => {
        echartInit()
    }
)

onMounted(() => {
    echartInit()
})


</script>

vue中的使用:

<My3DPie :optionsData="optionsData" />

vue的js引入组件

// 传入数据生成 option
         const optionsData = ref([
            {
              name: '饮料',
              value: 36,
              itemStyle: {
                opacity: 0.8,
                color: '#14EC92',
              },
            },
            {
              name: '小食品',
              value: 29,
              itemStyle: {
                opacity: 0.8,
                color: '#42A2D8',
              },
            }
            
          ])

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2124246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker部署FastDFS文件系统详细记录,每个步骤都有详细记录。含linux和windows双系统安装教程

docker部署FastDFS文件系统详细记录 参考文章docker搭建FastDFS文件系统&#xff08;最详细版&#xff09; 文章里所有步骤由作者亲自测试&#xff0c;所以和 原文有不少差异 文章里所有步骤由作者亲自测试&#xff0c;所以和 原文有不少差异 文章里所有步骤由作者亲自测试&…

HTML + CSS - 网页布局之一般布局浮动布局

1. 一般布局 1.1 一般布局相关参数 元素内容常常可以想像为放在一个盒子里&#xff0c;然后在周边加上内边距&#xff0c;边框和外边距&#xff0c;是盒子模型 默认一个块级区域会填充父类所有的行向空间&#xff0c;并且沿着块伸长容纳其内容&#xff0c;可以为块状体设置某…

18、公司信贷管理|贷款额度的测算|贷款期限及其定价的设定逻辑!

银行在综合权衡贷款的第一还款来源和第二还款来源、风险和收益的基础上&#xff0c;应明确提出贷与不贷的意见。经调查审查同意的贷款&#xff0c;应提出最终的融资方案。 合理的融资方案既要有利于提升本行的竞争力&#xff0c;又要有利于控制贷款风险。完整的融资方案一般包…

苹果的“AI茅”之路只走了一半

今年苹果发布会最大的亮点&#xff0c;也许是和华为“撞档”&#xff0c;又或者是替腾讯“发布”新手游&#xff0c;但肯定不是iPhone 16。 9月10日&#xff0c;苹果秋季新品发布会与华为见非凡品牌盛典相继举行&#xff0c;iPhone 16系列也与HUAWEI Mate XT同日发布。 不过&…

性能测试-jmeter脚本录制(十五)

一、jmeter脚本录制&#xff08;不推荐&#xff09;简介&#xff1a; 二、jmeter脚本录制步骤 1、添加代理服务器和线程组 2、配置http代理服务器的端口和目标线程组 3修改本机浏览器代理 4、点击启动 5、每次操作页面前&#xff0c;修改提示文字

html 中如何使用 uniapp 的部分方法

示例代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><…

2024年,最新 OKR 优秀案例集(建议收藏)

我们发现&#xff0c;在你学习 OKR 的例子之前&#xff0c;先回顾一下 OKR 是如何写的&#xff0c;会有帮助。这样&#xff0c;你就能更好地理解下面描述的目标和关键结果的例子。 好消息是&#xff0c;Tita 的 OKR案例库有很多例子可以学习&#xff0c;它们从公司的 OKR 开始…

离散时域和连续频谱之间的傅里叶

要得到虚假的时域离散点&#xff0c;要用频域一个周期的积分

对接开源大模型应用开发平台最佳实践

本文以Dify为例介绍如何使用OpenSearch LLM智能问答版对接大模型应用开发平台构建RAG系统。 背景 随着AIGC技术日新月异的发展&#xff0c;LLM应用也在持续迭代。基于LLM、Agent框架、工作流编排能力等&#xff0c;可以搭建不同场景下丰富的应用服务。其中&#xff0c;检索增…

基于node.js koa2模拟快递柜存储取出快递微信小程序

本文介绍了一个基于Node.js Koa2框架的快递柜存储和取出快递的微信小程序。首先&#xff0c;我们使用Koa2框架搭建了一个简单的后端服务器&#xff0c;用于处理微信小程序发送的请求。然后&#xff0c;我们实现了快递柜的存储和取出功能&#xff0c;用户可以通过微信小程序扫描…

【包教包会】CocosCreator3.x拖尾MotionStreak威力加强版(支持3.x、支持原生、可合批)

将去年写的2.x拖尾升级到3.x 完美适配Web、原生平台&#xff08;其余平台没测过&#xff09;。 保留原版功能&#xff08;拖尾会跟随节点位移、缩放、受节点透明度影响&#xff0c;但不会跟随节点旋转&#xff09; 支持世界坐标 / 本地坐标切换&#xff08;至于为什么需要这…

中国老年社会追踪调查(2011-2020年)

中国老年社会追踪调查&#xff08;CLASS&#xff09;是一项全国性、连续性、系统性、长期性的社会调查项目&#xff0c;它通过定期、系统地收集中国老年人群的社会、经济背景数据&#xff0c;以掌握老年人在衰老过程中面临的各种问题和挑战。这些数据评估了各项社会政策措施在提…

精通PostgreSQL:解锁高效数据库管理的十大必备技巧与最佳实践

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 目录 前言&#…

Leetcode 109.有序链表转换二叉搜索树(Medium)

给定一个单链表的头节点 head &#xff0c;其中的元素 按升序排序 &#xff0c;将其转换为 平衡 二叉搜索树。 示例 1: 输入: head [-10,-3,0,5,9] 输出: [0,-3,9,-10,null,5] 解释: 一个可能的答案是[0&#xff0c;-3,9&#xff0c;-10,null,5]&#xff0c;它表示所示的高度…

uni如何安装新依赖

如何在 uniapp 项目中&#xff0c;使用“插件市场” 的原生插件 插件市场 :DCloud 插件市场 1. 第一步&#xff1a; 首先在HBuilder x 中新建一个uniapp 项目&#xff0c;如果已建好 uniapp 项目则跳过该步骤。 2.第二步&#xff1a; 在 ”插件市场中 “ 选择你需要的原生插件…

微服务实战系列之玩转Docker(十四)

前言 时逢白露天骤变&#xff0c;细雨纷纷气渐凉&#xff0c;忽有故人心上过&#xff0c;回首山河已是秋。——碎碎念 当秋天来临的那一刻&#xff0c;你会想起故人么&#xff1f;此刻&#xff0c;突然一句唐诗——“故人具鸡黍&#xff0c;邀我去田家”&#xff0c;飘过了耳边…

CAN通信入门 - 1

CAN通信入门 - 1 CAN通信原理 参考链接&#xff1a;CAN总线原理 CAN总线信号为差分信号&#xff0c;其有2根总线分别为CAN-L\CAN-H。 CAN总线之间的电平分为显性电平和隐性电平&#xff0c;其中显性电平对应的是逻辑0&#xff0c;隐性电平对应的是逻辑1。当CAN差分信号为0V时…

【Web】骨架屏

文章目录 概述骨架屏的实现方案page-skeleton-webpack-plugin安装基本使用 来源 概述 骨架屏&#xff08;Skeleton Screen&#xff09;是一种在页面数据加载完成前&#xff0c;先给用户展示出页面的大致结构&#xff08;灰色占位图&#xff09;的技术。当页面实际数据加载并渲…

【话费充值】话费API接口对接有哪些关键步骤

话费API接口对接通常包括以下几个关键步骤&#xff1a; 选择服务提供商&#xff1a;选择一个可靠的话费充值API服务提供商&#xff0c;这可能是电信运营商本身或是一个信誉良好的第三方服务提供商。注册和认证&#xff1a;在选定的服务提供商平台上注册&#xff0c;并获得API访…

自研商家如何快速接入电商平台订单数据?

随着电子商务行业的快速发展&#xff0c;越来越多的商家开始寻求高效的订单管理和数据整合方案。对于那些自研系统的商家来说&#xff0c;如何实现与各大电商平台之间的无缝对接&#xff0c;成为了一项重要挑战。点三电商API正是为此类需求量身打造&#xff0c;为商家提供了一站…