SOURCE

const root = document.createElement('canvas')
const context = root.getContext('2d')

const width = 500
const height = 250
const labelY = 80
const transitionTime = 1000

root.width = width
root.height = height

const dataContainer = d3.select(
    document.createElement('custom')
)

function drawCustom(data, index) {
    const dataBinding = dataContainer
        .selectAll("custom.rect")
        .data(data, d => d.name)
    dataBinding
        .enter()
        .append("custom")
        .classed("rect", true)
        .attr('x', d => d.rank * 120)
        .attr('y', 100)
        .attr('size', d => {
            if (d.value) return 15
            return 0
        })
    dataBinding
        .transition()
        .ease(d3.easeLinear)
        .duration(transitionTime)
        .delay(index * transitionTime)
        .attr('size', d => d.value)
        .attr('x', d => d.rank * 120)
    dataBinding
        .exit()
        .transition()
        .duration(transitionTime)
        .delay(index * transitionTime)
        .attr('size', 0)
        .remove()

    const labels = dataContainer
        .selectAll("custom.text")
        .data(data, d => d.name)
    labels
        .transition()
        .ease(d3.easeLinear)
        .duration(transitionTime)
        .delay(index * transitionTime)
        .attr("size", d => {
            if (d.value) return 15
            return 0
        })
        .attr("x", d => d.rank * 120)
    labels
        .enter()
        .append("custom")
        .classed('text', true)
        .attr("x", d => {
            d.rank * 120
        })
        .attr("y", labelY)
        .attr("name", d => d.name)
        .attr("size", d => {
            if (d.value) return 15
            return 0
        })
    labels
        .exit()
        .transition()
        .duration(transitionTime)
        .delay(index * transitionTime)
        .attr("size", 0)
        .remove()
}

function drawCanvas() {
    // context.fillStyle = "#eee"
    // context.rect(0, 0, width, height)
    // context.fill()
    context.clearRect(0, 0, width, height)

    const elements = dataContainer.selectAll("custom.rect")
    elements.each(function (d) {
        const node = d3.select(this)
        context.beginPath()
        context.fillStyle = "rgba(255, 170, 1,0.8)"
        context.rect(node.attr("x"), node.attr("y"), 40, node.attr("size"))
        context.fill()
        context.closePath()
    })

    const labelElements = dataContainer.selectAll("custom.text")
    labelElements.each(function (d) {
        const labelNode = d3.select(this)

        context.font = `${labelNode.attr("size")}px Arial`
        context.textAlign = "center"
        context.fillStyle = "rgba(3, 123, 144,0.9)"
        context.fillText(labelNode.attr("name"), labelNode.attr("x"), labelNode.attr("y"))
        context.fill()
    })
}

const dataArray = [
    [
        { "name": "Apple", "value": 12, "rank": 2 },
        { "name": "Ball", "value": 84, "rank": 1 },
        { "name": "Cat", "value": 15, "rank": 3 },
        { "name": "Dog", "value": 0, "rank": 4 },
    ],
    [
        { "name": "Apple", "value": 17, "rank": 3 },
        { "name": "Ball", "value": 95, "rank": 1 },
        { "name": "Cat", "value": 45, "rank": 2 },

    ],
    [
        { "name": "Ball", "value": 17, "rank": 2 },
        { "name": "Apple", "value": 83, "rank": 1 },
        { "name": "Cat", "value": 15, "rank": 3 },
    ],
    [
        { "name": "Apple", "value": 17, "rank": 2 },
        { "name": "Ball", "value": 83, "rank": 1 },
        { "name": "Cat", "value": 15, "rank": 3 },
    ],
    [
        { "name": "Apple", "value": 17, "rank": 3 },
        { "name": "Ball", "value": 95, "rank": 1 },
        { "name": "Cat", "value": 45, "rank": 2 },
        { "name": "Dog", "value": 0, "rank": 4 },
    ],
]
for (let index = 0; index < dataArray.length; index++) {
    drawCustom(dataArray[index], index)
}
d3.timer(drawCanvas)
setTimeout(() => {
    document.body.append(root)
})
console 命令行工具 X clear

                    
>
console