画布和PHP - 绘制连接的线段

时间:2013-11-25 06:29:07

标签: php canvas

我正在尝试使用画布从MySql表中的数据绘制连接线。表(gene_dna_segments)包含每个线段的长度和段名称。

所需输出是由每个段组成的连续直水平线。每个细分受众群还需要显示相应细分受众群上方的细分受众群名称,如下图所示:

enter image description here

+----------------------+--------------------+------------------+--------------------+
| gene_dna_segments_pk | gene_expression_fk | dna_segment_name | dna_segment_length |
+----------------------+--------------------+------------------+--------------------+
|                    1 |                 11 | Exon 1           |                 50 |
|                    2 |                 11 | Intron 1         |                 75 |
|                    3 |                 11 | Exon 2           |                 20 |
|                    4 |                 11 | Intron 2         |                 90 |
+----------------------+--------------------+------------------+--------------------+

查询(老式没有PDO ......):

$query_dna = "SELECT * FROM gene_dna_segments WHERE gene_expression_fk = '11'";
$result_dna = mysql_query($query_dna, $connection) or die(mysql_error());

显示:

 <canvas id="canvas" width="800" height="500"></canvas>
    <script type="text/javascript">
    var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d');
    <?php
    while($row_dna = mysql_fetch_assoc($result_dna)) {

   echo "context.beginPath();context.moveTo(100, 100);context.lineTo(" . $row_dna['dna_segment_length'] . ", 100);context.lineWidth = 12;context.strokeStyle = '#009543';context.stroke();context.font = 'bold 12pt Calibri';
    context.fillStyle = '#009543';
    context.fillText('" . $row_dna['dna_segment_name'] . "', 180, 90);";
    }
    ?>
    </script>

现在,表格中定义的线段和文本被绘制得很好,但是相互叠加,因为context.moveTo(100, 100)对于在while循环中输出的每一行都是相同的。应该如何处理这些段以便将它们绘制成一条连续的线?

可能最简单的方法是在表格中添加一个起点列,并根据前一段的长度计算每个段的起点...我已经就这种可能性Calculating new array values based on another numeric array <打开了一个单独的问题/ p>

请注意,绘制线条的“普通”代码类似于以下代码,请注意,moveto部分从上一行的末尾开始。我需要在循环中做同样的事情......

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

context.beginPath();
context.moveTo(100, 100);
context.lineTo(150, 100);
context.lineWidth = 12;
context.strokeStyle = '#009543';
context.stroke();

context.font = 'bold 11pt Calibri';
context.fillStyle = '#009543';
context.fillText('Exon 1', 105, 90);

context.beginPath();
context.moveTo(150, 100);
context.lineTo(225, 100);
context.lineWidth = 12;
context.strokeStyle = '#e97300';
context.stroke();

context.font = 'bold 11pt Calibri';
context.fillStyle = '#e97300';
context.fillText('Intron 1', 165, 90);

context.beginPath();
context.moveTo(225, 100);
context.lineTo(275, 100);
context.lineWidth = 12;
context.strokeStyle = '#009543';
context.stroke();  

context.font = 'bold 11pt Calibri';
context.fillStyle = '#009543';
context.fillText('Exon 2', 230, 90);

context.beginPath();
context.moveTo(275, 100);
context.lineTo(375, 100);
context.lineWidth = 12;
context.strokeStyle = '#e97300';
context.stroke();  

context.font = 'bold 11pt Calibri';
context.fillStyle = '#e97300';
context.fillText('Intron 2', 300, 90);

1 个答案:

答案 0 :(得分:1)

[根据提问者的新信息更新]

您所描述的是水平堆积的条形图

演示:http://jsfiddle.net/m1erickson/RzMWS/

你的条形图非常简单。

小问题:

我们需要在每个栏上方显示segment_name标签。在完美的世界中,我们可以使用segment_length作为条宽。但实际上,有时候条形宽度太窄而不适合上面的文字标签。

因此,我们必须预处理数据并确定条形的适当比例因子。通过按比例缩放条形,每个条形将保持与其segment_length成比例,并且也足够宽以适合segment_name标签。

以下是评论很多的代码。

当然,您将从数据库读取而不是读取rows []数组中的测试数据。

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    // get references to the canvas

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    ctx.font = 'bold 11pt Calibri';


    // define the color of the bar for each segment_name

    var colorCodes={
        Exon1:"green",
        Intron1:"orange",
        Exon2:"green",
        Intron2:"orange"
    };


    // create some test data

    var rows=[];
    rows.push({dna_segment_name:"Exon 1",dna_segment_length:50});
    rows.push({dna_segment_name:"Intron 1",dna_segment_length:75});
    rows.push({dna_segment_name:"Exon 2",dna_segment_length:20});
    rows.push({dna_segment_name:"Intron 2",dna_segment_length:90});

    // some variables
    // segments[]: pre-processed data will be saved into segments[]
    // scaleFactor: scales every bar if any text label fails to fit
    // padding: allow minimum padding between text labels

    var segments=[];
    var scaleFactor=1;
    var padding=5;

    // pre-process

    // Of course, you will be reading from your database Fetch
    // instead of this test data in rows[]

    for(var i=0;i<rows.length;i++){

        var $row_dna=rows[i];

        // make variables for the segment_name & segment_length
        // being read from the data fetch

        var name=$row_dna['dna_segment_name'];
        var length=$row_dna['dna_segment_length'];

        // lookup the color for this segment_name

        var color=colorCodes[name.replace(/\s+/g, '')];

        // rescale the bars if any text won't fit

        var textWidth=ctx.measureText(name).width+padding;
        var textRatio=textWidth/length;
        if (textRatio>scaleFactor){ scaleFactor=textRatio; }

        // save the pre-processed info in a javascript object
        // for later processing

        segments.push({
            name:name,
            length:length*scaleFactor,
            color:color
        });

    }

    // draw the stacked bar-chart 
    // based on the preprocessed JS objects in segments[]

    var accumLength=0;
    var y=100;  // the Y-coordinate of the barchart

    for(var i=0;i<segments.length;i++){

        // load the object for the current bar

        var segment=segments[i];

        // set the bar color

        ctx.fillStyle=segment.color;

        // draw the bar

        ctx.fillRect(accumLength,y,segment.length,12);

        // draw the text label

        ctx.fillText(segment.name,accumLength,y-10);

        // accumulate where the next bar will begin

        accumLength+=segment.length;
    }


}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=400 height=300></canvas>
</body>
</html>