已知数组 [{ x, y, radius }, ...], 在canvas中绘制出对应图形

简介: 已知数组 [{ x, y, radius }, ...], 在canvas中绘制出对应图形

源码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>自定义圆角</title>
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
</head>
<body>
<div>已知数组 [{ x, y, radius }, ...], 在canvas中绘制出对应图形</div>
<canvas id="canvas" width="500" height="500"></canvas>
</body>
<script>
    let canvas = document.getElementById('canvas'),
        context = canvas.getContext('2d');
 
    /**
     * json说明,几何点应当按照顺时针方向以此填入。
     */
    let json = [
        //正方形
        // {
        //     x:10,
        //     y:10,
        //     radius:0
        // },
        // {
        //     x:70,
        //     y:10,
        //     radius:0
        // },
        // {
        //     x:70,
        //     y:70,
        //     radius:0
        // },
        // {
        //     x:10,
        //     y:70,
        //     radius:0
        // },
 
 
        //  奇怪的六边形
        // {
        //     x:10,
        //     y:10,
        //     radius:1000
        // },
        // {
        //     x:70,
        //     y:30,
        //     radius:100000
        // },
        // {
        //     x:60,
        //     y:50,
        //     radius:0
        // },
        // {
        //     x:50,
        //     y:50,
        //     radius:10
        // },
        // {
        //     x:60,
        //     y:40,
        //     radius:10
        // },
        // {
        //     x:40,
        //     y:30,
        //     radius:100
        // },
 
 
        // // 不规则的四边形
        // {
        //     x: 286 / 2,
        //     y: 293 / 2,
        //     radius: 4000 / 2
        // },
        // {
        //     x: 400 / 2,
        //     y: 200 / 2,
        //     radius: 50 / 2
        // },
        // {
        //     x: 400 / 2,
        //     y: 400 / 2,
        //     radius: 40 / 2
        // },
        // {
        //     x: 200 / 2,
        //     y: 400 / 2,
        //     radius: 50 / 2
        // },
 
 
        //具有内角的五边形
 
 
        {
            x:60,
            y:80,
            radius: 10
        },
        {
            x:10,
            y:80,
            radius: 10
        },
        {
            x:10,
            y:10,
            radius: 10
        },
        {
            x:60,
            y:5,
            radius: 10
        },
        {
            x:30,
            y:50,
            radius: 10
        },
 
 
 
    ];
 
    drawShape(json2HandlePoint(json));
 
 
    /**
     * 将json点集转化为可操作点handlePoints
     */
    function json2HandlePoint(json) {
 
        let copyJson = JSON.parse(JSON.stringify(json));
        console.log("用户输入的值", copyJson);
 
        //矫正radius
        fixRadius(json);
 
        console.log("画板渲染的值", json);
 
 
        //将所有可操作点保存handlePoint变量中
        let handlePoints = [];
 
        //第一个点
        handlePoints.push({
            start: {x: json[json.length - 1].x, y: json[json.length - 1].y},
            middle: {x: json[0].x, y: json[0].y},
            end: {x: json[1].x, y: json[1].y},
            radius: json[0].radius
        });
 
        //中间所有点
        for (let i = 1; i < json.length - 1; i++) {
            handlePoints.push({
                start: {x: json[i - 1].x, y: json[i - 1].y},
                middle: {x: json[i].x, y: json[i].y},
                end: {x: json[i + 1].x, y: json[i + 1].y},
                radius: json[i].radius
            });
        }
 
        //最后一个点
        handlePoints.push({
            start: {x: json[json.length - 2].x, y: json[json.length - 2].y},
            middle: {x: json[json.length - 1].x, y: json[json.length - 1].y},
            end: {x: json[0].x, y: json[0].y},
            radius: json[json.length - 1].radius
        });
 
 
        return handlePoints;
    }
 
    /**
     * 将异常json的radius按照Sketch模式矫正
     * sketch圆角异常数据处理方法:
     用户可设定radius值,当出现不合法值的时候,原值不变,图像自行优化
 
     说明:如100*50的矩形,可以在50像素的边对应的角,各设置成radius=25,也可以其中一个设为0,另一个设为50,总之就是radius1+radius2=50。当radius1+radius2>50的时候,在绘制里,radius_min不变,radius_max = 50/2,而在json数据中不改变这些数据,已保存用户的数据记录
     */
    function fixRadius(json) {
        //第一个点
        _fixRadius(json[json.length - 1], json[0], json[1]);
 
        for (let i = 1; i < json.length - 1; i++) {
            _fixRadius(json[i - 1], json[i], json[i + 1]);
        }
 
        //最后一个点
        _fixRadius(json[json.length - 2], json[json.length - 1], json[0]);
    }
 
    function _fixRadius(left, curr, right) {
        let {pow, acos, tan} = Math;
 
        const leftLine = pow(pow(left.x - curr.x, 2) + pow(left.y - curr.y, 2), 0.5);
        const rightLine = pow(pow(right.x - curr.x, 2) + pow(right.y - curr.y, 2), 0.5);
 
        //两边最短边
        const minLine = leftLine < rightLine ? leftLine : rightLine;
        //中间角度
        const angleCurr = acos(((left.x - curr.x) * (right.x - curr.x) + (left.y - curr.y) * (right.y - curr.y)) / (leftLine * rightLine));
        //理论上能实现的最大弧度半径
        let maxRadius = minLine * tan(angleCurr / 2);
 
 
 
        /*
            当左右都为0且,当前radius超过理论最大弧度半径时
            将radius缩减到最大弧度半径
         */
        if (left.radius === 0 && right.radius === 0) {
            if (curr.radius > maxRadius) {
                curr.radius = maxRadius;
            }
        }
 
        /*
            当左右任意一边有值,且radius超过理论最大弧度半径的一半时
            将radius缩减到最大弧度半径的一半
         */
 
        if (left.radius > 0 || right.radius > 0) {
            if (curr.radius > maxRadius / 2) {
                curr.radius = maxRadius / 2;
            }
        }
 
    }
 
    function drawShape(handlePoints) {
        //重置路径
        context.beginPath();
 
 
        let currPoint = undefined;
 
        const origin = _toOrigin(handlePoints[0].start, handlePoints[0].middle, handlePoints[0].end, handlePoints[0].radius);
 
        context.moveTo(origin.x, origin.y);
 
        // 测试:添加数字标记
        // context.fillText('0', origin.x, origin.y);
 
 
        for (let i = 1; i < handlePoints.length; i++) {
            currPoint = handlePoints[i];
 
            //绘制圆弧
            context.arcTo(currPoint.middle.x, currPoint.middle.y, currPoint.end.x, currPoint.end.y, currPoint.radius);
 
 
            // 测试:添加数字标记
            // context.fillText(i, currPoint.middle.x, currPoint.middle.y);
        }
 
        currPoint = handlePoints[0];
        context.arcTo(currPoint.middle.x, currPoint.middle.y, currPoint.end.x, currPoint.end.y, currPoint.radius);
 
        context.stroke();
        context.closePath();
    }
 
    /**
     * 已知A、B、C三点坐标,求半径为r的内切圆与AC的切点坐标
     * @param A
     * @param B
     * @param C
     * @param r
     * @returns {{x: *, y: *}}
     */
    function _toOrigin(A, B, C, r) {
        let {pow, acos, tan} = Math;
 
        //ab的长度
        const ab = pow(pow(A.x - B.x, 2) + pow(A.y - B.y, 2), 0.5);
        //bc的长度
        const bc = pow(pow(C.x - B.x, 2) + pow(C.y - B.y, 2), 0.5);
 
        //B的角度
        const angleB = acos(((A.x - B.x) * (C.x - B.x) + (A.y - B.y) * (C.y - B.y)) / (ab * bc));
 
        //BM的长度,即B到切线的长度
        const M = r / tan(angleB / 2);
 
 
        const k = (C.y - B.y) / (C.x - B.x);
 
 
        if (k == '-Infinity') {
            return {
                x: B.x,
                y: B.y - M
            };
        } else if (k == 'Infinity') {
            return {
                x: B.x,
                y: B.y + M
            };
        } else {
            let i = 1;
 
            if (1 / k == '-Infinity') {
                i = -1;
            }
 
            if (k < 0) {
                if ((C.y - B.y) < 0) {
                    i = 1;
                } else {
                    i = -1;
                }
            }
 
            if (k > 0) {
                if (C.y - B.y < 0) {
                    i = -1;
                }
            }
 
 
            return {
                x: B.x + (1 / pow(1 + k * k, 0.5)) * M * i,
                y: B.y + (k / pow(1 + k * k, 0.5)) * M * i
            };
        }
 
 
    }
 
 
