JavaScript - 渲染guage组件的问题

时间:2014-11-04 05:43:54

标签: javascript html5

我正在使用Javascript在我的应用程序中渲染车速表示例。从现有的脚本中,范围值从0到80,我正在尝试更改速度表的范围值 - 0-240。

我可以更改文本标记值,但我无法使针指向文本标记值。

我想通过使指针找到确切的文本标记值来实现。

drawTextMarker()方法中,我更改了以下代码以更改标记中的文本值。

iTickToPrint += 30;

请在下面找到我的代码。

/*jslint plusplus: true, sloppy: true, indent: 4 */
(function () {
    "use strict";
    // this function is strict...
}());

var iCurrentSpeed = 20,
    iTargetSpeed = 20,
    bDecrement = null,
    job = null;

function degToRad(angle) {
    // Degrees to radians
    return ((angle * Math.PI) / 180);
}

function radToDeg(angle) {
    // Radians to degree
    return ((angle * 180) / Math.PI);
}

function drawLine(options, line) {
    // Draw a line using the line object passed in
    options.ctx.beginPath();

    // Set attributes of open
    options.ctx.globalAlpha = line.alpha;
    options.ctx.lineWidth = line.lineWidth;
    options.ctx.fillStyle = line.fillStyle;
    options.ctx.strokeStyle = line.fillStyle;
    options.ctx.moveTo(line.from.X, line.from.Y);

    // Plot the line
    options.ctx.lineTo(
        line.to.X,
        line.to.Y
    );

    options.ctx.stroke();
}

function createLine(fromX, fromY, toX, toY, fillStyle, lineWidth, alpha) {
    // Create a line object using Javascript object notation
    return {
        from: {
            X: fromX,
            Y: fromY
        },
        to:    {
            X: toX,
            Y: toY
        },
        fillStyle: fillStyle,
        lineWidth: lineWidth,
        alpha: alpha
    };
}

function drawOuterMetallicArc(options) {
    /* Draw the metallic border of the speedometer 
     * Outer grey area
     */
    options.ctx.beginPath();

    // Nice shade of grey
    //options.ctx.fillStyle = "rgb(127,127,127)";
    //options.ctx.fillStyle = "rgb(0,0,0)";
    
    var my_gradient = options.ctx.createLinearGradient(0,0,20,145);
    my_gradient.addColorStop(1,"purple");
    my_gradient.addColorStop(0,"pink");
    options.ctx.fillStyle=my_gradient;

    // Draw the outer circle
    options.ctx.arc(options.center.X,
        options.center.Y,
        options.radius-10,
        0,
        Math.PI,
        true);

    // Fill the last object
    options.ctx.fill();
}

function drawInnerMetallicArc(options) {
    /* Draw the metallic border of the speedometer 
     * Inner white area
     */

    options.ctx.beginPath();

    // White
    options.ctx.fillStyle = "rgb(255,255,255)";
    
    //var my_gradient = options.ctx.createLinearGradient(0,0,15,135);
    //my_gradient.addColorStop(1,"purple");
    //my_gradient.addColorStop(0,"brown");
     //options.ctx.fillStyle=my_gradient;

    // Outer circle (subtle edge in the grey)
    options.ctx.arc(options.center.X,
                    options.center.Y,
                    options.radius-10,
                    0,
                    Math.PI);

    options.ctx.fill();
}

function drawMetallicArc(options) {
    /* Draw the metallic border of the speedometer
     * by drawing two semi-circles, one over lapping
     * the other with a bot of alpha transparency
     */

    drawOuterMetallicArc(options);
    drawInnerMetallicArc(options);
}

function drawBackground(options) {
    /* Black background with alphs transparency to
     * blend the edges of the metallic edge and
     * black background
     */
    var i = 0;

    options.ctx.globalAlpha = 0.2;
    //var my_gradient = options.ctx.createLinearGradient(0,0,30,220);
    //my_gradient.addColorStop(0,"white");
    //my_gradient.addColorStop(1,"aqua");
    //options.ctx.fillStyle=my_gradient;
    options.ctx.fillStyle = "rgb(255,255,255)";

    // Draw semi-transparent circles
    for (i = 140; i < 150; i++) {
        options.ctx.beginPath();

        options.ctx.arc(options.center.X,
            options.center.Y,
            i,
            0,
            Math.PI,
            true);

        options.ctx.fill();
    }
}

function applyDefaultContextSettings(options) {
    /* Helper function to revert to gauges
     * default settings
     */

    options.ctx.lineWidth = 2;
    options.ctx.globalAlpha = 0.5;
    options.ctx.strokeStyle = "rgb(255, 255, 255)";
    options.ctx.fillStyle = 'rgb(255,255,255)';
}

