【问题标题】:How to create a rounded hexagon with javascript andCANVAS如何使用 javascript 和 CANVAS 创建圆角六边形
【发布时间】:2020-11-12 15:37:48
【问题描述】:

我创建了this codepen 来展示我得到了什么。

我使用 Max Lawrence 的出色开源 Hexagon Progress jQuery 插件设法生成了一个带有进度条的六边形头像。

他还帮我稍微改进了自己的代码,但我不想再打扰他了。

也许这里有人可以帮我把这个六边形的角弄圆。

我希望它看起来像这样(来自很棒的 Vikinger Html 模板)但需要开源,因为我的软件都是开源的。我无法使用 Vikinger 代码。

到目前为止,我读到我必须在结束前停止该行并在下一行开始时添加一条二次曲线,但我无法做到。

他的代码在第 505 行做了这样的事情:

ctx.moveTo(this.coordBack[0].x + offset, this.coordBack[0].y + offset);
for(var i = 0; i < this.coordBack.length; i++) {
    ctx.lineTo(this.coordBack[i].x + offset, this.coordBack[i].y + offset);
}

不幸的是,我的 JavaScript 或数学不太好。

【问题讨论】:

    标签: javascript jquery html5-canvas


    【解决方案1】:

    有两种方法可以做到这一点。简单的方法,以及冗长的、大量的数学方法。

    简单的圆角

    要创建简单的圆角多边形,您可以使用ctx.arcTo。它会为角落做所有的数学运算。

    要创建多边形,以下函数会创建一个点和一个路径(点数组)

    const Point = (x,y) => ({x, y});
    function polygon(sides, rad, rot = 0) {
        var i = 0, step = Math.PI * 2 / sides, path = [];
        while (i < sides) {
            path.push(Point(Math.cos(i * step + rot) * rad, Math.sin((i++) * step + rot) * rad));
        }
        return path;
    }
    

    创建一个六边形。请注意,多边形以其本地原点 0,0

    为中心
    const hexagon = polygon(6, 100);
    

    要渲染圆形多边形,您需要从线段中心开始工作。以下函数将用圆角描边路径。

    function strokeRoundedPath(cx, cy, path, radius, style, width) {
        ctx.setTransform(1,0,0,1,cx,cy);
        var i = 0;
        const len = path.length
        var p1 = path[i++], p2 = path[i];
        ctx.lineWidth = width;
        ctx.lineCap = "round";
        ctx.strokeStyle = style;
        
        ctx.beginPath();
        
        ctx.lineTo((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
        while (i <= len) {
            p1 = p2;
            p2 = path[(++i) % len];
            ctx.arcTo(p1.x, p1.y, (p1.x + p2.x) / 2, (p1.y + p2.y) / 2, radius);
        }
        ctx.closePath();
        ctx.stroke();
        ctx.setTransform(1,0,0,1,0,0);
    }
    
    strokeRoundedPath(200, 200, hexagon, 20, "#000", 18);
    

    进度条

    创建进度条并不像起点不能在圆角上那么简单,在圆角上移动需要大量数学运算才能获得正确的坐标。这将否定使用简单 arcTo 的意义,并需要我们在 JS 中编写等效项(今天要松懈)

    使用短划线表示进度

    但是,有一个 hack 使用破折号来创建您可能会满意的效果。 sn-p 演示了这一点

    const barWidth = 10;
    const cornerRadius = barWidth * 2 + 8;
    const polyRadius = 100;
    const inset = 1;
    const barRadius = polyRadius - barWidth * inset;
    var progress = 0.0;
    const approxLineLen = barRadius * Math.PI * 2;
    const hexBar = polygon(6, barRadius);
    const hexPoly = polygon(6, polyRadius);
    const hexPolyInner = polygon(6, polyRadius - barWidth * 2 * inset);
    const ctx = canvas.getContext("2d");
    ctx.setLineDash([approxLineLen]);
    loop()
    
    
    function point(x,y) { return {x, y} }
    function polygon(sides, radius, rot = 0) {
        var i = 0;
        const step = Math.PI * 2 / sides, path = [];
        while (i < sides) {
            path.push(point(Math.cos(i * step + rot) * radius, Math.sin((i++) * step + rot) * radius));
        }
        return path;
    }
    function roundedPath(path, radius) {
        var i = 0, p1 = path[i++], p2 = path[i];
        const len = path.length
        ctx.moveTo((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
        while (i <= len) {
            p1 = p2;
            p2 = path[(++i) % len];
            ctx.arcTo(p1.x, p1.y, (p1.x + p2.x) / 2, (p1.y + p2.y) / 2, radius);
        }
    }
    function strokeRoundedPath(cx, cy, path, radius, style, width) {
        ctx.setTransform(1,0,0,1,cx,cy);
        ctx.lineWidth = width;
        ctx.lineCap = "round";
        ctx.strokeStyle = style;
        ctx.beginPath();
        roundedPath(path, radius);
        ctx.closePath();
        ctx.stroke();
    }
    function fillRoundedPath(cx, cy, path, radius, style) {
        ctx.setTransform(1,0,0,1,cx,cy);
        ctx.fillStyle = style;
        ctx.beginPath();
        roundedPath(path, radius);
        ctx.fill();
    }
    function loop() {
        ctx.setTransform(1,0,0,1,0,0);
        ctx.clearRect(0,0,canvas.width,canvas.height);
        fillRoundedPath(polyRadius, polyRadius, hexPoly, cornerRadius, "#000");
        fillRoundedPath(polyRadius, polyRadius, hexPolyInner, cornerRadius - barWidth * inset * 2, "#F80");
        ctx.lineDashOffset = approxLineLen - (progress % 1) * approxLineLen;
        strokeRoundedPath(polyRadius, polyRadius, hexBar, cornerRadius - barWidth * inset, "#09C", barWidth);
        progress += 0.005;
        requestAnimationFrame(loop);
    }
    &lt;canvas id="canvas" width = "210" height="210"&gt;&lt;/canvas&gt;

    【讨论】:

    • 好的,我得到了一些改进codepen.io/programad/pen/VweReYY。你能帮我从顶部开始进度条吗?这里的目标是看起来像第一个 codepen 但圆润。 codepen.io/programad/pen/wvMNRPy
    • @programad 函数 polygon 有第三个可选参数,它将旋转多边形。例如const hexBar = polygon(6, barRadius, -Math.PI * 0.5) 会将路径逆时针旋转 90 度
    猜你喜欢
    • 2019-07-23
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    相关资源
    最近更新 更多