2017-02-09 14:33
浏览 293


I need to draw line in SVG in a given width, for example 20px.

What I want to achive is to draw another line, what lines width is the half of the original line, and overlap the original line, but not in the center, but aligned left / bottom.

So, if my x1 and x2 for the original line is 30, then new line x1 and x2 is 25, because to right and left width is 5.

Here is a jsFiddle what I want to achive.

UPDATE On the jsfiddle the lines are ok, because x1 == x2, but I have problems when the line are not horizontal or vertical.

I am using the following code to figure out the new line x and y coordinates, but there is always a little gap.

The tricky part is when I want to rotate that line.

I do not see, where is the error, maybe somewhere with the angle functions?

Or is it good, just SVG / HTML is not precise enough?

//Init coordinates and thick
$x1 = 30; $y1 = 20; 
$x2 = 230; $y2 = 270;

$lineThick = 20;
//get the new data
$newData = getNewPositions($x1, $y1, $x2, $y2, $lineThick);

function getNewPositions($x1, $y1, $x2, $y2, $thick) {
    $offset = $thick / 4;
    $new['x1'] = $x1;
    $new['y1'] = $y1;
    $new['x2'] = $x2;
    $new['y2'] = $y2;
    if ($y1 == $y2) {
        $new['y1'] = $y1 - $offset;
        $new['y2'] = $y2 - $offset;
        return $new;
    if ($x1 == $x2) {
        $new['x1'] = $x1 - $offset;
        $new['x2'] = $x2 - $offset;
        return $new;

    $a = abs($y2 - $y1);
    $b = abs($x2 - $x1);
    $c = sqrt(pow($a,2) + pow($b,2));

    $modX = sin($a / $c);
    $modY = sin($b / $c);

    $new['x1'] = $x1 - ($offset * $modX);
    $new['x2'] = $x2 - ($offset * $modX);
    $new['y1'] = $y1 + ($offset * $modY);
    $new['y2'] = $y2 + ($offset * $modY);

    return $new;

echo '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . "
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="300" height="300" viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
        <line x1="<?php echo $x1; ?>" y1="<?php echo $y1; ?>" x2="<?php echo $x2; ?>" y2="<?php echo $y2; ?>" style="stroke-width:<?php echo $lineThick; ?>; stroke: #000;" />
        <line x1="<?php echo $newData['x1']; ?>" y1="<?php echo $newData['y1']; ?>" x2="<?php echo $newData['x2']; ?>" y2="<?php echo $newData['y2']; ?>" style="stroke-width:<?php echo $lineThick / 2; ?>; stroke: #0f0;" />
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • drhzc64482
    drhzc64482 2017-02-09 15:01

    SVG pixel perfect calculation

    svg {
      width: 500px;
    <svg viewBox="0 0 100 100">
      <text x="10" y="8" font-size="5">Example</text>
        <line x1="20" y1="10" x2="20" y2="20" stroke="green" stroke-width="20" />
        <line x1="25" y1="10" x2="25" y2="20" stroke="yellow" stroke-width="10" />
      <text x="10" y="24" font-size="5">Now if you zoom your browser</text>
      <text x="10" y="28" font-size="5">the end line should change visibility!</text>
      <text x="10" y="32" font-size="5">How to fix?</text>
        <line x1="20" y1="35" x2="20" y2="45" stroke="green" stroke-width="20" />
        <line x1="25" y1="34.9" x2="25" y2="45.1" stroke="yellow" stroke-width="10.2" />
      <text x="10" y="50" font-size="5">Make it .2 point bigger so that</text>
      <text x="10" y="55" font-size="5">there is no pixel perfect calculation</text>

    As to why this is happening well svg are by default 100% responsive to all sizes so there is a lot of calculation for the browser as to how to display the different shapes and in that process some pixels get rounded up to closes possible, and if I'm not mistaken that some times is half a pixel.

    点赞 评论
  • dscbxou1900343
    dscbxou1900343 2017-02-09 15:19

    This is the best I could come up with. I'm not sure if it's correct but I hope it will put you on the right path.

    I've used your above PHP code locally and I've come to the exact same conclusion. Some pixels are "off".

    enter image description here

    The only logical conclusion I could come with(other than bugs in the code) was that you were losing precision when doing arrhythmical operations.

    So I've tried to ceil() or floor() some values. After dabbling around a little I modified this:

    $new['x1'] = $x1 - ($offset * $modX);
    $new['x2'] = $x2 - ($offset * $modX);
    $new['y1'] = $y1 + ($offset * $modY);
    $new['y2'] = $y2 + ($offset * $modY);

    To this:

    $new['x1'] = floor($x1 - ($offset * $modX));
    $new['x2'] = floor($x2 - ($offset * $modX));
    $new['y1'] = ceil($y1 + ($offset * $modY));
    $new['y2'] = ceil($y2 + ($offset * $modY));

    And the end result is:

    enter image description here

    While not perfect it's very close. I can only assume precision is lost on the other operations.

    This is not really an answer, but as I said, I hope it puts you on the right path.

    点赞 评论