function drawSmallTickMarks(options) {
    /* The small tick marks against the coloured
     * arc drawn every 5 mph from 10 degrees to
     * 170 degrees.
     */

    var tickvalue = options.levelRadius - 8,
        iTick = 0,
        gaugeOptions = options.gaugeOptions,
        iTickRad = 0,
        onArchX,
        onArchY,
        innerTickX,
        innerTickY,
        fromX,
        fromY,
        line,
        toX,
        toY;

    applyDefaultContextSettings(options);

    // Tick every 20 degrees (small ticks)
    for (iTick = 10; iTick < 180; iTick += 20) {
        iTickRad = degToRad(iTick);

        /* Calculate the X and Y of both ends of the
         * line I need to draw at angle represented at Tick.
         * The aim is to draw the a line starting on the 
         * coloured arc and continueing towards the outer edge
         * in the direction from the center of the gauge. 
         */

        onArchX = gaugeOptions.radius - (Math.cos(iTickRad) * tickvalue);
        onArchY = gaugeOptions.radius - (Math.sin(iTickRad) * tickvalue);
        innerTickX = gaugeOptions.radius - (Math.cos(iTickRad) * gaugeOptions.radius);
        innerTickY = gaugeOptions.radius - (Math.sin(iTickRad) * gaugeOptions.radius);

        fromX = (options.center.X - gaugeOptions.radius) + onArchX;
        fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + onArchY;
        toX = (options.center.X - gaugeOptions.radius) + innerTickX;
        toY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY;

        // Create a line expressed in JSON
        line = createLine(fromX, fromY, toX, toY, "rgb(127,127,127)", 3, 0.6);

        // Draw the line
        drawLine(options, line);
    }
}

function drawLargeTickMarks(options) {
    /* The large tick marks against the coloured
     * arc drawn every 10 mph from 10 degrees to
     * 170 degrees.
     */

    var tickvalue = options.levelRadius - 8,
        iTick = 0,
        gaugeOptions = options.gaugeOptions,
        iTickRad = 0,
        innerTickY,
        innerTickX,
        onArchX,
        onArchY,
        fromX,
        fromY,
        toX,
        toY,
        line;

    applyDefaultContextSettings(options);

    tickvalue = options.levelRadius - 2;

    // 10 units (major ticks)
    for (iTick = 20; iTick < 180; iTick += 20) {

        iTickRad = degToRad(iTick);

        /* Calculate the X and Y of both ends of the
         * line I need to draw at angle represented at Tick.
         * The aim is to draw the a line starting on the 
         * coloured arc and continueing towards the outer edge
         * in the direction from the center of the gauge. 
         */

        onArchX = gaugeOptions.radius - (Math.cos(iTickRad) * tickvalue);
        onArchY = gaugeOptions.radius - (Math.sin(iTickRad) * tickvalue);
        innerTickX = gaugeOptions.radius - (Math.cos(iTickRad) * gaugeOptions.radius);
        innerTickY = gaugeOptions.radius - (Math.sin(iTickRad) * gaugeOptions.radius);

        fromX = (options.center.X - gaugeOptions.radius) + onArchX;
        fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + onArchY;
        toX = (options.center.X - gaugeOptions.radius) + innerTickX;
        toY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY;

        // Create a line expressed in JSON
        line = createLine(fromX, fromY, toX, toY, "rgb(127,127,127)", 3, 0.6);

        // Draw the line
        drawLine(options, line);
    }
}

function drawTicks(options) {
    /* Two tick in the coloured arc!
     * Small ticks every 5
     * Large ticks every 10
     */
    drawSmallTickMarks(options);
    drawLargeTickMarks(options);
}

