zoukankan      html  css  js  c++  java
  • 基于HTML5 Canvas的饼状图表实现教程

    昨天我们分享了一款基于HTML5的线性图表应用,效果非常不错,可以看在线DEMO或者实现教程。今天我们继续来分享一款基于HTML5的网页图表,它也是利用Canvas绘制的,但是和前面不同的是,这款图表是饼状图,并且我们可以点击右侧的表格来选中激活当前的图表数据,具体效果可以看DEMO演示。

    html5-canvas-pie-chart

    你也可以在这里查看在线演示

    下面是实现的过程及源码,一起分享给大家。

    HTML代码:

    <div id="container">
        
            <canvas id="chart" width="600" height="500"></canvas>
            <table id="chartData">
                <tr>
                    <th>脚本素材</th><th>下载量</th>
                </tr>
                <tr style="color:#0DA068">
                    <td>jquery图片特效</td><td>1862.12</td>
                </tr>
                <tr style="color:#194E9C">
                    <td>jquery导航菜单</td><td>1316.00</td>
                </tr>
                <tr style="color:#ED9C13">
                    <td>jquery选项卡特效</td><td>712.49</td>
                </tr>
                <tr style="color:#ED5713">
                    <td>jquery文字特效</td><td>3236.27</td>
                </tr>
                <tr style="color:#057249">
                    <td>jquery表单特效</td><td>6122.06</td>
                </tr>
                <tr style="color:#5F91DC">
                    <td>html5特效</td><td>128.11</td>
                </tr>
                <tr style="color:#F88E5D">
                    <td>html5 图表</td><td>245.55</td>
                </tr>
            </table>
        </div>

    HTML代码有两部分,第一部分是一个canvas标签,我们的饼状图就在这里绘制。另一部分是右侧的数据分类表格,点击表格的行就可以选中并激活图表中对应的数据块。

    接下来是CSS代码:

    /* reset */
    *{margin:0;padding:0;list-style-type:none;}
    a{blr:expression(this.onFocus=this.blur())}/*去掉a标签的虚线框,避免出现奇怪的选中区域*/
    :focus{outline:0;}
    label{cursor:pointer;}
    img{vertical-align:middle;}
    table{empty-cells:show;border-collapse:collapse;border-spacing:0;}
    h1{font-size:16px;}h2,h3,h4{font-size:14px;}h5,h6{font-size:12px;}
    abbr,acronym{border:0;font-variant:normal}
    address,caption,cite,code,dfn,em,th,var,optgroup{font-style:normal;font-weight:normal}
    input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit}
    input,button,textarea,select{*font-size:100%}
    a,img{border:0;}
    a,a:visited{color:#5e5e5e; text-decoration:none;}
    a:hover{color:#4183C4;text-decoration:underline;}
    .clear{display:block;overflow:hidden;clear:both;height:0;line-height:0;font-size:0;}
    .clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden;}
    .clearfix{display:inline-table;}/* Hides from IE-mac */
    *html .clearfix{height:1%;}
    .clearfix{display:block;}/* End hide from IE-mac */
    *+html .clearfix{min-height:1%;}
    body{font:12px/180% Arial,Lucida,Verdana,"宋体",Helvetica,sans-serif;color:#333;background:#fff;}
    /* shortcut */
    .shortcut{position:fixed;top:0;left:0;z-index:9999;width:100%;}
    *html,*html body /* 修正IE6振动bug */{background-image:url(about:blank);background-attachment:fixed;}
    *html .shortcut{position:absolute;top:expression(eval(document.documentElement.scrollTop));}
    .shortcut{height:28px;line-height:28px;font-size:12px;background:#EEEEEE;text-transform:uppercase;box-shadow:1px 0px 2px rgba(0,0,0,0.2);border-bottom:1px solid #DDDDDD;}
    .shortcut h1{font-size:14px;font-family:"微软雅黑","宋体";}
    .shortcut a,.shortcut h1{padding:0px 10px;letter-spacing:1px;color:#333;text-shadow:0px 1px 1px #fff;display:block;float:left;}
    .shortcut a:hover{background:#fff;}
    .shortcut span.right{float:right;}
    .shortcut span.right a{float:left;display:block;color:#ff6600;font-weight:800;}
    .headeline{height:40px;overflow:hidden;}
    .adv960x90{width:960px;height:90px;overflow:hidden;border:solid 1px #E6E6E6;margin:0 auto;}
    .adv728x90{width:728px;height:90px;overflow:hidden;border:solid 1px #E6E6E6;margin:0 auto;}

    一些基本页面样式设置,没什么特别的。

    最后是Javascript代码:

    $( pieChart );
    
    function pieChart() {
    
      // Config settings
      var chartSizePercent = 55;                        // The chart radius relative to the canvas width/height (in percent)
      var sliceBorderWidth = 1;                         // Width (in pixels) of the border around each slice
      var sliceBorderStyle = "#fff";                    // Colour of the border around each slice
      var sliceGradientColour = "#ddd";                 // Colour to use for one end of the chart gradient
      var maxPullOutDistance = 25;                      // How far, in pixels, to pull slices out when clicked
      var pullOutFrameStep = 4;                         // How many pixels to move a slice with each animation frame
      var pullOutFrameInterval = 40;                    // How long (in ms) between each animation frame
      var pullOutLabelPadding = 65;                     // Padding between pulled-out slice and its label  
      var pullOutLabelFont = "bold 16px 'Trebuchet MS', Verdana, sans-serif";  // Pull-out slice label font
      var pullOutValueFont = "bold 12px 'Trebuchet MS', Verdana, sans-serif";  // Pull-out slice value font
      var pullOutValuePrefix = "$";                     // Pull-out slice value prefix
      var pullOutShadowColour = "rgba( 0, 0, 0, .5 )";  // Colour to use for the pull-out slice shadow
      var pullOutShadowOffsetX = 5;                     // X-offset (in pixels) of the pull-out slice shadow
      var pullOutShadowOffsetY = 5;                     // Y-offset (in pixels) of the pull-out slice shadow
      var pullOutShadowBlur = 5;                        // How much to blur the pull-out slice shadow
      var pullOutBorderWidth = 2;                       // Width (in pixels) of the pull-out slice border
      var pullOutBorderStyle = "#333";                  // Colour of the pull-out slice border
      var chartStartAngle = -.5 * Math.PI;              // Start the chart at 12 o'clock instead of 3 o'clock
    
      // Declare some variables for the chart
      var canvas;                       // The canvas element in the page
      var currentPullOutSlice = -1;     // The slice currently pulled out (-1 = no slice)
      var currentPullOutDistance = 0;   // How many pixels the pulled-out slice is currently pulled out in the animation
      var animationId = 0;              // Tracks the interval ID for the animation created by setInterval()
      var chartData = [];               // Chart data (labels, values, and angles)
      var chartColours = [];            // Chart colours (pulled from the HTML table)
      var totalValue = 0;               // Total of all the values in the chart
      var canvasWidth;                  // Width of the canvas, in pixels
      var canvasHeight;                 // Height of the canvas, in pixels
      var centreX;                      // X-coordinate of centre of the canvas/chart
      var centreY;                      // Y-coordinate of centre of the canvas/chart
      var chartRadius;                  // Radius of the pie chart, in pixels
    
      // Set things up and draw the chart
      init();
    
    
      /**
       * Set up the chart data and colours, as well as the chart and table click handlers,
       * and draw the initial pie chart
       */
    
      function init() {
    
        // Get the canvas element in the page
        canvas = document.getElementById('chart');
    
        // Exit if the browser isn't canvas-capable
        if ( typeof canvas.getContext === 'undefined' ) return;
    
        // Initialise some properties of the canvas and chart
        canvasWidth = canvas.width;
        canvasHeight = canvas.height;
        centreX = canvasWidth / 2;
        centreY = canvasHeight / 2;
        chartRadius = Math.min( canvasWidth, canvasHeight ) / 2 * ( chartSizePercent / 100 );
    
        // Grab the data from the table,
        // and assign click handlers to the table data cells
        
        var currentRow = -1;
        var currentCell = 0;
    
        $('#chartData td').each( function() {
          currentCell++;
          if ( currentCell % 2 != 0 ) {
            currentRow++;
            chartData[currentRow] = [];
            chartData[currentRow]['label'] = $(this).text();
          } else {
           var value = parseFloat($(this).text());
           totalValue += value;
           value = value.toFixed(2);
           chartData[currentRow]['value'] = value;
          }
    
          // Store the slice index in this cell, and attach a click handler to it
          $(this).data( 'slice', currentRow );
          $(this).click( handleTableClick );
    
          // Extract and store the cell colour
          if ( rgb = $(this).css('color').match( /rgb((d+), (d+), (d+)/) ) {
            chartColours[currentRow] = [ rgb[1], rgb[2], rgb[3] ];
          } else if ( hex = $(this).css('color').match(/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/) ) {
            chartColours[currentRow] = [ parseInt(hex[1],16) ,parseInt(hex[2],16), parseInt(hex[3], 16) ];
          } else {
            alert( "Error: Colour could not be determined! Please specify table colours using the format '#xxxxxx'" );
            return;
          }
    
        } );
    
        // Now compute and store the start and end angles of each slice in the chart data
    
        var currentPos = 0; // The current position of the slice in the pie (from 0 to 1)
    
        for ( var slice in chartData ) {
          chartData[slice]['startAngle'] = 2 * Math.PI * currentPos;
          chartData[slice]['endAngle'] = 2 * Math.PI * ( currentPos + ( chartData[slice]['value'] / totalValue ) );
          currentPos += chartData[slice]['value'] / totalValue;
        }
    
        // All ready! Now draw the pie chart, and add the click handler to it
        drawChart();
        $('#chart').click ( handleChartClick );
      }
    
    
      /**
       * Process mouse clicks in the chart area.
       *
       * If a slice was clicked, toggle it in or out.
       * If the user clicked outside the pie, push any slices back in.
       *
       * @param Event The click event
       */
    
      function handleChartClick ( clickEvent ) {
    
        // Get the mouse cursor position at the time of the click, relative to the canvas
        var mouseX = clickEvent.pageX - this.offsetLeft;
        var mouseY = clickEvent.pageY - this.offsetTop;
    
        // Was the click inside the pie chart?
        var xFromCentre = mouseX - centreX;
        var yFromCentre = mouseY - centreY;
        var distanceFromCentre = Math.sqrt( Math.pow( Math.abs( xFromCentre ), 2 ) + Math.pow( Math.abs( yFromCentre ), 2 ) );
    
        if ( distanceFromCentre <= chartRadius ) {
    
          // Yes, the click was inside the chart.
          // Find the slice that was clicked by comparing angles relative to the chart centre.
    
          var clickAngle = Math.atan2( yFromCentre, xFromCentre ) - chartStartAngle;
          if ( clickAngle < 0 ) clickAngle = 2 * Math.PI + clickAngle;
                      
          for ( var slice in chartData ) {
            if ( clickAngle >= chartData[slice]['startAngle'] && clickAngle <= chartData[slice]['endAngle'] ) {
    
              // Slice found. Pull it out or push it in, as required.
              toggleSlice ( slice );
              return;
            }
          }
        }
    
        // User must have clicked outside the pie. Push any pulled-out slice back in.
        pushIn();
      }
    
    
      /**
       * Process mouse clicks in the table area.
       *
       * Retrieve the slice number from the jQuery data stored in the
       * clicked table cell, then toggle the slice
       *
       * @param Event The click event
       */
    
      function handleTableClick ( clickEvent ) {
        var slice = $(this).data('slice');
        toggleSlice ( slice );
      }
    
    
      /**
       * Push a slice in or out.
       *
       * If it's already pulled out, push it in. Otherwise, pull it out.
       *
       * @param Number The slice index (between 0 and the number of slices - 1)
       */
    
      function toggleSlice ( slice ) {
        if ( slice == currentPullOutSlice ) {
          pushIn();
        } else {
          startPullOut ( slice );
        }
      }
    
     
      /**
       * Start pulling a slice out from the pie.
       *
       * @param Number The slice index (between 0 and the number of slices - 1)
       */
    
      function startPullOut ( slice ) {
    
        // Exit if we're already pulling out this slice
        if ( currentPullOutSlice == slice ) return;
    
        // Record the slice that we're pulling out, clear any previous animation, then start the animation
        currentPullOutSlice = slice;
        currentPullOutDistance = 0;
        clearInterval( animationId );
        animationId = setInterval( function() { animatePullOut( slice ); }, pullOutFrameInterval );
    
        // Highlight the corresponding row in the key table
        $('#chartData td').removeClass('highlight');
        var labelCell = $('#chartData td:eq(' + (slice*2) + ')');
        var valueCell = $('#chartData td:eq(' + (slice*2+1) + ')');
        labelCell.addClass('highlight');
        valueCell.addClass('highlight');
      }
    
     
      /**
       * Draw a frame of the pull-out animation.
       *
       * @param Number The index of the slice being pulled out
       */
    
      function animatePullOut ( slice ) {
    
        // Pull the slice out some more
        currentPullOutDistance += pullOutFrameStep;
    
        // If we've pulled it right out, stop animating
        if ( currentPullOutDistance >= maxPullOutDistance ) {
          clearInterval( animationId );
          return;
        }
    
        // Draw the frame
        drawChart();
      }
    
     
      /**
       * Push any pulled-out slice back in.
       *
       * Resets the animation variables and redraws the chart.
       * Also un-highlights all rows in the table.
       */
    
      function pushIn() {
        currentPullOutSlice = -1;
        currentPullOutDistance = 0;
        clearInterval( animationId );
        drawChart();
        $('#chartData td').removeClass('highlight');
      }
     
     
      /**
       * Draw the chart.
       *
       * Loop through each slice of the pie, and draw it.
       */
    
      function drawChart() {
    
        // Get a drawing context
        var context = canvas.getContext('2d');
            
        // Clear the canvas, ready for the new frame
        context.clearRect ( 0, 0, canvasWidth, canvasHeight );
    
        // Draw each slice of the chart, skipping the pull-out slice (if any)
        for ( var slice in chartData ) {
          if ( slice != currentPullOutSlice ) drawSlice( context, slice );
        }
    
        // If there's a pull-out slice in effect, draw it.
        // (We draw the pull-out slice last so its drop shadow doesn't get painted over.)
        if ( currentPullOutSlice != -1 ) drawSlice( context, currentPullOutSlice );
      }
    
    
      /**
       * Draw an individual slice in the chart.
       *
       * @param Context A canvas context to draw on  
       * @param Number The index of the slice to draw
       */
    
      function drawSlice ( context, slice ) {
    
        // Compute the adjusted start and end angles for the slice
        var startAngle = chartData[slice]['startAngle']  + chartStartAngle;
        var endAngle = chartData[slice]['endAngle']  + chartStartAngle;
          
        if ( slice == currentPullOutSlice ) {
    
          // We're pulling (or have pulled) this slice out.
          // Offset it from the pie centre, draw the text label,
          // and add a drop shadow.
    
          var midAngle = (startAngle + endAngle) / 2;
          var actualPullOutDistance = currentPullOutDistance * easeOut( currentPullOutDistance/maxPullOutDistance, .8 );
          startX = centreX + Math.cos(midAngle) * actualPullOutDistance;
          startY = centreY + Math.sin(midAngle) * actualPullOutDistance;
          context.fillStyle = 'rgb(' + chartColours[slice].join(',') + ')';
          context.textAlign = "center";
          context.font = pullOutLabelFont;
          context.fillText( chartData[slice]['label'], centreX + Math.cos(midAngle) * ( chartRadius + maxPullOutDistance + pullOutLabelPadding ), centreY + Math.sin(midAngle) * ( chartRadius + maxPullOutDistance + pullOutLabelPadding ) );
          context.font = pullOutValueFont;
          context.fillText( pullOutValuePrefix + chartData[slice]['value'] + " (" + ( parseInt( chartData[slice]['value'] / totalValue * 100 + .5 ) ) +  "%)", centreX + Math.cos(midAngle) * ( chartRadius + maxPullOutDistance + pullOutLabelPadding ), centreY + Math.sin(midAngle) * ( chartRadius + maxPullOutDistance + pullOutLabelPadding ) + 20 );
          context.shadowOffsetX = pullOutShadowOffsetX;
          context.shadowOffsetY = pullOutShadowOffsetY;
          context.shadowBlur = pullOutShadowBlur;
    
        } else {
    
          // This slice isn't pulled out, so draw it from the pie centre
          startX = centreX;
          startY = centreY;
        }
    
        // Set up the gradient fill for the slice
        var sliceGradient = context.createLinearGradient( 0, 0, canvasWidth*.75, canvasHeight*.75 );
        sliceGradient.addColorStop( 0, sliceGradientColour );
        sliceGradient.addColorStop( 1, 'rgb(' + chartColours[slice].join(',') + ')' );
    
        // Draw the slice
        context.beginPath();
        context.moveTo( startX, startY );
        context.arc( startX, startY, chartRadius, startAngle, endAngle, false );
        context.lineTo( startX, startY );
        context.closePath();
        context.fillStyle = sliceGradient;
        context.shadowColor = ( slice == currentPullOutSlice ) ? pullOutShadowColour : "rgba( 0, 0, 0, 0 )";
        context.fill();
        context.shadowColor = "rgba( 0, 0, 0, 0 )";
    
        // Style the slice border appropriately
        if ( slice == currentPullOutSlice ) {
          context.lineWidth = pullOutBorderWidth;
          context.strokeStyle = pullOutBorderStyle;
        } else {
          context.lineWidth = sliceBorderWidth;
          context.strokeStyle = sliceBorderStyle;
        }
    
        // Draw the slice border
        context.stroke();
      }
    
    
      /**
       * Easing function.
       *
       * A bit hacky but it seems to work! (Note to self: Re-read my school maths books sometime)
       *
       * @param Number The ratio of the current distance travelled to the maximum distance
       * @param Number The power (higher numbers = more gradual easing)
       * @return Number The new ratio
       */
    
      function easeOut( ratio, power ) {
        return ( Math.pow ( 1 - ratio, power ) + 1 );
      }
    
    };
    View Code

    这里主要是drawChart和drawSlice两个方法。全部代码可以下载源码来学习。源代码下载

  • 相关阅读:
    第三题 bfw在睡觉
    第二题 bfw和zhk的故事
    第一题 奶牛散步
    AC加油站7月比赛总结
    暑期机房联赛
    题解 P5663 【加工零件【民间数据】】
    题解 P1052 【过河】
    并发编程之进程
    网络编程socketserver
    网络编程之黏包
  • 原文地址:https://www.cnblogs.com/jjet/p/3921722.html
Copyright © 2011-2022 走看看