qml折线面积图样式

demo01

v1无接口无坐标轴测试

TSAreaLine.qml
javascript 复制代码
import QtQuick 2.12

Item {
    id: lineChart
    // ===================== 外部可配置属性 ======================
    property real xMin: 0
    property real xMax: 10
    property real yMin: 0
    property real yMax: 100
    property string xLabel: "时间"
    property string xUnit: "秒"
    property string yLabel: "数值"
    property string yUnit: ""
    property int xTicks: 10
    property int yTicks: 10
    property int textSize: 12
    property bool dynamicMargin: true
    property real baseMargin: 40
    property var lineConfigs: []

    // 全局基础透明度提高到0.9
    property real fillOpacityBase: 0.9
    // 底部更透(0.1),顶部不透(1.0)
    property real topOpacityRatio: 1.0
    property real bottomOpacityRatio: 0.1

    // 新增:顶部颜色亮度提升比例(0.1=10%)
    property real topColorBrightnessRatio: 0.1

    // 默认填充样式(仅保留颜色,透明度由垂直渐变控制)
    property var defaultFillStyle: {
        "gradientStart": "#00e5ff",
        "gradientEnd": "#7c4dff",
        "gradientDirection": "vertical"
    }

    property color lineStrokeColor: "#ffffff"
    property real lineStrokeWidth: 1.5
    property int pointRadius: 3

    // ===================== 内部属性 ======================
    property color axisColor: "#333333"
    property color gridColor: "#e0e0e0"
    property real actualLeftMargin: baseMargin
    property real actualRightMargin: 20
    property real actualTopMargin: 20
    property real actualBottomMargin: baseMargin

    // ===================== 外部接口 ======================
    function setAxisConfig(config) {
        lineChart.xMin = config.xMin !== undefined ? config.xMin : lineChart.xMin;
        lineChart.xMax = config.xMax !== undefined ? config.xMax : lineChart.xMax;
        lineChart.yMin = config.yMin !== undefined ? config.yMin : lineChart.yMin;
        lineChart.yMax = config.yMax !== undefined ? config.yMax : lineChart.yMax;
        lineChart.xLabel = config.xLabel || lineChart.xLabel;
        lineChart.xUnit = config.xUnit || lineChart.xUnit;
        lineChart.yLabel = config.yLabel || lineChart.yLabel;
        lineChart.yUnit = config.yUnit || lineChart.yUnit;
        lineChart.xTicks = config.xTicks !== undefined ? config.xTicks : lineChart.xTicks;
        lineChart.yTicks = config.yTicks !== undefined ? config.yTicks : lineChart.yTicks;
        updateChart();
    }

    function setLineConfigs(configs) {
        lineChart.lineConfigs = configs || [];
        updateChart();
    }

    function setAllLineFillStyles(style) {
        for (let i = 0; i < lineConfigs.length; i++) {
            lineConfigs[i].fillStyle = Object.assign({}, lineConfigs[i].fillStyle || {}, style);
        }
        updateChart();
    }

    function setLineFillStyle(index, style) {
        if (index >= 0 && index < lineConfigs.length) {
            lineConfigs[index].fillStyle = Object.assign({}, lineConfigs[index].fillStyle || {}, style);
            updateChart();
        }
    }

    function setAllLineFillOpacity(opacity) {
        lineChart.fillOpacityBase = Math.max(0, Math.min(1, opacity));
        updateChart();
    }

    function setVerticalOpacityRatio(topRatio, bottomRatio) {
        lineChart.topOpacityRatio = Math.max(0, Math.min(1, topRatio));
        lineChart.bottomOpacityRatio = Math.max(0, Math.min(1, bottomRatio));
        updateChart();
    }

    // 新增:设置顶部颜色亮度
    function setTopColorBrightness(ratio) {
        lineChart.topColorBrightnessRatio = Math.max(-1, Math.min(1, ratio));
        updateChart();
    }

    function updateChart() {
        calculateDynamicMargin();
        chartCanvas.requestPaint();
    }

    // ===================== 内部工具函数 ======================
    function calculateDynamicMargin() {
        if (!dynamicMargin) {
            actualLeftMargin = baseMargin;
            actualBottomMargin = baseMargin;
            return;
        }

        let ctx = chartCanvas.getContext("2d");
        ctx.font = textSize + "px Arial";
        let maxYTickTextWidth = ctx.measureText(yMax.toString()).width;
        let yLabelWithUnit = yLabel + (yUnit ? "(" + yUnit + ")" : "");
        let yLabelWidth = ctx.measureText(yLabelWithUnit).width;
        actualLeftMargin = baseMargin + maxYTickTextWidth + yLabelWidth/2 + 10;

        let maxXTickTextHeight = textSize + 5;
        let xLabelWithUnit = xLabel + (xUnit ? "(" + xUnit + ")" : "");
        let xLabelHeight = textSize + 5;
        actualBottomMargin = baseMargin + maxXTickTextHeight + xLabelHeight + 5;

        let maxMargin = Math.min(width/4, height/4);
        actualLeftMargin = Math.min(actualLeftMargin, maxMargin);
        actualBottomMargin = Math.min(actualBottomMargin, maxMargin);
    }

    function dataToCanvasX(x) {
        let xRange = xMax - xMin;
        let canvasWidth = chartCanvas.width - actualLeftMargin - actualRightMargin;
        return actualLeftMargin + (x - xMin) * (canvasWidth / xRange);
    }

    function dataToCanvasY(y) {
        let yRange = yMax - yMin;
        let canvasHeight = chartCanvas.height - actualTopMargin - actualBottomMargin;
        return chartCanvas.height - actualBottomMargin - (y - yMin) * (canvasHeight / yRange);
    }

    // 工具函数:十六进制颜色转RGBA
    function hexToRgba(hex, alpha) {
        hex = hex.replace(/^#/, '');
        let r = parseInt(hex.substring(0, 2), 16);
        let g = parseInt(hex.substring(2, 4), 16);
        let b = parseInt(hex.substring(4, 6), 16);
        return `rgba(${r}, ${g}, ${b}, ${alpha})`;
    }

    // 新增:调整颜色亮度(核心!实现顶部变亮)
    function adjustColorBrightness(hex, brightness) {
        hex = hex.replace(/^#/, '');
        let r = parseInt(hex.substring(0, 2), 16);
        let g = parseInt(hex.substring(2, 4), 16);
        let b = parseInt(hex.substring(4, 6), 16);

        // 调整亮度:brightness>0变亮,<0变暗,范围-1到1
        r = Math.round(Math.min(255, Math.max(0, r + r * brightness)));
        g = Math.round(Math.min(255, Math.max(0, g + g * brightness)));
        b = Math.round(Math.min(255, Math.max(0, b + b * brightness)));

        // 转回十六进制
        let toHex = (n) => n.toString(16).padStart(2, '0');
        return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
    }

    // ===================== Canvas绘制(核心修改)=====================
    Canvas {
        id: chartCanvas
        anchors.fill: parent
        antialiasing: true
        renderTarget: Canvas.FramebufferObject

        onPaint: {
            let ctx = getContext("2d")
            ctx.reset()
            let w = width
            let h = height

            // 清空画布
            ctx.fillStyle = "#1e1e1e"
            ctx.fillRect(0, 0, w, h)

            if (xMax <= xMin || yMax <= yMin) {
                return;
            }

            if (lineConfigs.length > 0) {
                // 逆序绘制折线
                for (let lineIdx = lineConfigs.length - 1; lineIdx >= 0; lineIdx--) {
                    let lineCfg = lineConfigs[lineIdx]
                    let dataPoints = lineCfg.dataPoints || []
                    if (dataPoints.length < 2) continue;

                    // 获取填充样式
                    let fillStyle = lineCfg.fillStyle || lineChart.defaultFillStyle;
                    let gradientStart = fillStyle.gradientStart || lineChart.defaultFillStyle.gradientStart;
                    let gradientEnd = fillStyle.gradientEnd || lineChart.defaultFillStyle.gradientEnd;
                    let gradientDirection = fillStyle.gradientDirection || lineChart.defaultFillStyle.gradientDirection;

                    // ========== 核心修改1:顶部颜色变亮10% ==========
                    let brightenedTopColor = adjustColorBrightness(gradientEnd, lineChart.topColorBrightnessRatio);

                    // ========== 核心修改2:创建渐变(透明度+亮度) ==========
                    let gradient;
                    let fillTopY = dataToCanvasY(yMax);
                    let fillBottomY = dataToCanvasY(yMin);

                    if (gradientDirection === "horizontal") {
                        gradient = ctx.createLinearGradient(0, 0, w, 0);
                        gradient.addColorStop(0, hexToRgba(gradientStart, lineChart.fillOpacityBase));
                        gradient.addColorStop(1, hexToRgba(brightenedTopColor, lineChart.fillOpacityBase));
                    } else {
                        // 垂直渐变:底部更透(0.1),顶部不透(1.0)+变亮
                        gradient = ctx.createLinearGradient(0, fillBottomY, 0, fillTopY);
                        // 底部:原颜色 + 低透明度(0.9*0.1=0.09)
                        let bottomAlpha = lineChart.fillOpacityBase * lineChart.bottomOpacityRatio;
                        gradient.addColorStop(0, hexToRgba(gradientStart, bottomAlpha));
                        // 顶部:亮10%的颜色 + 高透明度(0.9*1.0=0.9)
                        let topAlpha = lineChart.fillOpacityBase * lineChart.topOpacityRatio;
                        gradient.addColorStop(1, hexToRgba(brightenedTopColor, topAlpha));
                    }

                    // 绘制填充区域
                    ctx.beginPath();
                    let firstPoint = dataPoints[0];
                    let firstCanvasX = dataToCanvasX(firstPoint.x);
                    let firstCanvasY = dataToCanvasY(firstPoint.y);
                    ctx.moveTo(firstCanvasX, firstCanvasY);

                    // 绘制折线
                    for (let pointIdx = 1; pointIdx < dataPoints.length; pointIdx++) {
                        let point = dataPoints[pointIdx];
                        let canvasX = dataToCanvasX(point.x);
                        let canvasY = dataToCanvasY(point.y);
                        ctx.lineTo(canvasX, canvasY);
                    }

                    // 闭合路径
                    let lastPoint = dataPoints[dataPoints.length - 1];
                    let lastCanvasX = dataToCanvasX(lastPoint.x);
                    let xAxisY = dataToCanvasY(yMin);
                    ctx.lineTo(lastCanvasX, xAxisY);
                    ctx.lineTo(firstCanvasX, xAxisY);
                    ctx.closePath();

                    // 填充渐变(透明度+亮度已融入)
                    ctx.fillStyle = gradient;
                    ctx.fill();

                    // 绘制折线描边
                    ctx.beginPath();
                    ctx.moveTo(firstCanvasX, firstCanvasY);
                    for (let pointIdx = 1; pointIdx < dataPoints.length; pointIdx++) {
                        let point = dataPoints[pointIdx];
                        let canvasX = dataToCanvasX(point.x);
                        let canvasY = dataToCanvasY(point.y);
                        ctx.lineTo(canvasX, canvasY);
                    }
                    ctx.strokeStyle = lineCfg.lineColor || lineChart.lineStrokeColor;
                    ctx.lineWidth = lineCfg.lineWidth || lineChart.lineStrokeWidth;
                    ctx.stroke();

                    // 绘制数据点
                    ctx.fillStyle = lineCfg.lineColor || lineChart.lineStrokeColor;
                    for (let pointIdx = 0; pointIdx < dataPoints.length; pointIdx++) {
                        let point = dataPoints[pointIdx];
                        let canvasX = dataToCanvasX(point.x);
                        let canvasY = dataToCanvasY(point.y);
                        ctx.beginPath();
                        ctx.arc(canvasX, canvasY, pointRadius, 0, Math.PI*2);
                        ctx.fill();
                    }
                }
            }
        }
    }

    // 组件初始化
    Component.onCompleted: {
        updateChart();
    }
}
main.qml
javascript 复制代码
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.15

Window {
    id: mainWindow
    width: 1355
    height: 519
    title: "科技风渐变折线图示例"
    visible: true
    color: "#1e1e1e"

    ColumnLayout {
        anchors.fill: parent
        spacing: 20

        TSAreaLine {
            id: myLineChart
            Layout.fillWidth: true
            Layout.fillHeight: true
            textSize: 14
            dynamicMargin: false
            baseMargin: 0
            // 初始参数更新:基础透明度0.9,底部0.1,顶部亮度+0.1
            fillOpacityBase: 0.9
            topOpacityRatio: 1.0
            bottomOpacityRatio: 0.1
            topColorBrightnessRatio: 0.1 // 顶部变亮10%
            defaultFillStyle: {
                "gradientStart": "#4dd0e1",
                "gradientEnd": "#006064",
                "gradientDirection": "vertical"
            }
            lineStrokeColor: "#80deea"
            lineStrokeWidth: 1.5
        }

        RowLayout {
            Layout.alignment: Qt.AlignHCenter
            spacing: 10

            Button {
                text: "显示科技风渐变折线"
                onClicked: {
                    myLineChart.setAxisConfig({
                        xMin: 0,
                        xMax: 28,
                        yMin: 0,
                        yMax: 160,
                        xLabel: "",
                        xUnit: "",
                        yLabel: "",
                        yUnit: "",
                        xTicks: 0,
                        yTicks: 0
                    });
                    myLineChart.setLineConfigs([
                        {
                            dataPoints: [
                                {x:0,y:10}, {x:1,y:5}, {x:2,y:0}, {x:3,y:5},
                                {x:4,y:3}, {x:5,y:8}, {x:6,y:12}, {x:7,y:15},
                                {x:8,y:10}, {x:9,y:18}, {x:10,y:12}, {x:11,y:20},
                                {x:12,y:8}, {x:13,y:5}, {x:14,y:15}, {x:15,y:20},
                                {x:16,y:25}, {x:17,y:20}, {x:18,y:30}, {x:19,y:35},
                                {x:20,y:55}, {x:21,y:30}, {x:22,y:20}, {x:23,y:15},
                                {x:24,y:35}, {x:25,y:45}, {x:26,y:40}, {x:27,y:38},
                                {x:28,y:15}
                            ],
                            fillStyle: {
                                "gradientStart": "#80deea",
                                "gradientEnd": "#004d40"
                            }
                        },
                        {
                            dataPoints: [
                                {x:0,y:15}, {x:1,y:0}, {x:2,y:5}, {x:3,y:10},
                                {x:4,y:18}, {x:5,y:22}, {x:6,y:15}, {x:7,y:35},
                                {x:8,y:40}, {x:9,y:10}, {x:10,y:40}, {x:11,y:20},
                                {x:12,y:35}, {x:13,y:35}, {x:14,y:30}, {x:15,y:35},
                                {x:16,y:40}, {x:17,y:80}, {x:18,y:145}, {x:19,y:135},
                                {x:20,y:105}, {x:21,y:110}, {x:22,y:60}, {x:23,y:45},
                                {x:24,y:50}, {x:25,y:40}, {x:26,y:42}, {x:27,y:10},
                                {x:28,y:5}
                            ],
                            fillStyle: {
                                "gradientStart": "#4dd0e1",
                                "gradientEnd": "#006064"
                            }
                        }
                    ]);
                }
            }

            Button {
                text: "清空折线"
                onClicked: {
                    myLineChart.setLineConfigs([]);
                }
            }

            Button {
                text: "统一修改填充样式"
                onClicked: {
                    myLineChart.setAllLineFillStyles({
                        "gradientStart": "#ff9800",
                        "gradientEnd": "#e64a19"
                    });
                }
            }

            Button {
                text: "修改第一条折线样式"
                onClicked: {
                    myLineChart.setLineFillStyle(0, {
                        "gradientStart": "#4caf50",
                        "gradientEnd": "#1b5e20"
                    });
                }
            }

            Button {
                text: "提高基础透明度(0.9)"
                onClicked: {
                    myLineChart.setAllLineFillOpacity(0.9);
                }
            }

            Button {
                text: "降低基础透明度(0.5)"
                onClicked: {
                    myLineChart.setAllLineFillOpacity(0.5);
                }
            }

            Button {
                text: "底部更透(0.1)"
                onClicked: {
                    myLineChart.setVerticalOpacityRatio(1.0, 0.1);
                }
            }

            Button {
                text: "底部稍透(0.5)"
                onClicked: {
                    myLineChart.setVerticalOpacityRatio(1.0, 0.5);
                }
            }

            // 新增:调整顶部亮度按钮
            Button {
                text: "顶部更亮(+20%)"
                onClicked: {
                    myLineChart.setTopColorBrightness(0.2);
                }
            }

            Button {
                text: "顶部默认亮度"
                onClicked: {
                    myLineChart.setTopColorBrightness(0.1);
                }
            }
        }
    }
}

v2坐标轴+标签+测试接口

TSAreaLine.qml
javascript 复制代码
import QtQuick 2.12

Item {
    id: lineChart

    // ===================== 外部可配置属性 ======================
    // 基础坐标轴配置
    property real xMin: 0
    property real xMax: 10
    property real yMin: 0
    property real yMax: 100
    property string xLabel: "时间"
    property string xUnit: "秒"
    property string yLabel: "数值"
    property string yUnit: ""
    property int xTicks: 10
    property int yTicks: 10
    property int textSize: 12
    property bool dynamicMargin: true
    property real baseMargin: 40
    property var lineConfigs: []

    // 渐变与透明度
    property real fillOpacityBase: 0.9
    property real topOpacityRatio: 1.0
    property real bottomOpacityRatio: 0.1
    property real topColorBrightnessRatio: 0.1

    property var defaultFillStyle: {
        "gradientStart": "#00e5ff",
        "gradientEnd": "#7c4dff",
        "gradientDirection": "vertical"
    }

    property color lineStrokeColor: "#ffffff"
    property real lineStrokeWidth: 1.5
    property int pointRadius: 3

    // 横轴自定义标签
    property var xTickLabels: []

    // ===================== 新增:标签相关全局配置 ======================
    property bool showLabel: true               // 全局控制是否显示标签(核心新增)
    property var defaultLabelOffset: [0, -15]   // 标签在拐点正上方的偏移(x=0水平居中,y=-15向上偏移)
    property real labelOffsetStep: 8            // 标签错开步长(防遮挡)
    property bool enableLabelStagger: true      // 是否开启标签自动错开

    // ===================== 内部属性 ======================
    property color axisColor: "#333333"
    property color gridColor: "#e0e0e0"
    property real actualLeftMargin: baseMargin
    property real actualRightMargin: 20
    property real actualTopMargin: 20
    property real actualBottomMargin: baseMargin

    // ===================== 外部接口(扩展标签配置)=====================
    function setAxisConfig(config) {
        // 原有坐标轴配置赋值
        lineChart.xMin = config.xMin !== undefined ? config.xMin : lineChart.xMin;
        lineChart.xMax = config.xMax !== undefined ? config.xMax : lineChart.xMax;
        lineChart.yMin = config.yMin !== undefined ? config.yMin : lineChart.yMin;
        lineChart.yMax = config.yMax !== undefined ? config.yMax : lineChart.yMax;
        lineChart.xLabel = config.xLabel || lineChart.xLabel;
        lineChart.xUnit = config.xUnit || lineChart.xUnit;
        lineChart.yLabel = config.yLabel || lineChart.yLabel;
        lineChart.yUnit = config.yUnit || lineChart.yUnit;
        lineChart.xTicks = config.xTicks !== undefined ? config.xTicks : lineChart.xTicks;
        lineChart.yTicks = config.yTicks !== undefined ? config.yTicks : lineChart.yTicks;
        lineChart.xTickLabels = config.xTickLabels || lineChart.xTickLabels;

        // 新增:标签全局配置赋值
        lineChart.showLabel = config.showLabel !== undefined ? config.showLabel : lineChart.showLabel;
        lineChart.defaultLabelOffset = config.defaultLabelOffset || lineChart.defaultLabelOffset;
        lineChart.enableLabelStagger = config.enableLabelStagger !== undefined ? config.enableLabelStagger : lineChart.enableLabelStagger;
        lineChart.labelOffsetStep = config.labelOffsetStep || lineChart.labelOffsetStep;

        updateChart();
    }

    function setLineConfigs(configs) {
        lineChart.lineConfigs = configs || [];
        updateChart();
    }

    // 新增:全局控制是否显示标签(核心接口)
    function setShowLabel(show) {
        lineChart.showLabel = show;
        updateChart();
    }

    // 新增:单独控制指定折线是否显示标签
    function setLineShowLabel(lineIndex, show) {
        if (lineChart.lineConfigs[lineIndex]) {
            lineChart.lineConfigs[lineIndex].showLabel = show;
            updateChart();
        }
    }

    // 保留:单独设置标签偏移(如需微调正上方位置)
    function setLineLabelOffset(lineIndex, offset) {
        if (lineChart.lineConfigs[lineIndex]) {
            lineChart.lineConfigs[lineIndex].labelOffset = offset;
            updateChart();
        }
    }

    function updateChart() {
        calculateDynamicMargin();
        chartCanvas.requestPaint();
    }

    // ===================== 内部工具函数(扩展标签相关)=====================
    function calculateDynamicMargin() {
        if (!dynamicMargin) {
            actualLeftMargin = baseMargin;
            actualBottomMargin = baseMargin;
            return;
        }
        let ctx = chartCanvas.getContext("2d");
        ctx.font = textSize + "px Arial";
        let maxYTickTextWidth = ctx.measureText(yMax.toString()).width;
        let yLabelWithUnit = yLabel + (yUnit ? "(" + yUnit + ")" : "");
        let yLabelWidth = ctx.measureText(yLabelWithUnit).width;
        actualLeftMargin = baseMargin + maxYTickTextWidth + yLabelWidth/2 + 10;

        let maxXTickTextHeight = textSize + 5;
        let xLabelWithUnit = xLabel + (xUnit ? "(" + xUnit + ")" : "");
        let xLabelHeight = textSize + 5;
        actualBottomMargin = baseMargin + maxXTickTextHeight + xLabelHeight + 5;

        let maxMargin = Math.min(width/4, height/4);
        actualLeftMargin = Math.min(actualLeftMargin, maxMargin);
        actualBottomMargin = Math.min(actualBottomMargin, maxMargin);
    }

    function dataToCanvasX(x) {
        let xRange = xMax - xMin;
        let canvasWidth = chartCanvas.width - actualLeftMargin - actualRightMargin;
        return actualLeftMargin + (x - xMin) * (canvasWidth / xRange);
    }

    function dataToCanvasY(y) {
        let yRange = yMax - yMin;
        let canvasHeight = chartCanvas.height - actualTopMargin - actualBottomMargin;
        return chartCanvas.height - actualBottomMargin - (y - yMin) * (canvasHeight / yRange);
    }

    function hexToRgba(hex, alpha) {
        hex = hex.replace(/^#/, '');
        if (hex.length === 3) {
            hex = hex.split('').map(c => c + c).join('');
        }
        let r = parseInt(hex.substring(0, 2), 16);
        let g = parseInt(hex.substring(2, 4), 16);
        let b = parseInt(hex.substring(4, 6), 16);
        let op = Math.max(0, Math.min(1, alpha || 1));
        return `rgba(${r}, ${g}, ${b}, ${op})`;
    }

    function adjustColorBrightness(hex, brightness) {
        hex = hex.replace(/^#/, '');
        let r = parseInt(hex.substring(0, 2), 16);
        let g = parseInt(hex.substring(2, 4), 16);
        let b = parseInt(hex.substring(4, 6), 16);
        r = Math.round(Math.min(255, Math.max(0, r + r * brightness)));
        g = Math.round(Math.min(255, Math.max(0, g + g * brightness)));
        b = Math.round(Math.min(255, Math.max(0, b + b * brightness)));
        let toHex = (n) => n.toString(16).padStart(2, '0');
        return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
    }

    // 核心修改:强制标签在拐点正上方,移除位置配置,简化计算
    function calculateLabelPosition(params) {
        let {ctx, x, y, value, lineIdx, pointIdx, lineCfg} = params;
        let labelText = value.toFixed(1);
        let textWidth = ctx.measureText(labelText).width + 6;
        let textHeight = textSize + 4;

        // 固定标签位置:拐点正上方,水平居中
        let customOffset = lineCfg.labelOffset || lineChart.defaultLabelOffset;
        let staggerStep = lineChart.labelOffsetStep;

        let labelX = x;                  // 水平居中于拐点
        let labelY = y + customOffset[1];// 垂直向上偏移(默认-15)
        let textAlign = "center";        // 文字水平居中
        let textBaseline = "bottom";     // 文字底部对齐

        // 自动错开防遮挡(仅水平微调,保持垂直在正上方)
        if (lineChart.enableLabelStagger) {
            let staggerX = (lineIdx % 3) * staggerStep - staggerStep;
            labelX += staggerX; // 仅水平错开,不改变垂直位置
        }

        // 边界检测:确保标签不超出画布
        let canvasWidth = chartCanvas.width;
        let canvasHeight = chartCanvas.height;
        if (labelX - textWidth/2 < actualLeftMargin) {
            labelX = actualLeftMargin + textWidth/2 + 5;
        }
        if (labelX + textWidth/2 > canvasWidth - actualRightMargin) {
            labelX = canvasWidth - actualRightMargin - textWidth/2 - 5;
        }
        if (labelY - textHeight < actualTopMargin) {
            labelY = actualTopMargin + textHeight + 5;
            textBaseline = "top";
        }

        return {
            x: labelX,
            y: labelY,
            textWidth: textWidth,
            textHeight: textHeight,
            textAlign: textAlign,
            textBaseline: textBaseline,
            labelText: labelText
        };
    }

    // 绘制自定义数据点
    function drawPoint(ctx, x, y, type, color, size) {
        ctx.fillStyle = color;
        ctx.strokeStyle = color;
        ctx.lineWidth = 1.5;
        switch(type) {
            case "diamond":
                ctx.beginPath();
                ctx.moveTo(x, y - size);
                ctx.lineTo(x + size, y);
                ctx.lineTo(x, y + size);
                ctx.lineTo(x - size, y);
                ctx.closePath();
                ctx.fill();
                break;
            case "square":
                ctx.fillRect(x - size, y - size, size*2, size*2);
                break;
            case "triangle":
                ctx.beginPath();
                ctx.moveTo(x, y - size);
                ctx.lineTo(x + size, y + size);
                ctx.lineTo(x - size, y + size);
                ctx.closePath();
                ctx.fill();
                break;
            case "cross":
                ctx.beginPath();
                ctx.moveTo(x - size, y - size);
                ctx.lineTo(x + size, y + size);
                ctx.moveTo(x + size, y - size);
                ctx.lineTo(x - size, y + size);
                ctx.stroke();
                break;
            default:
                ctx.beginPath();
                ctx.arc(x, y, size, 0, Math.PI*2);
                ctx.fill();
        }
    }

    // 核心修改:移除标签背景,仅绘制文字,固定在正上方
    function drawPointLabel(ctx, x, y, value, color, lineIdx, pointIdx, lineCfg) {
        let labelInfo = calculateLabelPosition({
            ctx: ctx,
            x: x,
            y: y,
            value: value,
            lineIdx: lineIdx,
            pointIdx: pointIdx,
            lineCfg: lineCfg
        });

        // 移除所有背景绘制逻辑,仅保留文字绘制
        ctx.fillStyle = "#ffffff"; // 固定白色文字
        ctx.font = (textSize - 1) + "px Arial";
        ctx.textAlign = labelInfo.textAlign;
        ctx.textBaseline = labelInfo.textBaseline;
        ctx.fillText(labelInfo.labelText, labelInfo.x, labelInfo.y);
    }

    // ===================== Canvas 绘制(整合标签+保留网格样式)=====================
    Canvas {
        id: chartCanvas
        anchors.fill: parent
        antialiasing: true
        renderTarget: Canvas.FramebufferObject

        onPaint: {
            let ctx = getContext("2d")
            ctx.reset()
            let w = width
            let h = height

            ctx.fillStyle = "#1e1e1e"
            ctx.fillRect(0, 0, w, h)

            if (xMax <= xMin || yMax <= yMin) return;

            // 网格横线:最下面的横线(i=0)设为白色,其他保持原颜色
            let yStep = (yMax - yMin) / yTicks
            for (let i = 0; i <= yTicks; i++) {
                let y = yMin + i * yStep
                let cy = dataToCanvasY(y)
                ctx.beginPath()
                if (i === 0) {
                    ctx.strokeStyle = "#ffffff" // 最下面的横线设为白色
                } else {
                    ctx.strokeStyle = gridColor
                }
                ctx.moveTo(actualLeftMargin, cy)
                ctx.lineTo(w - actualRightMargin, cy)
                ctx.stroke()
            }

            // 网格竖线:最左边的竖线(i=0)设为白色,其他设为透明
            let xStep = (xMax - xMin) / xTicks
            for (let i = 0; i <= xTicks; i++) {
                let x = xMin + i * xStep
                let cx = dataToCanvasX(x)
                ctx.beginPath()
                if (i === 0) {
                    ctx.strokeStyle = "#ffffff" // 最左边的竖线设为白色
                } else {
                    ctx.strokeStyle = "rgba(0, 0, 0, 0)" // 其他竖线透明
                }
                ctx.moveTo(cx, actualTopMargin)
                ctx.lineTo(cx, h - actualBottomMargin)
                ctx.stroke()
            }

            // 坐标轴
            ctx.strokeStyle = axisColor
            ctx.lineWidth = 1.5
            ctx.beginPath()
            ctx.moveTo(actualLeftMargin, h - actualBottomMargin)
            ctx.lineTo(w - actualRightMargin, h - actualBottomMargin)
            ctx.stroke()
            ctx.beginPath()
            ctx.moveTo(actualLeftMargin, actualTopMargin)
            ctx.lineTo(actualLeftMargin, h - actualBottomMargin)
            ctx.stroke()

            // X 刻度标签(支持自定义)
            ctx.fillStyle = "#fff"
            ctx.font = textSize + "px Arial"
            ctx.textAlign = "center"
            ctx.textBaseline = "top"
            for (let i = 0; i <= xTicks; i++) {
                let x = xMin + i * xStep
                let cx = dataToCanvasX(x)
                let txt = xTickLabels[i] ?? x.toFixed(0)
                ctx.fillText(txt, cx, h - actualBottomMargin + 8)
            }

            // Y 刻度
            ctx.textAlign = "right"
            ctx.textBaseline = "middle"
            for (let i = 0; i <= yTicks; i++) {
                let y = yMin + i * yStep
                let cy = dataToCanvasY(y)
                ctx.fillText(y.toFixed(0), actualLeftMargin - 8, cy)
            }

            // 折线 + 数据点 + 标签(核心扩展)
            if (lineConfigs.length > 0) {
                for (let lineIdx = lineConfigs.length - 1; lineIdx >= 0; lineIdx--) {
                    let lineCfg = lineConfigs[lineIdx]
                    let data = lineCfg.dataPoints || []
                    if (data.length < 2) continue;

                    let fs = lineCfg.fillStyle || defaultFillStyle
                    let gStart = fs.gradientStart || defaultFillStyle.gradientStart
                    let gEnd = fs.gradientEnd || defaultFillStyle.gradientEnd
                    let dir = fs.gradientDirection || defaultFillStyle.gradientDirection

                    let brightEnd = adjustColorBrightness(gEnd, topColorBrightnessRatio)
                    let bottomY = dataToCanvasY(yMin)
                    let topY = dataToCanvasY(yMax)
                    let grad = ctx.createLinearGradient(0, bottomY, 0, topY)
                    grad.addColorStop(0, hexToRgba(gStart, fillOpacityBase * bottomOpacityRatio))
                    grad.addColorStop(1, hexToRgba(brightEnd, fillOpacityBase * topOpacityRatio))

                    // 填充区域
                    ctx.beginPath()
                    let first = data[0]
                    let fx = dataToCanvasX(first.x)
                    let fy = dataToCanvasY(first.y)
                    ctx.moveTo(fx, fy)
                    for (let p of data.slice(1)) {
                        ctx.lineTo(dataToCanvasX(p.x), dataToCanvasY(p.y))
                    }
                    ctx.lineTo(dataToCanvasX(data[data.length-1].x), bottomY)
                    ctx.lineTo(fx, bottomY)
                    ctx.closePath()
                    ctx.fillStyle = grad
                    ctx.fill()

                    // 折线描边
                    ctx.beginPath()
                    ctx.moveTo(fx, fy)
                    for (let p of data.slice(1)) {
                        ctx.lineTo(dataToCanvasX(p.x), dataToCanvasY(p.y))
                    }
                    ctx.strokeStyle = lineCfg.lineColor || lineStrokeColor
                    ctx.lineWidth = lineStrokeWidth
                    ctx.stroke()

                    // 数据点 + 数值标签(核心修改)
                    let lineColor = lineCfg.lineColor || lineStrokeColor;
                    for (let pointIdx = 0; pointIdx < data.length; pointIdx++) {
                        let p = data[pointIdx];
                        let cx = dataToCanvasX(p.x);
                        let cy = dataToCanvasY(p.y);

                        // 绘制自定义数据点
                        drawPoint(ctx, cx, cy, lineCfg.pointType || "circle", lineColor, pointRadius);

                        // 绘制标签:全局开关 + 单条折线开关双重控制
                        if (lineChart.showLabel && lineCfg.showLabel !== false) {
                            drawPointLabel(ctx, cx, cy, p.y, lineColor, lineIdx, pointIdx, lineCfg);
                        }
                    }

                    // 折线名称标签
                    if (lineCfg.lineName) {
                        ctx.font = textSize + "px Arial"
                        ctx.textAlign = "left"
                        ctx.textBaseline = "top"
                        let nameX = actualLeftMargin + 10 + (lineIdx % 3) * 120
                        let nameY = actualTopMargin + 10 + Math.floor(lineIdx / 3) * 20
                        nameX = Math.min(nameX, w - actualRightMargin - 10)
                        nameY = Math.min(nameY, h - actualBottomMargin - 20)
                        let lineName = lineCfg.lineName.length > 10 ? lineCfg.lineName.substring(0, 10) + "..." : lineCfg.lineName
                        ctx.fillStyle = lineColor
                        ctx.fillRect(nameX, nameY + 2, 8, 8)
                        ctx.fillStyle = "#ffffff"
                        ctx.fillText(lineName, nameX + 12, nameY)
                    }
                }
            }
        }
    }

    Component.onCompleted: updateChart()
}
main.qml
javascript 复制代码
// ===================== 主窗口(测试标签控制接口)=====================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.15

Window {
    width: 1200
    height: 600
    title: "科技风折线图(标签固定正上方)"
    visible: true
    color: "#1e1e1e"

    ColumnLayout {
        anchors.fill: parent
        spacing: 20

        TSAreaLine {
            id: chart
            Layout.fillWidth: true
            Layout.fillHeight: true
            textSize: 14
            dynamicMargin: false
            baseMargin: 40
            fillOpacityBase: 0.9
            topOpacityRatio: 1.0
            bottomOpacityRatio: 0.1
            topColorBrightnessRatio: 0.1
            // 全局标签配置:默认显示,正上方偏移
            showLabel: true
            defaultLabelOffset: [0, -15] // x=0水平居中,y=-15向上偏移
            enableLabelStagger: true
        }

        RowLayout {
            Layout.alignment: Qt.AlignHCenter
            spacing: 20

            Button {
                text: "显示数据"
                onClicked: {
                    chart.setAxisConfig({
                        xMin: 0,
                        xMax: 12,
                        yMin: 0,
                        yMax: 160,
                        xTicks: 12,
                        yTicks: 8,
                        xTickLabels: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""],
                        // 全局标签配置
                        showLabel: true,
                        defaultLabelOffset: [0, -20], // 自定义正上方偏移量
                        enableLabelStagger: true
                    })
                    chart.setLineConfigs([
                        {
                            lineName: "数据1",
                            pointType: "circle",
                            showLabel: true, // 单独控制该折线显示标签
                            dataPoints: [
                                {x:0,y:20},{x:1,y:40},{x:2,y:35},{x:3,y:60},{x:4,y:70},{x:5,y:90},
                                {x:6,y:80},{x:7,y:100},{x:8,y:110},{x:9,y:95},{x:10,y:80},{x:11,y:60},{x:12,y:45}
                            ],
                            fillStyle: { gradientStart: "#80deea", gradientEnd: "#004d40" },
                            lineColor: "#80deea"
                        },
                        {
                            lineName: "数据2",
                            pointType: "diamond",
                            showLabel: true, // 单独控制该折线显示标签
                            dataPoints: [
                                {x:0,y:10},{x:1,y:25},{x:2,y:55},{x:3,y:45},{x:4,y:65},{x:5,y:85},
                                {x:6,y:120},{x:7,y:140},{x:8,y:125},{x:9,y:100},{x:10,y:75},{x:11,y:50},{x:12,y:30}
                            ],
                            fillStyle: { gradientStart: "#4dd0e1", gradientEnd: "#006064" },
                            lineColor: "#4dd0e1"
                        }
                    ])
                }
            }

            Button {
                text: "显示所有标签"
                onClicked: {
                    chart.setShowLabel(true); // 全局显示所有标签
                }
            }

            Button {
                text: "隐藏所有标签"
                onClicked: {
                    chart.setShowLabel(false); // 全局隐藏所有标签
                }
            }

            Button {
                text: "仅隐藏数据2标签"
                onClicked: {
                    chart.setLineShowLabel(1, false); // 单独隐藏第二条折线的标签
                }
            }

            Button {
                text: "恢复数据2标签"
                onClicked: {
                    chart.setLineShowLabel(1, true); // 单独显示第二条折线的标签
                }
            }
        }
    }
}

demo02

TSAreaLine.qml
javascript 复制代码
import QtQuick 2.12

Item {
    id: lineChart
    // ===================== 外部可配置属性(核心,新增/优化)=====================
    // 坐标轴范围(外部设置)
    property real xMin: 0          // X轴最小值
    property real xMax: 10         // X轴最大值
    property real yMin: 0          // Y轴最小值
    property real yMax: 100        // Y轴最大值
    // 坐标轴标签+单位(外部设置,解决单位配置需求)
    property string xLabel: "时间"  // X轴标签
    property string xUnit: "秒"    // X轴单位(新增:外部可设置横轴单位)
    property string yLabel: "数值"  // Y轴标签
    property string yUnit: ""      // Y轴单位(可选,外部可设置)
    // 刻度数量(控制网格密度)
    property int xTicks: 10        // X轴刻度数
    property int yTicks: 10        // Y轴刻度数
    // 文字配置(新增:解决文字显示不全)
    property int textSize: 12      // 基础文字大小,外部可调整
    property bool dynamicMargin: true // 动态计算边距(默认开启,保证文字全显示)
    property real baseMargin: 40   // 基础边距,外部可调整
    // 折线配置数组(外部设置,支持任意条数折线,默认空数组)
    // 格式:[{lineColor: "#4CAF50", lineWidth: 2, dataPoints: [{x:0,y:0}, ...], lineName: "折线1"}, ...]
    property var lineConfigs: []
    // 科技风渐变配置(新增)
    property color gradientStart: "#00e5ff"  // 渐变起始色(青色)
    property color gradientEnd: "#7c4dff"    // 渐变结束色(紫色)
    property color lineStrokeColor: "#ffffff" // 折线描边色(白色)
    property real lineStrokeWidth: 1.5       // 折线描边宽度

    // ===================== 内部属性(优化文字显示)=====================
    property color axisColor: "#333333"  // 坐标轴颜色
    property color gridColor: "#e0e0e0"  // 网格线颜色
    property int pointRadius: 3     // 数据点半径
    // 动态计算的实际边距(根据文字自动调整)
    property real actualLeftMargin: baseMargin
    property real actualRightMargin: 20
    property real actualTopMargin: 20
    property real actualBottomMargin: baseMargin


    // ===================== 外部友好接口(新增:简化配置)=====================
    /**
     * 外部一键配置坐标轴(范围+标签+单位)
     * @param {Object} config - 配置对象
     *        config.xMin: X轴最小值
     *        config.xMax: X轴最大值
     *        config.yMin: Y轴最小值
     *        config.yMax: Y轴最大值
     *        config.xLabel: X轴标签(如"时间")
     *        config.xUnit: X轴单位(如"秒")
     *        config.yLabel: Y轴标签(如"温度")
     *        config.yUnit: Y轴单位(如"℃")
     *        config.xTicks: X轴刻度数
     *        config.yTicks: Y轴刻度数
     */
    function setAxisConfig(config) {
        // 赋值坐标轴范围
        lineChart.xMin = config.xMin !== undefined ? config.xMin : lineChart.xMin;
        lineChart.xMax = config.xMax !== undefined ? config.xMax : lineChart.xMax;
        lineChart.yMin = config.yMin !== undefined ? config.yMin : lineChart.yMin;
        lineChart.yMax = config.yMax !== undefined ? config.yMax : lineChart.yMax;
        // 赋值标签+单位
        lineChart.xLabel = config.xLabel || lineChart.xLabel;
        lineChart.xUnit = config.xUnit || lineChart.xUnit;
        lineChart.yLabel = config.yLabel || lineChart.yLabel;
        lineChart.yUnit = config.yUnit || lineChart.yUnit;
        // 赋值刻度数
        lineChart.xTicks = config.xTicks !== undefined ? config.xTicks : lineChart.xTicks;
        lineChart.yTicks = config.yTicks !== undefined ? config.yTicks : lineChart.yTicks;
        // 刷新图表
        updateChart();
    }


    /**
     * 外部设置折线配置
     * @param {Array} configs - 折线配置数组(格式同lineConfigs)
     */
    function setLineConfigs(configs) {
        lineChart.lineConfigs = configs || [];
        updateChart();
    }


    /**
     * 刷新折线图(外部修改配置后调用此接口即可更新显示)
     */
    function updateChart() {
        // 动态计算边距(即使无折线,也要计算边距保证坐标轴文字显示)
        calculateDynamicMargin();
        // 触发重绘
        chartCanvas.requestPaint()
    }


    // ===================== 内部工具函数(核心:动态计算边距,解决文字截断)=====================
    /**
     * 动态计算边距:根据文字长度调整左/底部边距,保证文字不截断
     */
    function calculateDynamicMargin() {
        if (!dynamicMargin) {
            // 关闭动态边距:使用基础边距
            actualLeftMargin = baseMargin;
            actualBottomMargin = baseMargin;
            return;
        }


        // 1. 计算左侧边距(适配Y轴刻度文字、Y轴标签+单位)
        let ctx = chartCanvas.getContext("2d");
        ctx.font = textSize + "px Arial";
        // 最大Y轴刻度文字宽度(比如"150"比"50"宽)
        let maxYTickTextWidth = ctx.measureText(yMax.toString()).width;
        // Y轴标签+单位宽度(旋转后占左侧空间)
        let yLabelWithUnit = yLabel + (yUnit ? "(" + yUnit + ")" : "");
        let yLabelWidth = ctx.measureText(yLabelWithUnit).width;
        // 左侧边距 = 基础边距 + 最大刻度文字宽度 + 标签宽度/2(旋转后) + 预留空间
        actualLeftMargin = baseMargin + maxYTickTextWidth + yLabelWidth/2 + 10;


        // 2. 计算底部边距(适配X轴刻度文字、X轴标签+单位)
        let maxXTickTextHeight = textSize + 5; // 刻度文字高度+预留
        let xLabelWithUnit = xLabel + (xUnit ? "(" + xUnit + ")" : "");
        let xLabelHeight = textSize + 5; // 标签高度+预留
        // 底部边距 = 基础边距 + 刻度文字高度 + 标签高度 + 预留空间
        actualBottomMargin = baseMargin + maxXTickTextHeight + xLabelHeight + 5;


        // 3. 限制边距不超过画布1/4(避免边距过大)
        let maxMargin = Math.min(width/4, height/4);
        actualLeftMargin = Math.min(actualLeftMargin, maxMargin);
        actualBottomMargin = Math.min(actualBottomMargin, maxMargin);
    }


    /**
     * 将数据X值转换为Canvas像素X坐标(适配动态边距)
     */
    function dataToCanvasX(x) {
        let xRange = xMax - xMin;
        let canvasWidth = chartCanvas.width - actualLeftMargin - actualRightMargin;
        return actualLeftMargin + (x - xMin) * (canvasWidth / xRange);
    }


    /**
     * 将数据Y值转换为Canvas像素Y坐标(适配动态边距,Canvas Y轴向下,需反转)
     */
    function dataToCanvasY(y) {
        let yRange = yMax - yMin;
        let canvasHeight = chartCanvas.height - actualTopMargin - actualBottomMargin;
        return chartCanvas.height - actualBottomMargin - (y - yMin) * (canvasHeight / yRange);
    }


    // ===================== 折线图绘制Canvas(核心修改:分离基础图层和折线图层)=====================
    Canvas {
        id: chartCanvas
        anchors.fill: parent
        antialiasing: true  // 抗锯齿,保证线条平滑
        renderTarget: Canvas.FramebufferObject


        onPaint: {
            let ctx = getContext("2d")
            ctx.reset()
            let w = width
            let h = height


            // 1. 清空画布(科技风深色背景)
            ctx.fillStyle = "#1e1e1e"
            ctx.fillRect(0, 0, w, h)


            // 边界保护:仅判断坐标轴范围是否有效(移除lineConfigs.length === 0的判断)
            if (xMax <= xMin || yMax <= yMin) {
                return;
            }

            // 6. 绘制科技风渐变折线和填充区域(仅当lineConfigs有数据时执行)
            if (lineConfigs.length > 0) { // 新增判断:仅当有折线配置时才绘制
                for (let lineIdx = 0; lineIdx < lineConfigs.length; lineIdx++) {
                    let lineCfg = lineConfigs[lineIdx]
                    let dataPoints = lineCfg.dataPoints || []
                    if (dataPoints.length < 2) continue  // 至少2个点才绘制折线

                    // 创建线性渐变(从青色到紫色)
                    let gradient = ctx.createLinearGradient(0, 0, 0, h);
                    gradient.addColorStop(0, gradientStart);
                    gradient.addColorStop(1, gradientEnd);

                    // 绘制渐变填充区域
                    ctx.beginPath();
                    // 移动到第一个数据点
                    let firstPoint = dataPoints[0];
                    let firstCanvasX = dataToCanvasX(firstPoint.x);
                    let firstCanvasY = dataToCanvasY(firstPoint.y);
                    ctx.moveTo(firstCanvasX, firstCanvasY);

                    // 绘制折线
                    for (let pointIdx = 1; pointIdx < dataPoints.length; pointIdx++) {
                        let point = dataPoints[pointIdx];
                        let canvasX = dataToCanvasX(point.x);
                        let canvasY = dataToCanvasY(point.y);
                        ctx.lineTo(canvasX, canvasY);
                    }

                    // 闭合路径到X轴底部,形成填充区域
                    let lastPoint = dataPoints[dataPoints.length - 1];
                    let lastCanvasX = dataToCanvasX(lastPoint.x);
                    let xAxisY = dataToCanvasY(yMin); // X轴的Y坐标
                    ctx.lineTo(lastCanvasX, xAxisY);
                    ctx.lineTo(firstCanvasX, xAxisY);
                    ctx.closePath();

                    // 填充渐变
                    ctx.fillStyle = gradient;
                    ctx.fill();

                    // 绘制白色折线描边
                    ctx.beginPath();
                    ctx.moveTo(firstCanvasX, firstCanvasY);
                    for (let pointIdx = 1; pointIdx < dataPoints.length; pointIdx++) {
                        let point = dataPoints[pointIdx];
                        let canvasX = dataToCanvasX(point.x);
                        let canvasY = dataToCanvasY(point.y);
                        ctx.lineTo(canvasX, canvasY);
                    }
                    ctx.strokeStyle = lineStrokeColor;
                    ctx.lineWidth = lineStrokeWidth;
                    ctx.stroke();

                    // 绘制数据点(白色小圆点)
                    ctx.fillStyle = lineStrokeColor;
                    for (let pointIdx = 0; pointIdx < dataPoints.length; pointIdx++) {
                        let point = dataPoints[pointIdx];
                        let canvasX = dataToCanvasX(point.x);
                        let canvasY = dataToCanvasY(point.y);
                        ctx.beginPath();
                        ctx.arc(canvasX, canvasY, pointRadius, 0, Math.PI*2);
                        ctx.fill();
                    }
                }
            }
        }
    }


    // 组件初始化时触发一次绘制(即使无折线,也绘制基础图层)
    Component.onCompleted: {
        updateChart()
    }
}
main.qml
javascript 复制代码
//main.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.15


Window {
    id: mainWindow
    width: 800
    height: 600
    title: "科技风渐变折线图示例"
    visible: true
    color: "#1e1e1e" // 窗口背景色与图表一致


    ColumnLayout {
        anchors.fill: parent
        spacing: 20
        //padding: 10


        // 1. 折线图组件(核心):实例化时默认显示坐标轴+背景,无折线
        TSAreaLine {
            id: myLineChart
            Layout.fillWidth: true
            Layout.fillHeight: true
            // 初始配置(可被外部随时修改)
            textSize: 14  // 调大文字,测试是否显示全
            dynamicMargin: false // 关闭动态边距,隐藏坐标轴
            baseMargin: 0 // 基础边距设为0,隐藏坐标轴
            // lineConfigs默认空数组,无需额外配置
            gradientStart: "#00e5ff"
            gradientEnd: "#7c4dff"
            lineStrokeColor: "#ffffff"
            lineStrokeWidth: 1.5
        }


        // 2. 外部控制按钮(演示新增接口调用)
        RowLayout {
            Layout.alignment: Qt.AlignHCenter
            spacing: 10


            // 按钮1:显示科技风渐变折线图
            Button {
                text: "显示科技风渐变折线"
                onClicked: {
                    // 调用新增接口:一键配置坐标轴(数值范围+横轴单位)
                    myLineChart.setAxisConfig({
                        xMin: 0,
                        xMax: 10,
                        yMin: 0,
                        yMax: 100,
                        xLabel: "",
                        xUnit: "",
                        yLabel: "",
                        yUnit: "",
                        xTicks: 0,
                        yTicks: 0
                    });
                    // 调用接口设置折线配置
                    myLineChart.setLineConfigs([
                        {
                            dataPoints: [
                                {x:0,y:10}, {x:1,y:12}, {x:2,y:8}, {x:3,y:15},
                                {x:4,y:30}, {x:5,y:25}, {x:6,y:28}, {x:7,y:40},
                                {x:8,y:60}, {x:9,y:80}, {x:10,y:95}
                            ]
                        }
                    ]);
                }
            }


            // 按钮2:清空折线图(仅隐藏折线,保留坐标轴+背景)
            Button {
                text: "清空折线"
                onClicked: {
                    myLineChart.setLineConfigs([]);
                }
            }
        }
    }
}
相关推荐
weixin_1103 天前
qml滑动色块
qt·qml
SilentSlot8 天前
【QT-QML】8. 输入元素
qt·qml
SilentSlot10 天前
【QT-QML】6.定位元素
qt·qml
SilentSlot11 天前
【QT-QML】5. 简单变换
qt·qml
SilentSlot14 天前
【QT-QML】4. 组件
qt·qml
SilentSlot16 天前
【QT-QML】1. 快速入门
开发语言·qt·qml
SilentSlot16 天前
【QT-QML】2. QML语法
开发语言·qt·qml
Respect@1 个月前
qml之TableViewColumn
开发语言·qml
水煎包V:YEDIYYDS8882 个月前
QT QML 实现的旋钮按钮,类似收音机音量旋钮,可指示方向和角度
qt·qml·旋钮组件