2014-11-04 43 views
0

我正在使用Javascript在我的应用程序中渲染速度计标尺。从现有的脚本,范围值是从0到80,我试图改变车速表的范围值 - 0-240。JavaScript - 渲染标量组件的问题

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

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

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

回答

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; 
} 

尝试堵塞,看看你的问题仍然存在。

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()方法的代码变更,使针指出确切的刻度值。