水柱刻度图edit icon

创建者:
流光
Fork(复制)
下载
嵌入
BUG反馈
index.html
index.html
            
            <!DOCTYPE html>
<html>

<head>
    <title>圆柱刻度图</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: Arial, sans-serif;
        }
        
        .chart-container {
            position: relative;
            width: 300px;
            text-align: center;
        }
        
        canvas {
            display: block;
            margin: 0 auto;
        }
        
        .chart-title {
            margin-top: 10px;
            font-size: 18px;
            font-weight: bold;
        }
        
        .chart-value {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 24px;
            font-weight: bold;
            color: #333;
        }
    </style>
</head>

<body>
    <div class="chart-container">
        <canvas id="cylinderGauge" width="200" height="300"></canvas>
        <div class="chart-value" id="gaugeValue">75</div>
    </div>

    <script>
        const canvas = document.getElementById('cylinderGauge');
        const ctx = canvas.getContext('2d');
        const valueDisplay = document.getElementById('gaugeValue');

        // 图表配置
        const config = {
            width: canvas.width,
            height: canvas.height,
            centerX: canvas.width / 2,
            centerY: canvas.height / 2,
            cylinderRadius: 60,
            cylinderHeight: 240,
            radiusRate: 0.25, // 椭圆扁平度,越小越扁
            segments: 12,
            value: 75, // 当前值(百分比)
            maxValue: 350,
            animationDuration: 1000, // 动画持续时间(毫秒)
            tickGap: 50, // 刻度值
            colors: {
                empty: '#DFE6F1',
                fill: '#36C5F7',
                tick: '#7f8c8d',
                border: '#34495e',
                highlight: '#5dade2'
            }
        };

        // 绘制圆柱刻度图
        function drawCylinderGauge() {
            // 清除画布
            ctx.clearRect(0, 0, config.width, config.height);

            // 绘制背景圆柱
            drawCylinderBg(0, config.cylinderHeight, config.colors.empty);

            // 绘制填充部分
            const fillHeight = (config.value / config.maxValue) * config.cylinderHeight;
            drawCylinderVal(config.cylinderHeight - fillHeight, config.cylinderHeight, config.colors.fill);

            // 绘制刻度线
            drawTicks();

            // 绘制顶部椭圆
            drawTopEllipse();

            // 绘制3D效果边框
            // drawBorder();
        }

        // 绘制圆柱背景部分
        function drawCylinderBg(startY, endY, color) {
            const segmentAngle = (Math.PI * 2) / config.segments;
            const topY = config.centerY - config.cylinderHeight / 2;

            ctx.fillStyle = color;
            // ctx.globalCompositeOperation = 'darken';
            for (let i = 0; i < config.segments; i++) {
                const angle1 = i * segmentAngle;
                const angle2 = (i + 1) * segmentAngle;

                ctx.beginPath();

                // 顶部两点
                const x1 = config.centerX + Math.cos(angle1) * config.cylinderRadius;
                const y1 = topY + startY + Math.sin(angle1) * config.cylinderRadius * config.radiusRate;

                const x2 = config.centerX + Math.cos(angle2) * config.cylinderRadius;
                const y2 = topY + startY + Math.sin(angle2) * config.cylinderRadius * config.radiusRate;

                // 底部两点
                const x3 = x2;
                const y3 = topY + endY + Math.sin(angle2) * config.cylinderRadius * config.radiusRate;

                const x4 = x1;
                const y4 = topY + endY + Math.sin(angle1) * config.cylinderRadius * config.radiusRate;

                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.lineTo(x3, y3);
                ctx.lineTo(x4, y4);
                ctx.closePath();
                ctx.fill();
            }
        }

        function drawCylinderVal(startY, endY, color) {

            const topY = config.centerY - config.cylinderHeight / 2;
            ctx.fillStyle = color;

            //下椭圆
            const bottomY = config.cylinderRadius / 2 + config.cylinderHeight;
            ctx.beginPath();
            ctx.ellipse(
                config.centerX,
                bottomY,
                config.cylinderRadius,
                config.cylinderRadius * config.radiusRate,
                0,
                0,
                Math.PI * 2
            );
            ctx.fill();

            //中间矩形
            ctx.beginPath();
            ctx.fillStyle = color;

            // 顶部两点
            const x1 = config.centerX - config.cylinderRadius;
            const y1 = topY + startY;
            const x2 = config.centerX + config.cylinderRadius;
            const y2 = topY + startY;
            // 底部两点
            const x3 = x2;
            const y3 = topY + endY;
            const x4 = x1;
            const y4 = topY + endY;

            ctx.moveTo(x1, y1);
            ctx.lineTo(x2, y2);
            ctx.lineTo(x3, y3);
            ctx.lineTo(x4, y4);
            ctx.closePath();
            ctx.fill();

            //上椭圆
            ctx.beginPath();
            ctx.fillStyle = "#39ACE7";
            if (topY + startY <= 0) ctx.fillStyle = color;
            ctx.ellipse(
                config.centerX,
                topY + startY,
                config.cylinderRadius,
                config.cylinderRadius * config.radiusRate,
                0,
                0,
                Math.PI * 2
            );
            ctx.fill();
        }



        // 绘制顶部椭圆
        function drawTopEllipse() {
            const topY = config.centerY - config.cylinderHeight / 2;

            ctx.fillStyle = "#CFDAE8";
            ctx.beginPath();
            ctx.ellipse(
                config.centerX,
                topY,
                config.cylinderRadius,
                config.cylinderRadius * config.radiusRate,
                0,
                0,
                Math.PI * 2
            );
            ctx.fill();

            // 绘制边缘线
            // ctx.strokeStyle = config.colors.border;
            // ctx.lineWidth = 1;
            // ctx.stroke();
        }

        // 绘制刻度线
        function drawTicks() {
            const topY = config.centerY - config.cylinderHeight / 2;
            const tickLength = 8;

            ctx.strokeStyle = config.colors.tick;
            ctx.lineWidth = 1;
            ctx.font = '10px Arial';
            ctx.fillStyle = config.colors.tick;
            ctx.textAlign = 'right';

            for (let i = 0; i <= config.maxValue; i += 10) {
                const value = i;
                const y = topY + config.cylinderHeight - (value / config.maxValue) * config.cylinderHeight;
                let start_x = config.centerX - config.cylinderRadius - tickLength / 2;
                const end_x = config.centerX - config.cylinderRadius;
                if (i % config.tickGap == 0) {
                    start_x = config.centerX - config.cylinderRadius - tickLength;
                }

                // 绘制刻度线
                ctx.beginPath();
                ctx.moveTo(start_x, y);
                ctx.lineTo(end_x, y);
                ctx.stroke();
                if (i % config.tickGap == 0) {
                    ctx.fillStyle = "#000000";
                    // 绘制刻度值
                    ctx.fillText(value, config.centerX - config.cylinderRadius - tickLength - 5, y + 3);
                }
            }

        }

        // 绘制3D效果边框
        function drawBorder() {
            const topY = config.centerY - config.cylinderHeight / 2;
            const bottomY = config.centerY + config.cylinderHeight / 2;

            // 左侧边框(阴影)
            ctx.strokeStyle = darkenColor(config.colors.border, config.radiusRate);
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(config.centerX - config.cylinderRadius, topY);
            ctx.lineTo(config.centerX - config.cylinderRadius, bottomY);
            ctx.stroke();

            // 右侧边框(高光)
            ctx.strokeStyle = lightenColor(config.colors.border, config.radiusRate);
            ctx.beginPath();
            ctx.moveTo(config.centerX + config.cylinderRadius, topY);
            ctx.lineTo(config.centerX + config.cylinderRadius, bottomY);
            ctx.stroke();

            // 顶部椭圆边框
            ctx.strokeStyle = config.colors.border;
            ctx.beginPath();
            ctx.ellipse(
                config.centerX,
                topY,
                config.cylinderRadius,
                config.cylinderRadius * config.radiusRate,
                0,
                0,
                Math.PI * 2
            );
            ctx.stroke();

            // 底部椭圆边框
            ctx.beginPath();
            ctx.ellipse(
                config.centerX,
                bottomY,
                config.cylinderRadius,
                config.cylinderRadius * config.radiusRate,
                0,
                0,
                Math.PI * 2
            );
            ctx.stroke();
        }

        // 颜色辅助函数
        function adjustColorBrightness(color, amount) {
            const hex = color => parseInt(color.slice(1), 16);
            const r = (hex(color) >> 16) & 255;
            const g = (hex(color) >> 8) & 255;
            const b = hex(color) & 255;

            const adjust = value => Math.max(0, Math.min(255, value + Math.round(255 * amount)));

            return `rgb(${adjust(r)}, ${adjust(g)}, ${adjust(b)})`;
        }

        function interpolateColor(color1, color2, factor) {
            const hex = color => parseInt(color.slice(1), 16);
            const r1 = (hex(color1) >> 16) & 255;
            const g1 = (hex(color1) >> 8) & 255;
            const b1 = hex(color1) & 255;

            const r2 = (hex(color2) >> 16) & 255;
            const g2 = (hex(color2) >> 8) & 255;
            const b2 = hex(color2) & 255;

            const r = Math.round(r1 + (r2 - r1) * factor);
            const g = Math.round(g1 + (g2 - g1) * factor);
            const b = Math.round(b1 + (b2 - b1) * factor);

            return `rgb(${r}, ${g}, ${b})`;
        }

        function lightenColor(color, amount) {
            return adjustColorBrightness(color, amount);
        }

        function darkenColor(color, amount) {
            return adjustColorBrightness(color, -amount);
        }

        // 动画更新数值
        function updateValue(newValue) {
            const startTime = Date.now();
            const startValue = config.value;
            const change = newValue - startValue;

            function animate() {
                const elapsed = Date.now() - startTime;
                const progress = Math.min(elapsed / config.animationDuration, 1);

                // 缓动函数
                const easedProgress = easeOutQuad(progress);

                config.value = startValue + change * easedProgress;
                valueDisplay.textContent = Math.round(config.value);
                drawCylinderGauge();

                if (progress < 1) {
                    requestAnimationFrame(animate);
                }
            }

            animate();
        }

        // 缓动函数
        function easeOutQuad(t) {
            return t * (2 - t);
        }

        // 初始化绘制
        drawCylinderGauge();

        // 示例:点击更新数值
        canvas.addEventListener('click', () => {
            const newValue = Math.floor(Math.random() * 300);
            updateValue(newValue);
        });
    </script>
</body>

</html>
        
编辑器加载中
预览
控制台