SOURCE

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

const width = 800
const height = 100
root.width = width
root.height = height

const tick_length = 20
const axis_rectangle = [5, 50 - tick_length, width - 100, 50]
const axis_container = {
    width: axis_rectangle[2] - axis_rectangle[0],
    height: axis_rectangle[3] - axis_rectangle[1]
}
const transitionTime = 500
const dataArray = [12, 15, 25, 38, 45, 51, 64, 87, 92, 112, 234, 345, 456, 567, 678, 789, 899, 999, 1023, 1123, 1234, 1345, 1456, 678, 567, 456, 345, 234, 999]

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

function drawCustom(data, index) {
    const allowedUnits = allowedAxisUnits(data)
    const allowedUnitsDict = []
    allowedUnits.forEach(d => {
        allowedUnitsDict.push({ name: d.toString(), value: d, highest_value: data })
    })

    const dataBinding = dataContainer
        .selectAll("custom.ticks")
        .data(allowedUnitsDict, d => d.name)
    dataBinding
        .enter()
        .append('custom')
        .classed('ticks', true)
        .attr('tickColor', 'rgba(252, 110, 34,0)')
        .attr('tickLabel', d => d.value)
        .attr('x', d => d.value)
        .attr('tickSize', 7)
        .attr('highest_val', d => d.highest_value)
    dataBinding
        .transition()
        .ease(d3.easeLinear)
        .duration(transitionTime)
        .delay(index * transitionTime)
        .attr('tickColor', 'rgba(252, 110, 34,0.9)')
        .attr('x', d => d.value)
        .attr('highest_val', d => d.highest_value)
        .attr('tickSize', 7)
    dataBinding
        .exit()
        .transition()
        .duration(transitionTime)
        .delay(index * transitionTime)
        .attr('tickColor', 'rgba(252, 110, 34,0)')
        .remove()
}

function drawCanvas() {
    context.clearRect(0, 0, width + 100, height)

    // draw main line
    context.beginPath()
    context.strokeStyle = 'black'
    context.moveTo(axis_rectangle[0], axis_rectangle[1]);
    context.lineTo(axis_rectangle[2], axis_rectangle[3] - tick_length)
    context.stroke()

    // draw ticks and tick labels
    const elements = dataContainer.selectAll('custom.ticks')
    elements.each(function (d) {
        const node = d3.select(this)
        const tick_len = parseFloat(node.attr('tickSize'))
        const xVal = parseFloat(node.attr('x'))
        // ticks
        const point = scaleLin(
            xVal,
            [0, parseFloat(node.attr('highest_val'))],
            [axis_rectangle[0], axis_rectangle[2]]
        )
        context.beginPath()
        context.strokeStyle = node.attr('tickColor')
        context.moveTo(point + axis_rectangle[0], axis_rectangle[1])
        context.lineTo(point + axis_rectangle[0], axis_rectangle[1] + tick_len)
        context.stroke()
        // tick label
        context.fillStyle = node.attr('tickColor')
        context.font = '14px Arial'
        context.textAlign = 'center'
        context.fillText(
            node.attr("tickLabel"),
            point + axis_rectangle[0],
            axis_rectangle[1] + tick_len + 15
        )
        context.closePath()
    })
}

for (let index = 0; index < dataArray.length; index++) {
    drawCustom(dataArray[index], index)
}
d3.timer(drawCanvas)


setTimeout(() => {
    document.body.append(root)
})




function scaleLin(value, domain, range) {
    const dominDiff = domain[1] - domain[0]
    const rangeDiff = range[1] - range[0]
    const ratio = rangeDiff / dominDiff
    return range[0] + value * ratio
}

function allowedAxisUnits(n) {
    const u = axisNiceUnit(n)
    const returnArray = []
    let count = 0
    while (u * count <= n) {
        returnArray.push(u * count)
        count++
    }
    returnArray.push(u * count)
    count++
    returnArray.push(u * count)

    return returnArray
}

function axisNiceUnit(n) {
    const f = firstDigitPower(n)
    if (n < 0.5 * 10 ** (f + 1)) {
        return 1 * 10 ** f
    } else {
        return 2 * 10 ** f
    }
}

// 得出数字的位数,如 1=>0  0.1=>-1  0.01=>-2
function firstDigitPower(n) {
    if (n < 0) {
        n *= -1
    }
    let count = 0
    if (n >= 1) {
        const n1 = parseInt(n)
        return n1.toString().length - 1
    } else {
        while (n < 1) {
            // console.log(n)
            count = count - 1
            n = n * 10
        }
        return count
    }
}
console 命令行工具 X clear

                    
>
console