Graphic

描述:当前是关于Echarts图表中的 折线图 示例。
 
            var bgPatternImg = new Image();
bgPatternImg.setAttribute("crossOrigin",'Anonymous');
bgPatternImg.src = 'http://image.91chep.com/3b731aac1f9441c7b505c7c7357e2e43680_680.jpg';


 
let pointList = [{
        name: '测点1',
        position: [250, 10],
        color: 'green',
        items: [{
                name: '沉降',
                value: 132
            },
            {
                name: '温度',
                value: 12
            },
        ]
    },
    {
        name: '测点2',
        position: [323, 36],
        color: 'green',
        items: [{
            name: '沉降',
            value: 132
        }, ]
    },
    {
        name: '测点3',
        position: [360, 50],
        color: 'green',
        items: [{
                name: '沉降测点3测点3测点3测点3测',
                value: 132
            },
            {
                name: '温度',
                value: 12
            },
        ]
    },
    {
        name: '测点4',
        position: [300, 150],
        color: 'skyblue',
        items: [{
                name: '沉降',
                value: 132
            },
            {
                name: '温度',
                value: 12
            },
        ]
    },
    {
        name: '测点5',
        position: [440, 150],
        color: 'skyblue',
        items: [{
                name: '沉降',
                value: 132
            },
            {
                name: '温度',
                value: 12
            },
        ]
    },
    {
        name: '测点6',
        position: [480, 150],
        color: 'skyblue',
        items: [{
                name: '沉降',
                value: 132
            },
            {
                name: '温度',
                value: 12
            },
        ]
    },
    {
        name: '测点7',
        position: [480, 80],
        color: 'red',
        items: [{
                name: '沉降',
                value: 132
            },
            {
                name: '温度',
                value: 12
            },
        ]
    },
];

let lineList = [{
        name: '测线1',
        nodes: [{
            name: '测点1'
        }, {
            name: '测点2'
        }, {
            name: '测点3'
        }, ]
    },
    {
        name: '测线2',
        nodes: [{
            name: '测点4'
        }, {
            name: '测点5'
        }, {
            name: '测点6'
        }, ]
    }
];