</script>
</html>
相关文章
|
7月前
|
前端开发
canvas绘制圆环
canvas绘制圆环
|
前端开发 关系型数据库 容器
利用Canvas进行绘制XY坐标系
原文:利用Canvas进行绘制XY坐标系 首先来一发图 绘制XY的坐标主要是利用Canvas setLeft和setBottom功能(Canvas内置坐标的功能) 1.首先WPF中的坐标系都是从左到右,从上到下的 即左上角位置(0,0)点,所以XY的Canvas要以(RenderTransf...
1742 0
|
5月前
|
前端开发
width展示的是在Canvas画布上的字符串‘Hello‘的宽度
width展示的是在Canvas画布上的字符串‘Hello‘的宽度
|
7月前
|
前端开发
canvas详解04-绘制文字
canvas详解04-绘制文字
97 2
canvas详解04-绘制文字
|
7月前
|
缓存 前端开发 JavaScript
canvas详解01-绘制基本图形
canvas详解01-绘制基本图形
132 2
|
7月前
|
前端开发 API
canvas详解03-绘制图像和视频
canvas详解03-绘制图像和视频
99 1
|
XML 移动开发 前端开发
Canvas 和 SVG 绘图的区别
Canvas 和 SVG 绘图的区别
103 0
|
前端开发
canvas绘制五角星
canvas绘制五角星
191 0
|
前端开发
Canvas绘制圆形头像
Canvas绘制圆形头像
253 0
Canvas绘制圆形头像
|
前端开发
Canvas图形绘制
Canvas图形绘制
178 0
Canvas图形绘制