function drawTextMarkers(options) {
    /* The text labels marks above the coloured
     * arc drawn every 10 mph from 10 degrees to
     * 170 degrees.
     */
    var innerTickX = 0,
        innerTickY = 0,
        iTick = 0,
        gaugeOptions = options.gaugeOptions,
        iTickToPrint = 0;

    applyDefaultContextSettings(options);

    // Font styling
    options.ctx.font = 'italic 10px sans-serif';
    options.ctx.textBaseline = 'top';
     
    options.ctx.beginPath();

    // Tick every 20 (small ticks)
    for (iTick = 10; iTick < 180; iTick += 20) {
        innerTickX = gaugeOptions.radius - (Math.cos(degToRad(iTick)) * gaugeOptions.radius);
        innerTickY = gaugeOptions.radius - (Math.sin(degToRad(iTick)) * gaugeOptions.radius);

        // Some cludging to center the values (TODO: Improve)
        if (iTick <= 10) {
            options.ctx.fillStyle = 'rgb(0,0,0)';
            options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX,
                    (gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
        } else if (iTick < 50) {
            options.ctx.fillStyle = 'rgb(0,0,0)';
            options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX - 5,
                    (gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
        } else if (iTick < 90) {
            options.ctx.fillStyle = 'rgb(0,0,0)';
            options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX,
                    (gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
        } else if (iTick === 90) {
            options.ctx.fillStyle = 'rgb(0,0,0)';
            options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 4,
                    (gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
        } else if (iTick < 145) {
            options.ctx.fillStyle = 'rgb(0,0,0)';
            options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 10,
                    (gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
        } else {
            options.ctx.fillStyle = 'rgb(0,0,0)';
            options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 15,
                    (gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
        }

        // MPH increase by 10 every 20 degrees
        // iTickToPrint += Math.round(2160 / 9);
        iTickToPrint += 30;
    }

    options.ctx.stroke();
}

function drawSpeedometerPart(options, alphaValue, strokeStyle, startPos) {
    /* Draw part of the arc that represents
    * the colour speedometer arc
    */
    options.ctx.beginPath();

    options.ctx.globalAlpha = alphaValue;
    options.ctx.lineWidth = 5;
    options.ctx.strokeStyle = strokeStyle;

    options.ctx.arc(options.center.X,
        options.center.Y,
        options.levelRadius,
        Math.PI + (Math.PI / 360 * startPos),
        0 - (Math.PI / 360 * 10),
        false);

    options.ctx.stroke();
}

function drawSpeedometerColourArc(options) {
    /* Draws the colour arc.  Three different colours
     * used here; thus, same arc drawn 3 times with
     * different colours.
     * TODO: Gradient possible?
     */

    var startOfGreen = 10,
        endOfGreen = 280,
        endOfOrange = 280;

    drawSpeedometerPart(options, 1.0, "rgb(82, 240, 55)", startOfGreen);
    // drawSpeedometerPart(options, 0.9, "rgb(198, 111, 0)", endOfOrange);
    drawSpeedometerPart(options, 0.9, "rgb(255, 0, 0)", endOfGreen);
}

function drawNeedleDial(options, alphaValue, strokeStyle, fillStyle) {
    /* Draws the metallic dial that covers the base of the
    * needle.
    */
    var i = 0;

    options.ctx.globalAlpha = alphaValue;
    options.ctx.lineWidth = 3;
    options.ctx.strokeStyle = strokeStyle;
    options.ctx.fillStyle = fillStyle;

    // Draw several transparent circles with alpha
    for (i = 0; i < 25; i++) {
        options.ctx.beginPath();
        options.ctx.arc(options.center.X,
            options.center.Y,
            i,
            0,
            Math.PI,
            true);

        options.ctx.fill();
        options.ctx.stroke();
    }
}

function convertSpeedToAngle(options) {
    /* Helper function to convert a speed to the 
    * equivelant angle.
    */
     
    var iSpeed = (options.speed / 10),
        iSpeedAsAngle = ((iSpeed * 20) + 10) % 180;

    // Ensure the angle is within range
    if (iSpeedAsAngle < 0) {
        iSpeedAsAngle = iSpeedAsAngle + 180;
    }else if(iSpeedAsAngle > 180) {
        iSpeedAsAngle = iSpeedAsAngle - 180;
    }
    
    return iSpeedAsAngle;
}

function drawNeedle(options) {
    /* Draw the needle in a nice read colour at the
    * angle that represents the options.speed value.
    */

    var iSpeedAsAngle = convertSpeedToAngle(options),
        iSpeedAsAngleRad = degToRad(iSpeedAsAngle),
        gaugeOptions = options.gaugeOptions,
        innerTickX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * 20),
        innerTickY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * 20),
        fromX = (options.center.X - gaugeOptions.radius) + innerTickX,
        fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY,
        endNeedleX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * gaugeOptions.radius),
        endNeedleY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * gaugeOptions.radius),
        toX = (options.center.X - gaugeOptions.radius) + endNeedleX,
        toY = (gaugeOptions.center.Y - gaugeOptions.radius) + endNeedleY,
        line = createLine(fromX, fromY, toX, toY, "rgb(255,0,0)", 5, 0.6);

    drawLine(options, line);

    // Two circle to draw the dial at the base (give its a nice effect?)
    drawNeedleDial(options, 0.6, "rgb(0, 0, 0)", "rgb(255,255,255)");
    drawNeedleDial(options, 0.2, "rgb(0, 0, 0)", "rgb(127,127,127)");
}

function buildOptionsAsJSON(canvas, iSpeed) {
    /* Setting for the speedometer 
    * Alter these to modify its look and feel
    */
    var centerX = 170,
        centerY = 170,
        radius = 110,
        outerRadius = 175;

    // Create a speedometer object using Javascript object notation
    return {
        ctx: canvas.getContext('2d'),
        speed: iSpeed,
        center:    {
            X: centerX,
            Y: centerY
        },
        levelRadius: radius - 10,
        gaugeOptions: {
            center:    {
                X: centerX,
                Y: centerY
            },
            radius: radius
        },
        radius: outerRadius
    };
}

function clearCanvas(options) {
    options.ctx.clearRect(0, 0, 800, 600);
    applyDefaultContextSettings(options);
}

function draw() {
    /* Main entry point for drawing the speedometer
    * If canvas is not support alert the user.
    */
    console.log('Target: ' + iTargetSpeed);
    console.log('Current: ' + iCurrentSpeed);

    var canvas = document.getElementById('tutorial'),
        options = null;

    // Canvas good?
    if (canvas !== null && canvas.getContext) {
        options = buildOptionsAsJSON(canvas, iCurrentSpeed);

        // Clear canvas
        clearCanvas(options);

        // Draw the metallic styled edge
        drawMetallicArc(options);

        // Draw thw background
        drawBackground(options);

        // Draw tick marks
        drawTicks(options);

        // Draw labels on markers
        drawTextMarkers(options);

        // Draw speeometer colour arc
        drawSpeedometerColourArc(options);

        // Draw the needle and base
        drawNeedle(options);
    } else {
        alert("Canvas not supported by your browser!");
    }

    if(iTargetSpeed == iCurrentSpeed) {
        clearTimeout(job);
        return;
    } else if(iTargetSpeed < iCurrentSpeed) {
        bDecrement = true;
    } else if(iTargetSpeed > iCurrentSpeed) {
        bDecrement = false;
    }

    if(bDecrement) {
        if(iCurrentSpeed - 10 < iTargetSpeed)
            iCurrentSpeed = iCurrentSpeed - 1;
        else
            iCurrentSpeed = iCurrentSpeed - 5;
    } else {
        if(iCurrentSpeed + 10 > iTargetSpeed)
            iCurrentSpeed = iCurrentSpeed + 1;
        else
            iCurrentSpeed = iCurrentSpeed + 5;
    }

    job = setTimeout("draw()", 5);
}

function drawWithInputValue() {
    var txtSpeed = document.getElementById('txtSpeed');
     
    if (txtSpeed !== null) {
        iTargetSpeed = txtSpeed.value;

        // Sanity checks
        if (isNaN(iTargetSpeed)) {
            iTargetSpeed = 0;
        } else if (iTargetSpeed < 0) {
            iTargetSpeed = 0;
        } else if (iTargetSpeed > 80) {
            iTargetSpeed = 80;
        }

        job = setTimeout("draw()", 5);
    }
}
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Speedometer HTML5 Canvas</title>
    <script src="speedometer.js"></script>
</head>
<body>
    <canvas id="tutorial" width="380" height="170">Canvas not available.</canvas>
    <div>
        <form id="drawTemp">
            <input type="text" id="txtSpeed" name="txtSpeed"> </input>
            <input type="button" value="Draw" onClick="drawWithInputValue();">
        </form>
    </div>
</body>
<script>
    function randomIntFromInterval(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    }
    window.onload = drawsRandomValues;
    function drawsRandomValues() {
        //var n = 25;
        // var number = Math.floor(Math.random()*n)+1;
        var number = randomIntFromInterval(0,220);
        document.getElementById("txtSpeed").innerHTML = number;
        document.getElementById("txtSpeed").value = number;
        //alert(number);
        drawWithInputValue();
    }
    setInterval("drawsRandomValues();", 3000);
</script>
</html>

任何人都建议我在哪里更改代码以实现此目的。

PS:我从Speedometer example

找到了上述脚本

2 个答案:

答案 0 :(得分:0)

似乎是convertSpeedToAngle()的一部分:

var iSpeed = (options.speed / 10),
    iSpeedAsAngle = ((iSpeed * 20) + 10) % 180;

如果你输入40,那么你得到(((40/10)* 20)+ 10)%180 = 90的角度。他们如何想出这种想法是超出我的。

由于您知道范围是0-180度,因此您想知道当前值使用的那些度数的百分比,然后找到以度为单位的相关角度。

范围[0-1]中的小数百分比由options.speed / 240给出,因为240是您的刻度上的最大值。

取这个百分比并乘以180得到它应该在度数方面下降的位置。然后,函数非常简单(没有任何错误检查!):

function convertSpeedToAngle(options) {
  return 180 * options.speed/240;
}

尝试将其插入并查看您的问题是否仍然存在。

答案 1 :(得分:0)

感谢@Miller的想法。我尝试了你的方法,但结果在某些方面有所不同。

顺便说一下,我想出来就像从

更改下面的代码
var iSpeed = (options.speed / 10),
iSpeedAsAngle = ((iSpeed * 20) + 10) % 180;

到下面的新代码

var iSpeed = (options.speed / 30), // 30 --> between tick interval range  
iSpeedAsAngle = ((iSpeed * 20) + 10) ; // omitted the modulo operator of 180

convertSpeedToAngle()方法中代码的上述更改,使指针指向确切的刻度值。