option = {
    graphic: [{
            // 拖拽的透明面板
            id: 'toolPan',
            type: 'rect',
            z: 2, // z 方向上的高度
            position: [200, 0],
            invisible: true, // 节点不可见,不然会挡住底图
            style: {
                fill: '#ccc'
            },
            //draggable: true,
            cursor: 'all-scroll',
            shape: {
                height: 300,
                width: 600,
            },
            //ondrag: groupCanDrag(), // 拖动组
            onmousedown: dragPanStart(),
            onmouseup: dragPanEnd(),
            onmousemove: dragPan(),
            onmouseout: dragPanEnd(),
            onmousewheel: groupCanZoom('group1', 'toolPan'), // 缩放组
            onclick: e => {
                //const curPos = vecSub([e.offsetX, e.offsetY])
                // console.log(curPos)
                panPosition = myChart.getOption().graphic[0].elements[0].position;
                console.log(panPosition)
            },
        },
        {
            type: 'group',
            id: 'group1',
            scale: [1, 1],
            position: [0, 0],
            children: [{
                    // 背景图
                    type: 'image',
                    id: 'bg',
                    position: [200, 0],
                    //bounding: 'raw',
                    // origin: [75, 75],
                    z: 1,
                    style: {
                        image: bgPatternImg
                    },
                    shape: {
                        height: 300,
                        width: 600,
                    }
                },
                //{
                //     id: 'p1',
                //     type: 'circle',
                //     position: [10, 10], // 圆心的坐标位置
                //     draggable: true,
                //     z: 3,
                //     style: {
                //         fill: 'red'
                //     },
                //     shape: {
                //         r: 10 // 半径
                //     }
                // },
                // {
                //     id: 'p2',
                //     type: 'circle',
                //     position: [100, 10],
                //     draggable: true,
                //     z: 3,
                //     style: {
                //         fill: 'red'
                //     },
                //     shape: {
                //         r: 10 // 半径
                //     }
                // },
                // {
                //     id: 'l1',
                //     type: 'line',
                //     // position: [100, 10],
                //     draggable: true,
                //     z: 2,
                //     style: {
                //         fill: 'red'
                //     },
                //     shape: {
                //         x1: 10,
                //         y1: 10,
                //         x2: 100,
                //         y2: 10
                //     }
                // },
                {
                    type: 'group',
                    id: 'tooltip',
                    ignore: true,
                    children: [{
                            type: 'rect',
                            id: 'tooltip-bg',
                            z: 100,
                            left: 'center',
                            shape: {
                                width: 190,
                                height: 90
                            },
                            style: {
                                fill: 'rgba(22,50,79,0.7)',
                                stroke: '#555',
                                lineWidth: 0,
                                shadowBlur: 8,
                                shadowOffsetX: 0,
                                shadowOffsetY: 2,
                                shadowColor: 'rgba(0,0,0,0.15)'
                            }
                        }, {
                            type: 'text',
                            id: 't-text',
                            z: 100,
                            left: 'center',
                            top: 10,
                            style: {
                                fill: '#fff',
                                textAlign: 'center',
                                text: '横轴表示温度,单位是°C',
                                font: '400 14px Microsoft YaHei'
                            }
                        },
                        {
                            type: 'text',
                            id: 't-text0',
                            z: 100,
                            left: 'center',
                            top: 10,
                            style: {
                                fill: '#fff',
                                textAlign: 'center',
                                text: '横轴表示温度,单位是°C',
                                font: '400 14px Microsoft YaHei'
                            }
                        },
                        {
                            type: 'text',
                            id: 't-text1',
                            z: 100,
                            left: 'center',
                            top: 30,
                            style: {
                                fill: '#fff',
                                textAlign: 'center',
                                text: ',单位是°C',
                                font: '400 14px Microsoft YaHei'
                            }
                        }
                    ]
                }

            ]

        },
        {
            id: 'legend',
            type: 'group',
            children: [{
                id: 'legend-bg',
                type: 'rect',
                z: 66,
                cursor: 'default',
                shape: {
                    width: 200,
                    height: 300
                },
                style: {
                    fill: 'yellow'
                }
            }]
        }
    ]
};

/*
let dragMoveV = [0, 0]; // 拖动产生的偏移
let pointListCopy
// 拖动组
function groupCanDrag() {
    return e => {
        const curPosition = e.target.position; // 拖拽面板的位置
        dragMoveV = curPosition;
        //移动背景图
        myChart.setOption({
            graphic: [{
                    id: 'group1',
                    $action: 'merge',
                    position: vecSub(curPosition, scaleMoveV)
                },
            ]
        });
    };
}*/

let isDrag // 是否在拖动
let dragOrigin //拖动的起始点
let panPosition = [0, 0]; // 拖拽盘的位置
function dragPanStart() {

    return e => {
        isDrag = true;
        dragOrigin = [e.offsetX, e.offsetY];
        panPosition = myChart.getOption().graphic[0].elements[0].position;
    }

}

let pointListCopy;

function dragPan() {
    return e => {

        if (!isDrag) return;
        const curPos = [e.offsetX, e.offsetY]
        const moveV = vecSub(curPos, dragOrigin);
        pointListCopy = JSON.parse(JSON.stringify(pointList));
        pointListCopy.forEach(point => {
            point.position = vecAdd(point.position, moveV)
        })
        updatePoints(pointListCopy);
        drawLine(lineList, pointListCopy);


        myChart.setOption({
            graphic: [{
                    id: 'toolPan',
                    $action: 'merge',
                    position: vecAdd(moveV, panPosition),
                },
                {
                    id: 'bg',
                    $action: 'merge',
                    position: vecAdd(moveV, panPosition),
                }
            ],

        })
    }

}

function dragPanEnd() {
    return e => {
        isDrag = false;
        dragOrigin = undefined;
        if (!pointListCopy) return
        pointList = pointListCopy;
        panPosition = myChart.getOption().graphic[0].elements[0].position;
    }

}


