Search code examples
javascriptcanvasqml

How to reposition the canvas item through Y value whose height varies dynamically


I have a Qml Canvas Item where in, the height of canvas item keeps varying based on the input dynamically. Below is my code

Item {
id: barGraph

property int highestRange: 200
property int rangeVal: (RangeModel.rangeValue === "---") ? highestRange : RangeModel.rangeValue
property int totalHeight: 450
property int canvasHeight: (rangeVal * totalHeight) / highestRange

height: 700
width: 500
x: 120
y: 145

Canvas {
    id: mycanvas
    height: canvasHeight
    width: 16

    onPaint: {
        var context = getContext("2d");
        var startX = mycanvas.x;
        var startY = mycanvas.y;
        context.lineWidth = 0.5;
        context.strokeStyle = "white";

        context.beginPath();
        context.moveTo(startX, startY);
        context.lineTo(startX,(mycanvas.height - 10));
        context.lineTo(mycanvas.width, mycanvas.height);
        context.lineTo(mycanvas.width, startY + 10);
        context.lineTo(mycanvas.width / 2, startY);
        context.closePath(); // base drawn automatically
        context.fill();
        context.stroke();
    }
}}

The output of this code look like this :

canvas with full height

The problem with this code is, whenever the height of the canvas changes dynamically it actually resizes from the below position as shown in the image

resized canvas image.

I actually need this to be resized from the Y top position and keeping the below position unmovable which I am unable to achieve through this code.

Any support is much appreciated here.


Solution

  • I am guessing your requirement is to use the Canvas object to build an isometric bar chart application?

    I have refactored a BarGraph.qml and replaced Canvas with Shape, ShapePath, and PathLine.

    I removed the unnecessary Item parent and made Shape the parent and made everything size accordingly.

    There is a mistake in your code. You set startX and startY to be relative to canvas.x and canvas.y. This is problematic, since if canvas.x and canvas.y is non-zero your usage of startX and startY later will only move the components further.

    Instead, all coordinates within your shape must tree the top-left corner of your canvas as (0, 0) regardless of where it is.

    To make things position on the bottom, it is based on:

    y: parent.height - height
    

    parent.height refers to the height of the page and height refers to the height of the BarGraph. By assigning this value to y it will make the BarGraph appear anchored to the bottom instead of the top of the page. Additional math is added later to create the isometric look.

    To mock some data up, I use an array of numbers which I feed into the Repeater. This will allow me to create multiple instances of BarGraph and use the numbers via modelData.

    As added bonus, I added a fill color to the BarGraph and made the fill color gradually change from "blue" to "white".

    import QtQuick
    import QtQuick.Controls
    Page {
        background: Rectangle { color: "black" }
    
        Repeater {
            model: [ 100, 150, 200, 250, 300, 100, 150, 200, 250, 300 ]
            BarGraph {
                x: index * 30 + 100
                y: parent.height - height - 200 + index * 15
                fillColor: Qt.lighter(Qt.color("blue"), 1.0 + (index % 10) / 10.0)
                height: modelData
            }
        }
    
        BarXYAxis {
        }
    }
    
    // BarGraph.qml
    import QtQuick
    import QtQuick.Shapes
    Shape {
        id: shape
        width: 20
        height: 250
        property alias strokeColor: shapePath.strokeColor
        property alias strokeWidth: shapePath.strokeWidth
        property alias fillColor: shapePath.fillColor
        ShapePath {
            id: shapePath
            startX: 0; startY: 0
            strokeColor: "white"
            strokeWidth: 1.0
            fillColor: "blue"
            PathLine { x: 0; y: shape.height - 10 }
            PathLine { x: shape.width; y: shape.height }
            PathLine { x: shape.width; y: 10 }
            PathLine { x: shape.width / 2; y: 0 }
            PathLine { x: 0; y: 0 }
        }
    }
    
    // BarXYAxis.qml
    import QtQuick
    import QtQuick.Shapes
    Item {
        id: axis
    
        anchors.fill: parent
    
        Shape {
            anchors.fill: parent
            visible: parent.height - 200 > 30
            ShapePath {
                startX: 80; startY: 30
                strokeColor: "white"
                strokeWidth: 1.0
                fillColor: "transparent"
                PathLine { x: 80; y: parent.height - 200 }
            }
        }
    
        Shape {
            anchors.fill: parent
            ShapePath {
                startX: 80; startY: parent.height - 200
                strokeColor: "white"
                strokeWidth: 1.0
                fillColor: "transparent"
                PathLine { x: 410; y: parent.height - 35 }
            }
        }
    
        Repeater {
            model: Math.floor((axis.height - 230) / 20)
    
            Shape {
                x: 70
                y: axis.height - 222 - index * 20
                ShapePath {
                    startX: 0; startY: 0
                    strokeColor: "white"
                    strokeWidth: 1.0
                    fillColor: "transparent"
                    PathLine { x: 4; y: 2 }
                }
             }
        }
    }
    

    You can Try it Online!