自定义echart的封装

1.canvas.vue

​
<template>
    <div>

        <div style="margin: 50px auto;width: 100%" class="canvasDom">
            <div class="legend">
                <div v-for="(item,index) in legend" >
                    <div class="classBar" :style="{background: color[index]}"></div>
                    <span :style="{color:color[index]}"> {{item}}</span>
                </div>
                <div>
                    <div class="classLine" style="background:#FF8C11"></div>
                    <span style="color: #FF8C11"> 当期资产</span>
                </div>
            </div>
            <canvas :id="id" ref="chart" height="420">
                当前浏览器不支持Canvas,请更换浏览器后再试
            </canvas>
        </div>

    </div>
</template>

<script>
    export default {
        name: "career-simulation-chart",
        data() {
            return {
                color: ['#E8C251', '#68BEBB', '#FF6F72', '#9400d3', '#B22222',
                    '#006400', '#BDB76B', '#556B2F', '#FF8C00', '#8B0000'],
                legend:[],
            }
        },
        props: ['id'],
        methods: {
            init(obj) {
                let canvas = document.getElementById(this.id);
                canvas.width = canvas.parentNode.clientWidth
                console.log(canvas.parentNode.clientWidth)
                console.log(canvas.width)
                canvas.height = canvas.height
                let ctx = canvas.getContext('2d');
                //画布的款高
                let cw = canvas.width;
                let ch = Math.round(canvas.height / 1.3);
                let xz = obj.x;
                let yz = obj.y;
                //内间距padding
                let padding = 80;
                //原点,bottomRight:X轴终点,topLeft:Y轴终点
                let origin = {x: padding+50, y: ch + padding-10};
                let bottomRight = {x: cw - padding, y: ch + padding};
                let topLeft = {x: padding+50, y: padding};
                let YscaleNum = 9;   // Y轴刻度长度
                let Ydata = [];   // Y轴数据存放数组
                ctx.beginPath();
                //绘制Y轴
                ctx.moveTo(origin.x, origin.y);
                ctx.lineTo(topLeft.x, topLeft.y);

                //设置字号
                ctx.font = '16px SimHei';
                ctx.strokeStyle = '#797979';
                ctx.fillText('单位:元',
                    origin.x - 60,
                    topLeft.y+20);
                //绘制Y方向刻度
                // let max = 0;    //最大刻度max
                let step = 0;   // 判断负值个数
                for (let i = 0; i < yz.length; i++) {
                    if (yz[i] < 0) {
                        step++
                    }
                }
                step = Math.floor(step / yz.length * YscaleNum);
                // let min = Math.min.apply(this, yz);
                let sum = yz.reduce((i, n) => {
                    return i + n
                }, 0);
                let min = Math.min.apply(this, yz);
                let max = Math.max.apply(this, yz);
                let avgValue = Math.floor((max - min) / YscaleNum);
                console.log('avgValue',avgValue)
                console.log((max-min)/YscaleNum)
                // step = avgValue < 0 ? step : YscaleNum - step
                console.log(step)
                let avgHeight = (ch - 50) / YscaleNum;
                for (let i = 1; i < YscaleNum + 1; i++) {
                    // let minus = 0
                    // if (avgValue < 0) {
                    //   if (i <= step) {
                    //     minus = 1
                    //   } else {
                    //     minus = -1
                    //   }
                    // } else {
                    //   if (i <= step) {
                    //     minus = -1
                    //   } else {
                    //     minus = 1
                    //   }
                    // }
                    // //绘制Y轴文字
                    // let text = ''
                    // if (i <= step) {
                    //   console.log(i)
                    //   text = minus * avgValue * (step - i)
                    // } else {
                    //   text = minus * avgValue * (i - step)
                    // }
                    let text = min + avgValue*i
                    Ydata.push(text)
                    let txtWidth = ctx.measureText(this.currency(text)).width;
                    ctx.fillText(this.currency(text),
                        origin.x - txtWidth - 5,
                        origin.y - (i) * avgHeight + 6);
                }
                console.log(Ydata)
                ctx.strokeStyle = '#64A1FA';
                ctx.lineWidth = 2;
                ctx.stroke();
                ctx.beginPath();
                //绘制X轴
                ctx.lineWidth = 1;
                ctx.moveTo(origin.x, origin.y+5);
                ctx.lineTo(bottomRight.x + 100, origin.y+5);
                //计算刻度可使用的总宽度
                ctx.stroke();
                ctx.beginPath();
                let avgWidth = (cw - 2 * padding + 70) / (this.arr_integ(xz)[this.arr_integ(xz).length - 1]);

                let s = this.arr_integ(xz)[0]
                let e = this.arr_integ(xz)[this.arr_integ(xz).length - 1]
                e = isNaN(xz[xz.length - 1]) && (e * 1 + 5);
                s = s - s % 5;
                for (let i = s; i <= e; i++) {
                    let jl = i - this.arr_integ(xz)[0]
                    if (i % 5 == 0) {
                        let text = i == obj.retireList ? '退休后' : (i + '岁');
                        if (i > obj.retireList) text = '';
                        // console.log(jl * avgWidth)
                        let txtWidth = ctx.measureText(text).width;   // 文字的宽度
                        ctx.fillText(
                            text,
                            origin.x + jl * avgWidth - txtWidth / 2 + 100,
                            origin.y+25);
                        ctx.stroke();
                        ctx.beginPath();
                        ctx.fillText(
                            '.',
                            origin.x + jl * avgWidth - txtWidth / 2 + 110,
                            origin.y+6);
                    }
                }
                for (let i = 0; i < obj.series.length; i++) {
                    this.creatBar(bottomRight, ctx, origin, step, avgWidth, avgHeight, xz, obj.series[i], i);
                }
                ctx.strokeStyle = '#64A1FA';
                ctx.lineWidth = 1;
                ctx.stroke();
                //绘制折线
                ctx.beginPath();
                for (let i = 0; i < yz.length; i++) {
                    let index = this.getArraySection(Ydata, yz[i])
                    let div = ((yz[i] - Ydata[index]) / Math.abs(avgValue) * avgHeight)
                    let posY = origin.y - ((index + 1) * avgHeight + Number(div));
                    let jl = xz[i].replace(/岁/, "") - this.arr_integ(xz)[0];

                    if (i == 0) {
                        ctx.moveTo(origin.x + jl * avgWidth + 70, Number(posY.toFixed(2)));
                    } else {
                        ctx.lineTo(origin.x + jl * avgWidth + 70, Number(posY.toFixed(2)));
                    }

                }
                ctx.strokeStyle = '#FF8C11';
                ctx.lineWidth = 2;
                ctx.stroke();
                //0刻度
                let zeroIdx = Ydata.findIndex(ele => {
                    return ele === 0
                }) + 1
                if (zeroIdx != 0) {
                    ctx.beginPath();
                    ctx.strokeStyle = '#64A1FA';
                    ctx.lineWidth = 2;
                    ctx.moveTo(origin.x, origin.y - zeroIdx * avgHeight);
                    ctx.lineTo(bottomRight.x + 100, origin.y - zeroIdx * avgHeight);
                    ctx.stroke();
                }
            },
            // 画柱状图
            creatBar(bottomRight, ctx, origin, step, avgWidth, avgHeight, xz, bar, hd) {
                ctx.fillStyle = bar.color;   //柱状图的颜色
                let sc = bar.data;       //柱状图的长度阈值
                let yHeight = origin.x + hd * 26
                let rectCoor = (sc[0].replace(/岁/, '') - this.arr_integ(xz)[0]) * avgWidth + 70
                let rectWidth = sc[1].replace(/岁/, '') - sc[0].replace(/岁/, '');
                rectWidth = rectWidth || (this.arr_integ(xz)[this.arr_integ(xz).length - 1] - sc[0].replace(/岁/, '') + 5)
                ctx.fillRect(origin.x + rectCoor, yHeight, rectWidth * avgWidth, 20);
                ctx.font = '13px SimHei';
                ctx.fillStyle = '#666';
                ctx.lineWidth = 1;
                ctx.fillText(bar.name,
                    origin.x + rectCoor + rectWidth * avgWidth + 5,
                    yHeight + 15
                );
                // 画竖向标的
                if (bar.target) {
                    for (let i = 0; i < bar.target.length; i++) {
                        this.target(ctx, origin, step, avgWidth, avgHeight, xz, bar.target[i], yHeight)
                    }
                }
            },

            // bar 位置定位
            // 数组去掉汉字
            arr_integ(arr) {
                let newArr = [];
                arr = arr.slice(0, -1)
                newArr = arr.map(item => {
                    return item.replace(/岁/, "")
                })
                return newArr
            },
            target(ctx, origin, step, avgWidth, avgHeight, xz, obj, hd) {
                let name = obj.name;
                let tar = obj.data;
                let x = origin.x + (tar.replace(/岁/, '') - this.arr_integ(xz)[0]) * avgWidth + 70;
                // - hd * avgHeight
                let sH = origin.y - (step + 1) * avgHeight;
                console.log(hd)
                ctx.fillStyle = '#64A1FA '
                console.log(sH)
                if (hd > sH) {
                    hd += 50;
                    ctx.moveTo(x, sH)
                    ctx.lineTo(x, hd);
                    ctx.fillText(name, x, hd - 10);
                } else {
                    hd -= 30;
                    ctx.moveTo(x, sH)
                    ctx.lineTo(x, hd);
                    ctx.fillText(name, x, hd + 20);
                }
            },
            getInitData() {     // 获取数据
                let cis = this.$store.state.userInfo.cis;
                let planID = this.$store.state.planId;
                const params = {
                    functionName: "wealthmanagementbiz.service.CustomerAchievementService",
                    methodName: "getPlanCareerSimulation",
                    data: {
                        cis: cis,
                        planId: planID
                    }
                };
                this.http(JSON.stringify(params))
                    .then(res => {
                        console.log(res)
                        console.log('lengthrrrrrrrrrrrrrr',res.data.data.list.length)
                        if (!res.data.data.list.length) {
                            document.getElementsByClassName('canvasDom')[0].innerHTML = '暂无数据'
                            return
                        }
                        this.Arrangement(res.data)
                    })
                    .catch(err => {
                        document.getElementsByClassName('canvasDom')[0].innerHTML = '暂无数据'
                        console.log(err)
                    })
            },
            getArraySection(arr, num) {      // 数据第一次出现的下标
                if (arr[arr.length - 1] < num) return arr.length - 1;
                if (arr[0] > num) return 0;
                for (let i = 0; i < arr.length; i++) {
                    if (arr[i] > num) {
                        return i - 1;
                    }
                }
            },
            Arrangement(data) {   // 整理数据
                console.log(data)

                let x = data.totalNetIncome.map(ele => {
                    return `${ele.year}岁`
                });
                let y = data.totalNetIncome.map(ele => {
                    return ele.totalNetIncome
                });

                let series = [];
                for (let i = 0; i < data.data.list.length; i++) {
                    // debugger
                    if(data.data.list[i] !== null) {
                        if (data.data.list[i].data.length !== 0) {
                            this.legend.push(data.data.list[i].name)
                            series.push({
                                name: data.data.list[i].name,
                                text: data.data.list[i].name,
                                data: data.data.list[i].data.map(ele => {
                                    return ele + '岁'
                                }),
                                color: this.color[i]
                            })
                        }
                    }


                }
                this.init({
                    x: x,
                    y: y,
                    retireList: data.data.list[0].name==="养老规划"?data.data.list[0].data[0]:'100',
                    series: series
                })
            },
            currency (value, currency, decimals){
                const digitsRE = /(\d{3})(?=\d)/g
                value = parseFloat(value)
                if (!isFinite(value) || (!value && value !== 0)) return ''
                currency = currency != null ? currency : ''
                decimals = decimals != null ? decimals : 0
                var stringified = Math.abs(value).toFixed(decimals)
                var _int = decimals
                    ? stringified.slice(0, -1 - decimals)
                    : stringified
                var i = _int.length % 3
                var head = i > 0
                    ? (_int.slice(0, i) + (_int.length > 3 ? ',' : ''))
                    : ''
                var _float = decimals
                    ? stringified.slice(-1 - decimals)
                    : ''
                var sign = value < 0 ? '-' : ''
                return sign + currency + head +
                    _int.slice(i).replace(digitsRE, '$1,') +
                    _float
            },
        },
        mounted() {
            this.getInitData();


        }
    }
</script>

<style scoped lang="scss">
    .legend{
        display: flex;
        justify-content: center;
        align-items: center;
    }
    .canvasDom {
        text-align: center;
        font-size: 24px;
        color: crimson;
        span{
            font-size: 14px;
        }
    }
    .classBar{
        width: 20px;
        height: 10px;
        border-radius: 4px;
        display: inline-block;
        margin-left: 10px;
    }
    .classLine{
        width: 20px;
        height: 2px;
        transform: translateY(-4px);
        border-radius: 4px;
        display: inline-block;
        margin-left: 10px;
    }
</style>

​

2.使用

<el-dialog
                title="生涯仿真图"
                center
                :visible.sync="isShowChart"
                width="70%"
                :before-close="handleClose"
>
            <career-chart :id="'career4'"></career-chart>
</el-dialog>
import careerChart from "@/components/modules/CareerSimulationChart/CareerSimulationChart";
components: {careerChart},

猜你喜欢

转载自blog.csdn.net/weixin_43837268/article/details/89598424
今日推荐