let curScale = 1.00;
// 缩放 group
function groupCanZoom(groupId, dragPanId) {
    return e => {
        let curPointerPos = [e.offsetX, e.offsetY];
        const minScale = 0.5; // 最小缩小尺寸
        const maxScale = 5; // 最大放大尺寸
        const scaleStep = 1.25; // 每次滚轮滑动改变的尺寸
        const isWheelUp = e.wheelDelta > 0;
        scale = isWheelUp ? scaleStep : (1 / scaleStep);


      

        
        
       if (curScale * scale < minScale || curScale * scale > maxScale) return;
       curScale = curScale * scale;

        //const pointListCopy = JSON.parse(JSON.stringify(pointList)); // 深拷贝
        pointList.forEach(point => {
            const tmp1 = vecSub(point.position, curPointerPos);
            const tmp2 = vecScale(tmp1, scale);
            point.position = vecAdd(tmp2, curPointerPos);
        })
        updatePoints(pointList);
        drawLine(lineList, pointList)
        panPosition = myChart.getOption().graphic[0].elements[0].position;
        // 计算背景图原点的偏移
        const temp1 = vecSub(panPosition, curPointerPos);
        const temp2 = vecScale(temp1, scale);
        panPosition = vecAdd(temp2, curPointerPos)

        myChart.setOption({
            graphic: [{
                    id: 'bg',
                    $action: 'merge',
                    scale: [curScale, curScale],
                    shape: {
                        height: 300 * curScale,
                        width: 600 * curScale,
                    },
                    position: panPosition,
                },

                {
                    id: 'toolPan',
                    $action: 'merge',
                    shape: {
                        height: 300 * curScale,
                        width: 600 * curScale,
                    },
                    position: panPosition
                }
            ]
        });


    };
}

// 画点
function drawPoint(points) {
    const pointsInGroup = [];
    // 给每个点设置 circle 的配置
    points.forEach(point => {
        const pointOpt = {
            id: point.name,
            name: point.name,
            type: 'circle',
            items: point.items,
            position: point.position,
            draggable: true,
            z: 3,
            style: {
                fill: point.color || 'black'
            },
            shape: {
                r: 10 // 半径
            },
            ondrag: onDragPoint(lineList, pointList),
            onclick: onPointClick(),
            onmouseover: showTooltip(),
            onmousemove: showTooltip(),
            onmouseout: hideTooltip(),
        };
        pointsInGroup.push(pointOpt);
    });
    myChart.setOption({
        graphic: [{
            id: 'group1',
            type: 'group',
            $action: 'merge',
            children: pointsInGroup
        }]
    });

}

// 更新点
function updatePoints(points) {
    const pointsOpt = [];
    points.forEach(point => {
        pointsOpt.push({
            id: point.name,
            $action: 'merge',
            position: point.position
        })
    });
    myChart.setOption({
        graphic: pointsOpt
    })
}

// 画线
function drawLine(lines, points) {
    const linesInGroup = [];
    lines.forEach(line => {
        const pointsInLine = [];
        line.nodes.forEach(item => {
            points.forEach(point => {
                if (item.name === point.name) pointsInLine.push(point.position)
            })
        });
        const lineOpt = {
            id: line.name,
            type: 'polyline',
            z: 2,
            style: {
                stroke: '#838A9D',
                lineWidth: 2,
            },
            shape: {
                points: pointsInLine,
            },
            onclick: onClickLine()
        };
        linesInGroup.push(lineOpt);

    });
    myChart.setOption({
        graphic: [{
            id: 'group1',
            type: 'group',
            $action: 'merge',
            children: linesInGroup
        }]
    });

}

// 当移动点时,重新画线
function onDragPoint(lines, points) {
    return e => {
        const curPointName = e.target.__ecGraphicId;
        const curPointPosition = [e.offsetX, e.offsetY];
        pointList.forEach(point => {
            if (point.name === curPointName) {
                point.position = curPointPosition;

            }
        });
        // 只从新画该测点的测线
        const linesNeedDraw = lines.filter(line => {
            if (line.nodes.filter(item => item.name === curPointName).length) return true;
        });
        updatePoints(pointList);
        drawLine(linesNeedDraw, pointList);
    };
}

