使用html5画布创建竖起大拇指图标

时间:2017-10-07 07:15:10

标签: javascript html5 canvas

我想用html5画布命令(例如arc,arcto ......)绘制一个类似于(https://thenounproject.com/term/thumbs-up/70801/)的竖起大拇指图标。我知道这些功能,但我不知道如何开始设计竖起大拇指的图标。

设计应分成3 x 3格的网格。

画布将为300 x 300,画布中的每个单元格将为100 x 100。

<canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;">

    <script>
        var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");

        var rectX = 70;
        var rectY = 70;
        var rectWidth = 50;
        var rectHeight = 100;
        var cornerRadius = 10;

        ctx.lineJoin = "round";
        ctx.lineWidth = cornerRadius;

        ctx.strokeRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
    </script>

上面的代码创建了竖起大拇指图标的圆形袖子。

任何人都可以帮助如何创建这样的图标。感谢

1 个答案:

答案 0 :(得分:0)

圆角。

要绘制该形状,您需要绕过路径的角。路径只是一组x和y坐标作为数组。

Beziers太丑了

大多数解决方案都涉及使用贝塞尔曲线,但我发现它们相当难看,因为它们永远不会与圆形相匹配,而在设计世界中,圆形必须是正确的形状(圆形)。

最佳解决方案是使用ctx.arc函数渲染路径,并为每个角提供起始角度和结束角度。这可能很难在脑海中形象化,因此最好将角定义为具有半径,让机器为您计算起点和终点弧角。

因此,您的路径将具有x,y坐标和每个角的半径。半径0不是圆整的。如果形状是闭合的,您还需要在末端创建圆角,而如果形状是打开的,则不需要。

您的形状数据可能看起来像(不是竖起大拇指)

const shape = [
    {x : 10,  y : 10,  r : 20},    
    {x : 150, y : 10,  r : 50},    
    {x : 100, y : 100, r : 20},    
    {x : 50,  y : 100, r : 20},    
    {x : 50,  y : 200, r : 20},    
    {x : 210, y : 100, r : 60},    
    {x : 210, y : 300, r : 20},    
    {x : 10,  y : 300, r : 20},    
    "closed",
];

渲染示例

渲染很复杂,每个角都需要找到弧的中心和弧相交的入射线上的点(切线)。我不打算在这里解释数学,因为我认为你不想这样,但如果你对矢量数学感到满意的话,这个例子有一步一步的解释。

通过检查有效解决方案并提供打开和关闭路径的方法(末端有开弧和闭弧),下面的示例可以让您创建所需的形状。 (我不会绘制积分,懒得这样做)

示例绘制两个形状,即闭合和打开。右边的形状也有一个没有半径的角落。它使用ctx.lineJoin = "miter"来确保角落清晰。虽然您可以通过设置ctx.lineJoin = "round"来围绕外边缘,并且外部圆角的半径等于线宽的一半。

const ctx = canvas.getContext("2d");
const shape = [
    {x : 10,  y : 10,  r : 20},    
    {x : 150, y : 10,  r : 50},    
    {x : 100, y : 100, r : 20},    
    {x : 50,  y : 100, r : 20},    
    {x : 50,  y : 200, r : 20},    
    {x : 210, y : 100, r : 60},    
    {x : 210, y : 300, r : 20},    
    {x : 10,  y : 300, r : 20},    
    "closed",
];
const shapeOpen = [
    {x : 390,  y : 10,  r : 0},    
    {x : 390,  y : 300, r : 50},    
    {x : 310,  y : 300, r : 20},    
    {x : 320,  y : 200, r : 0},    
    {x : 240,  y : 200, r : 40},   
    {x : 240,  y : 10,  r : 0},       
];

// round line ends
ctx.lineCap = "round";
// and sharp corners if no radius
ctx.lineJoin = "miter";

// draw closed and open shapes
drawRoundedShape(shape,12,"Black");
drawRoundedShape(shapeOpen,12,"Black");

function drawRoundedShape(shape, lineWidth, strokeStyle, fillStyle) {
    function roundCorner(p1, p2, p3, returnCorner = {}) {
        var x, y, v1x, v1y, v2x, v2y, leng, l1, cx, cy;
        
        // Convert lines to normalised vectors from p2
        v1x = p1.x - p2.x;
        v1y = p1.y - p2.y;
        v2x = p3.x - p2.x;
        v2y = p3.y - p2.y;
        leng = Math.sqrt(v1x * v1x + v1y * v1y);
        v1x /= leng; 
        v1y /= leng;
        leng = Math.sqrt(v2x * v2x + v2y * v2y);
        v2x /= leng; 
        v2y /= leng;
        
        // check directions
        const cross = (v1x * v2y - v1y * v2x); 
        
        // if near parallel then ignore as roundable.
        if(Math.abs(cross) < 1e-4){
            returnCorner.solutionFound = false;
            return returnCorner;
        }
        
        // set radius sign, arc direction to match corner
        const radius = p2.r * Math.sign(cross); 
        returnCorner.dir = radius > 0;
        returnCorner.solutionFound = true;

        // Get distance from p2 to circle tangent along line p2 - p1
        l1 = radius / cross; 
        l1 += l1 * (v1x * v2x + v1y * v2y);
    
        // Find tangent contact on line p2-p1
        x = p2.x + v1x * l1;
        y = p2.y + v1y * l1;        
        
        // Save first tangent point though not needed could come in handy
        returnCorner.t1x = x;
        returnCorner.t1y = y;

        // Find arc center
        cx = x - v1y * radius;
        cy = y + v1x * radius;
        
        // Find direction of arc start
        returnCorner.ang1 = Math.atan2(y - cy, x - cx);
        
        // Find tangent contact on line p2-p3
        x = p2.x + v2x * l1;
        y = p2.y + v2y * l1;        

        // Find direction of arc end
        returnCorner.ang2 = Math.atan2(y - cy, x - cx);
        
        // Save second tangent point 
        returnCorner.t2x = x;
        returnCorner.t2y = y;

        // Save arc center
        returnCorner.x = cx;
        returnCorner.y = cy;
        
        // Return the corner as an arc
        return returnCorner;
    }
    var corner, i, len, isClosed;
    len = shape.length;
    isClosed = false;
    if (typeof shape[len - 1] === "string" && shape[len - 1].toLowerCase() === "closed") {
        len -= 1;
        isClosed = true;
    }
    // shape must have 3 or more points
    if (len < 3) { return }

    // start rendering
    ctx.beginPath();
    i = 0;
    do {
        if (shape[i].r === 0) { // if the radius is 0 then just set the corner point
            ctx.lineTo(shape[i].x, shape[i].y);
        } else {
        
            // Calculate the corner arc details
            corner = roundCorner(
                shape[(i + len - 1) % len], 
                shape[i],
                shape[(i + 1) % len], 
                corner
            );
            
            // If a valid solution found render the arc
            if (corner.solutionFound) {  // only if there is a rounded corner
                ctx.arc(
                    corner.x,
                    corner.y,
                    shape[i].r,
                    corner.ang1,
                    corner.ang2,
                    corner.dir
                );
            } else {
                ctx.lineTo(shape[i].x, shape[i].y);  // no valid solution so just plot the point
            }
        }
    } while (++i < len);
    
    isClosed && ( ctx.closePath() );

    // The path has been created so fill and or stroke it
    if (fillStyle) {
        ctx.fillStyle = fillStyle;    
        ctx.fill();
    }
    if (strokeStyle) {
        ctx.lineWidth = lineWidth;
        ctx.strokeStyle = strokeStyle;
        ctx.stroke();
    }    
}
canvas { border : 2px solid black; }
<canvas id="canvas" width = "400" height = "310"></canvas>

备注

代码仅在一小组形状上进行了测试,当角落处的弧线位于错误的一侧时,仍可能出现这种情况。如果确实发生了这种情况,那么简单的解决方案就是在角点添加一个标志来反转方向,如果该标志为真,则在roundCorner函数中否定半径。

同样,由于路径是预先计算的,我没有看到需要限制半径。这意味着如果半径导致切线位于进入角落的线段之外,则路径将在被绘制到切线后自行返回。要修复只是缩小角落的半径以适应。