SVG rounded corner

前端 未结 12 2018
谎友^
谎友^ 2020-11-28 22:21

I have the following SVG:



        
相关标签:
12条回答
  • 2020-11-28 22:39

    This question is the first result for Googling "svg rounded corners path". Phrogz suggestion to use stroke has some limitations (namely, that I cannot use stroke for other purposes, and that the dimensions have to be corrected for the stroke width).

    Jlange suggestion to use a curve is better, but not very concrete. I ended up using quadratic Bézier curves for drawing rounded corners. Consider this picture of a corner marked with a blue dot and two red points on adjacent edges:

    corner of a figure marked blue with two points on the adjacent edges

    The two lines could be made with the L command. To turn this sharp corner into a rounded corner, start drawing a curve from the left red point (use M x,y to move to that point). Now a quadratic Bézier curve has just a single control point which you must set on the blue point. Set the end of the curve at the right red point. As the tangent at the two red points are in the direction of the previous lines, you will see a fluent transition, "rounded corners".

    Now to continue the shape after the rounded corner, a straight line in a Bézier curve can be achieved by setting the control point between on the line between the two corners.

    To help me with determining the path, I wrote this Python script that accepts edges and a radius. Vector math makes this actually very easy. The resulting image from the output:

    shape created from script output

    #!/usr/bin/env python
    # Given some vectors and a border-radius, output a SVG path with rounded
    # corners.
    #
    # Copyright (C) Peter Wu <peter@lekensteyn.nl>
    
    from math import sqrt
    
    class Vector(object):
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def sub(self, vec):
            return Vector(self.x - vec.x, self.y - vec.y)
    
        def add(self, vec):
            return Vector(self.x + vec.x, self.y + vec.y)
    
        def scale(self, n):
            return Vector(self.x * n, self.y * n)
    
        def length(self):
            return sqrt(self.x**2 + self.y**2)
    
        def normal(self):
            length = self.length()
            return Vector(self.x / length, self.y / length)
    
        def __str__(self):
            x = round(self.x, 2)
            y = round(self.y, 2)
            return '{},{}'.format(x, y)
    
    # A line from vec_from to vec_to
    def line(vec_from, vec_to):
        half_vec = vec_from.add(vec_to.sub(vec_from).scale(.5))
        return '{} {}'.format(half_vec, vec_to)
    
    # Adds 'n' units to vec_from pointing in direction vec_to
    def vecDir(vec_from, vec_to, n):
        return vec_from.add(vec_to.sub(vec_from).normal().scale(n))
    
    # Draws a line, but skips 'r' units from the begin and end
    def lineR(vec_from, vec_to, r):
        vec = vec_to.sub(vec_from).normal().scale(r)
        return line(vec_from.add(vec), vec_to.sub(vec))
    
    # An edge in vec_from, to vec_to with radius r
    def edge(vec_from, vec_to, r):
        v = vecDir(vec_from, vec_to, r)
        return '{} {}'.format(vec_from, v)
    
    
    # Hard-coded border-radius and vectors
    r = 5
    a = Vector(  0,  60)
    b = Vector(100,   0)
    c = Vector(100, 200)
    d = Vector(  0, 200 - 60)
    
    path = []
    # Start below top-left edge
    path.append('M {} Q'.format(a.add(Vector(0, r))))
    
    # top-left edge...
    path.append(edge(a, b, r))
    path.append(lineR(a, b, r))
    path.append(edge(b, c, r))
    path.append(lineR(b, c, r))
    path.append(edge(c, d, r))
    path.append(lineR(c, d, r))
    path.append(edge(d, a, r))
    path.append(lineR(d, a, r))
    
    # Show results that can be pushed into a <path d="..." />
    for part in path:
        print(part)
    
    0 讨论(0)
  • 2020-11-28 22:40

    You are using a path element, why don't you just give the path a curve? See here for how to make curves using path elements: http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

    0 讨论(0)
  • 2020-11-28 22:43

    I've happened upon this problem today myself and managed to solve it by writing a small JavaScript function.

    From what I can tell, there is no easy way to give a path element in an SVG rounded corners except if you only need the borders to be rounded, in which case the (CSS) attributes stroke, stroke-width and most importantly stroke-linejoin="round" are perfectly sufficient.

    However, in my case I used a path object to create custom shapes with n corners that are filled out with a certain color and don't have visible borders, much like this:

    I managed to write a quick function that takes an array of coordinates for an SVG path and returns the finished path string to put in the d attribute of the path html element. The resulting shape will then look something like this:

    Here is the function:

    /**
     * Creates a coordinate path for the Path SVG element with rounded corners
     * @param pathCoords - An array of coordinates in the form [{x: Number, y: Number}, ...]
     */
    function createRoundedPathString(pathCoords) {
        const path = [];
        const curveRadius = 3;
    
        // Reset indexes, so there are no gaps
        pathCoords = pathCoords.slice();
    
        for (let i = 0; i < pathCoords.length; i++) {
    
          // 1. Get current coord and the next two (startpoint, cornerpoint, endpoint) to calculate rounded curve
          const c2Index = ((i + 1) > pathCoords.length - 1) ? (i + 1) % pathCoords.length : i + 1;
          const c3Index = ((i + 2) > pathCoords.length - 1) ? (i + 2) % pathCoords.length : i + 2;
    
          const c1 = pathCoords[i];
          const c2 = pathCoords[c2Index],
          const c3 = pathCoords[c3Index];
    
          // 2. For each 3 coords, enter two new path commands: Line to start of curve, bezier curve around corner.
    
          // Calculate curvePoint c1 -> c2
          const c1c2Distance = Math.sqrt(Math.pow(c1.x - c2.x, 2) + Math.pow(c1.y - c2.y, 2));
          const c1c2DistanceRatio = (c1c2Distance - curveRadius) / c1c2Distance;
          const c1c2CurvePoint = [
            ((1 - c1c2DistanceRatio) * c1.x + c1c2DistanceRatio * c2.x).toFixed(1),
            ((1 - c1c2DistanceRatio) * c1.y + c1c2DistanceRatio * c2.y).toFixed(1)
          ];
    
          // Calculate curvePoint c2 -> c3
          const c2c3Distance = Math.sqrt(Math.pow(c2.x - c3.x, 2) + Math.pow(c2.y - c3.y, 2));
          const c2c3DistanceRatio = curveRadius / c2c3Distance;
          const c2c3CurvePoint = [
            ((1 - c2c3DistanceRatio) * c2.x + c2c3DistanceRatio * c3.x).toFixed(1),
            ((1 - c2c3DistanceRatio) * c2.y + c2c3DistanceRatio * c3.y).toFixed(1)
          ];
    
          // If at last coord of polygon, also save that as starting point
          if (i === pathCoords.length - 1) {
            path.unshift('M' + c2c3CurvePoint.join(','));
          }
    
          // Line to start of curve (L endcoord)
          path.push('L' + c1c2CurvePoint.join(','));
          // Bezier line around curve (Q controlcoord endcoord)
          path.push('Q' + c2.x + ',' + c2.y + ',' + c2c3CurvePoint.join(','));
        }
        // Logically connect path to starting point again (shouldn't be necessary as path ends there anyway, but seems cleaner)
        path.push('Z');
    
        return path.join(' ');
    }
    

    You can determine the rounding strength by setting the curveRadius variable at the top. The default is 3 for a 100x100 (viewport) coordinate system, but depending on the size of your SVG, you may need to adjust this.

    0 讨论(0)
  • 2020-11-28 22:50

    You have explicitly set your stroke-linejoin to round but your stroke-width to 0, so of course you're not going to see rounded corners if you have no stroke to round.

    Here's a modified example with rounded corners made through strokes:
    http://jsfiddle.net/8uxqK/1/

    <path d="M64.5 45.5 82.5 45.5 82.5 64.5 64.5 64.5 z"
          stroke-width="5"
          stroke-linejoin="round"
          stroke="#808600"
          fill="#a0a700" />
    

    Otherwise—if you need an actual rounded shape fill and not just a rounded fatty stroke—you must do what @Jlange says and make an actual rounded shape.

    0 讨论(0)
  • 2020-11-28 22:56
    <?php
    $radius = 20;
    $thichness = 4;
    $size = 200;
    
    if($s == 'circle'){
      echo '<svg width="' . $size . '" height="' . $size . '">';
      echo '<circle cx="' . ($size/2) . '" cy="' . ($size/2) . '" r="' . (($size/2)-$thichness) . '" stroke="black" stroke-width="' . $thichness . '" fill="none" />';
      echo '</svg>';
    }elseif($s == 'square'){
      echo '<svg width="' . $size . '" height="' . $size . '">';
      echo '<path d="M' . ($radius+$thichness) . ',' . ($thichness) . ' h' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 ' . $radius . ',' . $radius . ' v' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 -' . $radius . ',' . $radius . ' h-' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 -' . $radius . ',-' . $radius . ' v-' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 ' . $radius . ',-' . $radius . ' z" fill="none" stroke="black" stroke-width="' . $thichness . '" />';
      echo '</svg>';
    }
    ?>
    
    0 讨论(0)
  • 2020-11-28 22:57

    I found a solution but it is a bit hacky so it may not always work. I found that if you have an arc (A or a) with really small values it forces it to create a curve in one spot thus forming a rounded comer...

    <svg viewBox="0 0 1 0.6" stroke="black" fill="grey" style="stroke-width:0.05px;">
      <path d="M0.7 0.2 L0.1 0.1 A0.0001 0.0001 0 0 0 0.099 0.101 L0.5 0.5Z"></path>
    </svg>

    0 讨论(0)
提交回复
热议问题