let lastClickedPoint = '';
// 鼠标点击测点,测点放大和加上阴影
function onPointClick() {
    // 放大后的scale
    magnifyScale = 1.2;
    return e => {
        const curPoint = e.target;
        const curPointName = curPoint.__ecGraphicId;
        const curScale = curPoint.scale[0];
        const curColor = curPoint.style.fill;
        const scale = curScale === magnifyScale ? 1 : magnifyScale;
        optList = [{
            id: curPointName,
            $action: 'merge',
            scale: [scale, scale],
            style: {
                shadowBlur: curScale === magnifyScale ? 0 : 4,
                shadowColor: curColor
            }
        }];
        // 取消上次的选中
        if (lastClickedPoint && lastClickedPoint !== curPointName) {
            optList.push({
                id: lastClickedPoint,
                $action: 'merge',
                scale: [1, 1],
                style: {
                    shadowBlur: 0,
                }
            });
            lastClickedPoint = '';
        }
        myChart.setOption({
            graphic: optList
        });
        lastClickedPoint = curPointName;
    }
}

lastClickedLine = ''
// 点击线后放大线
function onClickLine() {
    // 放大后的线宽
    magnifyWidth = 4.5;
    return e => {
        const curLine = e.target;
        const curLineName = curLine.__ecGraphicId;
        const curLineWidth = curLine.style.lineWidth;
        optList = [{
            id: curLineName,
            $action: 'merge',
            style: {
                lineWidth: curLineWidth === magnifyWidth ? 2 : magnifyWidth,
            }
        }];
        // 取消上次的选中
        if (lastClickedLine && lastClickedLine !== curLineName) {
            optList.push({
                id: lastClickedLine,
                $action: 'merge',
                style: {
                    lineWidth: 2,
                }
            });
            lastClickedLine = '';
        }
        myChart.setOption({
            graphic: optList
        });
        lastClickedLine = curLineName;
    }
}

// 还原上次放大的点
/*
function resizeLastPoint() {
    if (!lastClickedPoint) return;
    console.log(lastClickedPoint);
    optList = [{
        id: lastClickedPoint,
        $action: 'merge',
        scale: [1, 1],
    }];
    myChart.setOption({
        graphic: optList
    });
    lastClickedPoint = '';
}
*/


// 显示 tooltip
function showTooltip() {
    return e => {
        const point = e.target;

        let maxLen = 0; // 最长字符数
        point.items.forEach(item => {
            const str = item.name + item.value;
            maxLen = str.length > maxLen ? str.length : maxLen;
        });

        const width = maxLen * 14 + 20;
        const height = point.items ? point.items.length * 20 + 40 : 40;
        const position = point.position;
        let tipX;
        let tipY;

        if (position[0] < width + 10) {
            // x 坐标过小,tooltip 在右方
            tipX = position[0] + width / 2 + 10;
        } else {
            tipX = position[0] - width / 2 - 10;
        }

        if (position[1] < height + 10) {
            // y 坐标过小,tooltip 在下方
            tipY = position[1] + 10;
        } else {
            tipY = position[1] - height - 10;
        }

        const textList = [{
            type: 'text',
            id: 't-text',
            $action: 'merge',
            top: 10,
            style: {
                text: point.name,
            }
        }]
        point.items.forEach((item, i) => {
            textOpt = {
                type: 'text',
                id: 't-text' + i,
                $action: 'merge',
                top: 10 + (i + 1) * 20,
                style: {
                    fill: '#fff',
                    textAlign: 'center',
                    text: item.name + ': ' + item.value,
                    font: '400 14px Microsoft YaHei'
                }
            }
            textList.push(textOpt)
        })
        // 先把 tooltip 内容置空
        myChart.setOption({
            graphic: [{
                    id: 't-text',
                    $action: 'merge',
                    style: {
                        text: '',
                    }
                },
                {
                    id: 't-text0',
                    $action: 'merge',
                    style: {
                        text: '',
                    }
                },
                {
                    id: 't-text1',
                    $action: 'merge',
                    style: {
                        text: '',
                    }
                }
            ]
        });
        myChart.setOption({
            graphic: [{
                    id: 'tooltip',
                    ignore: false,
                    $action: 'merge',
                    position: [tipX, tipY],
                }, {
                    id: 'tooltip-bg',
                    $action: 'merge',
                    shape: {
                        height,
                        width
                    }
                },
                ...textList
            ]
        });

    }
}

