美文网首页
canvas 文字沿曲线排列

canvas 文字沿曲线排列

作者: littlesunn | 来源:发表于2023-06-09 00:48 被阅读0次
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <canvas id="myCanvas" width="1080" height="630" style="border: 1px solid;"></canvas>
    </body>
    
    </html>
    <script>
        const ctx = myCanvas.getContext('2d');
    
        let p1 = [150, 300];
        let p2 = [850, 300];
        let c1 = [400, 150];
        let c2 = [600, 400];
    
        /**
     * 在一段三次贝塞尔曲线上均匀取点, 不包括终点
     * @param counts 一段贝塞尔曲线中取点的数量
     */
        getPntsOf3Bezier = function (p0, p1, p2, p3, counts) {
            let per = counts && counts != 0 ? 1 / counts : 0.02;    //取点间隔
            let points = [];
            for (let t = 0; t <= 0.999999; t += per) {
                points.push(getPntIn3Bezier(p0, p1, p2, p3, t));
            }
            return points;
        };
    
        /**
         * 获取三次贝塞尔曲线上的一点,t∈[0,1]
         * @param t 介于0 ~ 1, 表示点在曲线中的相对位置
         */
        getPntIn3Bezier = function (p0, p1, p2, p3, t) {
            let t_ = 1 - t;
            let x = p0[0] * t_ * t_ * t_ + 3 * p1[0] * t * t_ * t_ + 3 * p2[0] * t * t * t_ + p3[0] * t * t * t,
                y = p0[1] * t_ * t_ * t_ + 3 * p1[1] * t * t_ * t_ + 3 * p2[1] * t * t * t_ + p3[1] * t * t * t;
            return [x, y];
        };
    
    
        function getVctLen([x, y]) {  // 模长
            return Math.sqrt(x * x + y * y);
        };
    
        /**
    * 根据点在向量上的比例计算点坐标, [xO, yO]为起点,[xVct, yVct]为向量,k 为该点在向量方向上的长度
    * 获取
    */
        function getPntInVct([xO, yO], [xVct, yVct], k) {
            let lenVct = getVctLen([xVct, yVct]);  // 获取向量长度
            let stdVct;
            if (lenVct === 0) {
                stdVct = [0, 0];
            } else {
                stdVct = [xVct / lenVct, yVct / lenVct];   // 单位向量
            }
            return [xO + k * stdVct[0], yO + k * stdVct[1]];
        };
    
        function getLenOfTwoPnts(p1, p2) {
            return Math.sqrt(Math.pow((p1[0] - p2[0]), 2) + Math.pow((p1[1] - p2[1]), 2));
        };
    
        vct = function (start, end) {
            let x = end[0] - start[0];
            let y = end[1] - start[1];
            return [x, y];
        };
    
        /**
     * 在曲线(多点折线, 按点顺序)上取长度为 len 的一点坐标, 包括点真实坐标和曲线点数组中仅次于该点的下标, 0 < k < 1;
     */
        calPntInCurveByLen = function (curvePnts, len) {
            let lenNodes = curvePnts.length;
            let P, index;
            let templen = 0, temp = 0;
            for (let j = 0; j < lenNodes - 1; j++) {
                temp = getLenOfTwoPnts(curvePnts[j], curvePnts[j + 1]);
                if (templen + temp > len) {
                    P = getPntInVct(curvePnts[j], vct(curvePnts[j], curvePnts[j + 1]), len - templen);
                    index = j;
                    break;
                }
                templen += temp;
            }
            if (!P) {
                return null;
            }
            return [P, index];
        };
    
    
        // 获取两个向量之间的夹角
        getRotateAng = function ([x1, y1], [x2, y2]) {
            let EPSILON = 1.0e-8;
            let dist, dot, cross, degree, angle;
    
            dist = Math.sqrt(x1 * x1 + y1 * y1);
            x1 /= dist;
            y1 /= dist;
            dist = Math.sqrt(x2 * x2 + y2 * y2);
            x2 /= dist;
            y2 /= dist;
    
            dot = x1 * x2 + y1 * y2;
            if (Math.abs(dot - 1.0) <= EPSILON) {
                angle = 0;
            } else if (Math.abs(dot + 1.0) <= EPSILON) {
                angle = Math.PI;
            } else {
                angle = Math.acos(dot);
                cross = x1 * y2 - x2 * y1;
                if (cross < 0) {
                    angle = 2 * Math.PI - angle;
                }
            }
            degree = angle * 180 / Math.PI;
            return degree;
        };
    
        /**
    * 创建向量 (从起点到终点的一个向量)
    */
        function vct([x1, y1], [x2, y2]) {
            let x = x2 - x1;
            let y = y2 - y1;
            return [x, y];
        };
    
        var len = 0;
        ctx.textBaseline = 'middle'  // 一定要设置,不然高度平移不一致
        function draw() {
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
            let points = getPntsOf3Bezier(p1, c1, c2, p2, 100);
            points.forEach((p, i) => {
                if (i == 0) {
                    ctx.moveTo(p[0], p[1]);
                } else {
                    ctx.lineTo(p[0], p[1]);
                }
            })
            ctx.stroke();
    
            ctx.beginPath();
            ctx.fillStyle = "blue";
            ctx.arc(c1[0], c1[1], 5, 0, 2 * Math.PI);
            ctx.fill();
            ctx.fillText("控制点1", c1[0], c1[1]);
    
            ctx.beginPath();
            ctx.fillStyle = "blue";
            ctx.arc(c2[0], c2[1], 5, 0, 2 * Math.PI);
            ctx.fill();
            ctx.fillText("控制点2", c2[0], c2[1]);
    
            ctx.beginPath();
            let gpIndex = calPntInCurveByLen(points, len)[1];
            let ni = gpIndex + 1;
            let gp = calPntInCurveByLen(points, len)[0];
            let vct1 = vct(gp, points[ni]);
            let vct2 = vct(gp, [gp[0] + 1000, gp[1]]);    // 或这直接写 [任意值, 0]也是与X轴平行的向量
            let angle = getRotateAng(vct2, vct1);
            ctx.save();
            ctx.font = `30px Arial`
            ctx.translate(gp[0], gp[1]); //设置画布上的(0,0)位置,也就是旋转的中心点
            ctx.rotate(angle * Math.PI / 180);
            ctx.fillStyle = "#006d75";
            ctx.fillText("测", 0, -20);
            ctx.restore();
        }
        // draw()
        setInterval(draw, 0);
    
        window.addEventListener("keydown", (e) => {
            if (e.keyCode == 38) {
                len += 5;
            }
            if (e.keyCode == 40) {
                len -= 5;
            }
            console.log(len);
        })
    
    </script>
    <style>
    </style> 作者:__SomeBody https://www.bilibili.com/read/cv24251081 出处:bilibili
    

    相关文章

      网友评论

          本文标题:canvas 文字沿曲线排列

          本文链接:https://www.haomeiwen.com/subject/yfzaedtx.html