//隐藏 tooltip
function hideTooltip() {
    return e => {
        myChart.setOption({
            graphic: {
                id: 'tooltip',
                ignore: true,
                $action: 'merge',
            }
        });
    }
}

function vecAdd(vec1, vec2) {
    if (vec1.length < 2 || vec2.length < 2) return
    return [vec1[0] + vec2[0], vec1[1] + vec2[1]]
}

function vecSub(vec1, vec2) {
    if (vec1.length < 2 || vec2.length < 2) return
    return [vec1[0] - vec2[0], vec1[1] - vec2[1]]
}

function vecScale(vec, size) {
    if (vec.length < 2) return
    return [vec[0] * size, vec[1] * size]
}
// 取反向量
function vecNegate(vec) {
    if (vec.length < 2) return
    return [-vec[0], -vec[1]]
}

// 画图例
function drawLegend() {

    const legendOpt = pointList.map((point, i) => {
        return {
            id: 'legend' + i,
            type: 'group',
            position: [10, 25 * i],
            children: [{
                        // 点击的透明面板
                        id: 'legend' + i + '-pan',
                        pId: point.name,
                        legendId: 'legend' + i,
                        color: point.color,
                        type: 'rect',
                        z: 67, // z 方向上的高度
                        position: [0,0],
                        invisible: true, // 节点不可见,不然会挡住底图
                        shape: {
                            height: 20,
                            width: 100,
                        },
                        onclick: onLegendClick()
                    },

                    {
                        id: 'legend' + i + '-c',
                        type: 'circle',
                        position: [10, 10], // 圆心的坐标位置
                        z: 66,
                        silent: true,
                        style: {
                            fill: point.color
                        },
                        shape: {
                            r: 10 // 半径
                        }
                    },
                    {
                        id: 'legend' + i + '-t',
                        type: 'text',
                        position: [30, 10 - 5], // 圆心的坐标位置
                        z: 66,
                        silent: true,
                        style: {
                            text: point.name,
                            font: 'bolder 14px "Microsoft YaHei", sans-serif',
                            fill: point.color
                        },
                    },]
        }

    });


    myChart.setOption({
        graphic: [{
            id: 'legend',
            type: 'group',
            $action: 'merge',
            children: legendOpt
        }]
    });

}

// 点击图例
function onLegendClick() {
    return e => {
        const legend = e.target;
        const pId = legend.pId;
        const legendId = legend.legendId
        if (!legend.isHide) {
            myChart.setOption({
                graphic:[{
                    id: legendId + '-c',
                    style: {
                        fill: 'rgba(230,233,239,1)',
                    }
                },
                {
                    id: legendId + '-t',
                    style: {
                        fill: 'rgba(230,233,239,1)',
                    }
                },
                {
                    id: pId,
                    ignore: true,
                }
                ]
            });
            legend.isHide = true;
            
        }else {
              myChart.setOption({
                graphic:[{
                    id: legendId + '-c',
                    style: {
                        fill: legend.color,
                    }
                },
                {
                    id: legendId + '-t',
                    style: {
                        fill: legend.color,
                    }
                },
                 {
                    id: pId,
                    ignore: false,
                }
                ]
            });
            legend.isHide = false;
        } 
        
    }
}


setTimeout(function() {
    drawPoint(pointList);
    drawLine(lineList, pointList);
    drawLegend();
}, 100);