1、vue实现动态绘制贝塞尔曲线

  • 效果图:

如何用python绘制三阶贝塞尔曲线 贝塞尔曲线代码实现_如何用python绘制三阶贝塞尔曲线

  • index.vue界面代码
<template>
    <body>
        <div>
            <el-button type="primary" @click="readJson()">读取JSON</el-button>
        </div>
        <div class="canvasBox">
            <v-stage ref="stage" :config="{width: 1000, height: 600}">
                <v-layer ref="layer">
                    <v-shape v-for="(el,index) in shapes" :config="el" :key="index + 'shape'"/> <!--绘制曲线-->
                    <v-line v-for="(item,index) in lines" :config="item" :key="index + 'line'"/><!--绘制辅助线-->
                    <v-circle @dragmove="dragmove(el)" @mouseout="mouseout(el)" @mouseover="mouseover(el)" v-for="(el,index) in anchors" :config="el" :key="index+ 'circle'"/> <!--绘制实际路径-->
                </v-layer>
            </v-stage>
        </div>
        
    </body>
</template>

<script>
    export default {
        name: "konva",
        data() {
            return {
                liness:[ //定义曲线
                    { id:1, key:"PointLine04422", lineId:"4422", strokeWidth:2, stroke:'#2973FF' },
                    { id:2, key:"PointLine14422", lineId:"44122", strokeWidth:2, stroke:'#2973FF' },
                    { id:3, key:"PointLine24422", lineId:"44222", strokeWidth:2, stroke:'#2973FF' }],
                locations:[ //定义点位
                    { id:1, order:1, x:50, y:50, index:1, line:1, link:0, dot:0, key:"1", remarks:"", },
                    { id:2, order:2, x:50, y:300, index:2, line:1, link:0, dot:1, key:"2", remarks:"" },
                    { id:3, order:3, x:212, y:256, index:3, line:1, link:0, dot:1, key:"3", remarks:"" },
                    { id:4, order:4, x:350, y:160, index:4, line:1, link:1, dot:2, key:"4", remarks:"" },
                    { id:5, order:4, x:350, y:160, index:1, line:2, link:1, dot:2, key:"4", remarks:"" },
                    { id:6, order:5, x:450, y:100, index:2, line:2, link:0, dot:1, key:"5", remarks:"" },
                    { id:7, order:6, x:600, y:100, index:3, line:2, link:0, dot:1, key:"6", remarks:"" },
                    { id:8, order:7, x:455, y:366, index:4, line:2, link:1, dot:2, key:"7", remarks:"" },
                    { id:9, order:7, x:455, y:366, index:1, line:3, link:1, dot:2, key:"7", remarks:"" },
                    { id:10, order:8, x:564, y:243, index:2, line:3, link:0, dot:1, key:"8", remarks:"" },
                    { id:11, order:9, x:650, y:300, index:3, line:3, link:0, dot:1, key:"9", remarks:"" },
                    { id:12, order:10, x:800, y:400, index:4, line:3, link:0, dot:0, key:"10", remarks:"" }],
                anchors: [],
                shapes: [],
                points:[],
                lines: [],
                stageSize: {
                    width: 1000,
                    height: 600
                }
            }
        },
        created(){
            this.creatPoint_n(); //界面加载时执行
        },
        methods: {
            orderby(x,y){ //排序(升序ASC)
                return x.order-y.order
            },
            readJson() //此方法用于读取点位移动变化后的json值
            {
                this.$message({
                message: JSON.stringify(this.locations),
                type: "success"
                });
                console.log(this.locations);
            },
            dragmove(el){ //移动光标时的方法
                this.updateDottedLines(); //辅助线跟随点动
            },
            mouseover(el){ //鼠标移动到此点位的事件方法
                document.body.style.cursor = 'pointer'; //鼠标小手指
                el.strokeWidth = 4; //鼠标停留在中间几个定位点的光圈大小
            },
            mouseout(el){ //鼠标离开时的方法
                document.body.style.cursor = 'default'; //普通鼠标光标
                el.strokeWidth = 2; //鼠标停留在起始点或者末尾点的光标状态
            },
            //根据入口,出口点位创建n个等间距的点位
            creatPoint_n(){
                let num =2;
                let line = {
                    endId:"22",
                    startId:"44",
                    linePoints:[[0,100],[1000,100]]
                    };
                //生成每段的辅助点位
                this.createAuxiliaryPoint()
            },
            createAuxiliaryPoint(){ //生产每段的辅助点位
                let lines = []; //辅助线集合
                let i=0,b=1;
                //生成点位 //"#Point"+i+"anchor1"+ startId
                var wholestory = this.locations.filter(function(item){ //始末点
                        return item.dot == 0;
                }).sort(this.orderby); //排序
                
                var auxiliary = this.locations.filter(function(item){ //辅助点
                    return item.dot == 1;
                }).sort(this.orderby); //排序

                var linking = this.locations.filter(function(item){ //连接点
                    return item.dot == 2;
                }).sort(this.orderby); //排序
                for (let i = 0; i < linking.length; i++) { //去除重复数据
                    for (let j = i + 1; j < linking.length;) {
                        if (linking[i].key == linking[j].key) {
                            linking.splice(j, 1)
                        } else j++
                    }
                }
                
                for (let i = 0; i < wholestory.length; i++) { //创建始末点
                    this.buildAnchor(wholestory[i].x, wholestory[i].y, wholestory[i].key, "A"+wholestory[i].line);
                }
                for (let i = 0; i < auxiliary.length; i++) { //创建辅助点
                    this.buildControl(auxiliary[i].x, auxiliary[i].y, auxiliary[i].key, "A"+auxiliary[i].line);
                }
                for (let i = 0; i < linking.length; i++) { //创建连接点
                    this.buildAnchorMerge(linking[i].x, linking[i].y, linking[i].key, "A"+linking[i].line);
                }

                //画贝塞尔曲线
                this.shapes.push({
                    stroke: '#f6d87e', //曲线颜色
                    strokeWidth: 2, //曲线宽度
                    lineId:"AAA", //曲线Id
                    sceneFunc: (ctx, shape) => {
                        let layer = this.$refs.layer.getNode();
                        ctx.beginPath();
                        //{ id:1, order:1, x:50, y:50, line:1, link:0, key:"Build"+0+"anchor" + "4422", remarks:"", },
                        ctx.moveTo(layer.findOne("#"+this.locations[0].key).x(), layer.findOne("#"+this.locations[0].key).y());

                        debugger;
                        for (let i = 0; i < this.liness.length; i++) { //创建始末点
                            let lineid=this.liness[i].id;
                            var locat= this.locations.filter(function(item){ //根据获取到的id得到集合
                                return item.line == lineid;
                            });
                            ctx.bezierCurveTo(
                                layer.findOne("#"+locat[1].key).x(), layer.findOne("#"+locat[1].key).y(),
                                layer.findOne("#"+locat[2].key).x(), layer.findOne("#"+locat[2].key).y(),
                                layer.findOne("#"+locat[3].key).x(), layer.findOne("#"+locat[3].key).y(),
                            );
                        }
                        ctx.fillStrokeShape(shape);
                        ctx.stroke();
                        ctx.closePath();
                    }
                });
                
                for (let i = 0; i < this.liness.length; i++) { //创建辅助线
                    this.lines.push({
                        dash: [10, 10, 0, 10], //虚线的样式
                        strokeWidth: 2, //虚线的宽度
                        stroke: '#2973FF', //虚线的颜色
                        lineCap: 'round',
                        id: this.liness[i].key,
                        lineId: this.liness[i].lineId,
                        points: [0, 0],
                    });
                }
                this.$nextTick(()=>{
                    this.updateDottedLines()
                })
            },
            updateDottedLines(){ //创建辅助线
                //let startId = line.startId+ "" + line.endId;
                //let endId = line.startId+ "1" + line.endId;
                let layer = this.$refs.layer.getNode();
                this.lines.map((item,i) => {
                    let bezierLinePath = layer.findOne("#" + item.id); //根据Id来获取这个贝塞尔曲线
                    var codeinfo = this.liness.find((element) => (element.key == item.id)); //根据key值获取id
                    var codeinfos= this.locations.filter(function(item){ //根据获取到的id得到集合
                        return item.line == codeinfo.id;
                    });
                    let point = [
                        layer.findOne("#"+codeinfos[0].key).x(), layer.findOne("#"+codeinfos[0].key).y(),
                        layer.findOne("#"+codeinfos[1].key).x(), layer.findOne("#"+codeinfos[1].key).y(),
                        layer.findOne("#"+codeinfos[2].key).x(), layer.findOne("#"+codeinfos[2].key).y(),
                        layer.findOne("#"+codeinfos[3].key).x(), layer.findOne("#"+codeinfos[3].key).y(),
                    ];
                    bezierLinePath.points(point); //绘制1号辅助线
                    
                    for (let index = 0; index < this.locations.length; index++) { //赋值
                        this.locations[index].x = layer.findOne("#"+this.locations[index].key).x();
                        this.locations[index].y = layer.findOne("#"+this.locations[index].key).y();
                    }
                    /*
                    .catch(ex => {
                            this.$message({
                            message: ex.message,
                            type: "error"
                        });
                    });
                    */
                });
            },
 
            //创建轨迹出入口的点
            buildAnchor(x, y,id,lineId) {
                this.anchors.push({
                    x: x,
                    y: y,
                    id:id,
                    lineId:lineId,
                    radius: 5, //点的大小
                    stroke: '#b5d4ff', //点的环线颜色
                    fill: '#63d12b',
                    strokeWidth: 0,
                    draggable: true, //是否可以鼠标拖动
                });
            },
            //创建合并的点,即中间共用点位
            buildAnchorMerge(x, y,id,lineId) {
                this.anchors.push({
                    x: x,
                    y: y,
                    id:id,
                    lineId:lineId,
                    radius: 5,
                    stroke: 'green',
                    fill: 'green',
                    strokeWidth: 2,
                    draggable: true,
                });
            },
            //创建贝塞尔曲线辅助移动点位
            buildControl(x, y,id,lineId) {
                this.anchors.push({
                    x: x,
                    y: y,
                    id:id,
                    lineId:lineId,
                    radius: 5,
                    stroke: '#2973FF',
                    fill: '#2973FF',
                    strokeWidth: 2,
                    draggable: true,
                });
            },
        }
    }
</script>

<style scoped>
 
    html,body{
        width: 100%;
        height: 100%;
    }
 
    .canvasBox {
        width: 1000px;
        height: 600px;
        margin: 0 auto;
        border: 1px solid #ccc;
        background-size: 100% 100%;
        position: relative;
    }
</style>

3、参考示例,vue绘制连续有规律的曲线,可拖动。

  • 效果图:

如何用python绘制三阶贝塞尔曲线 贝塞尔曲线代码实现_贝塞尔曲线_02

  • index.vue源代码:
<template>
    <div class="canvasBox">
        <v-stage ref="stage" :config="{width: 1000, height: 600}">
            <v-layer ref="layer">
                <v-shape v-for="(el,index) in shapes" :config="el" :key="index + 'shape'"/>
                <v-line v-for="(item,index) in lines" :config="item" :key="index + 'line'"/>
                <v-circle @dragmove="dragmove(el)" @mouseout="mouseout(el)" @mouseover="mouseover(el)" v-for="(el,index) in anchors" :config="el" :key="index+ 'circle'" />
            </v-layer>
        </v-stage>
    </div>
</template>

<script>
    export default {
        name: "konva",
        data() {
            return {
                anchors: [],
                shapes: [],
                points:[],
                lines: [],
                stageSize: {
                    width: 1000,
                    height: 600
                }
            }
        },
        created(){
            this.creatPoint_n({endId:"22",startId:"44",linePoints:[[50,400],[800,400]]},5)
        },
        methods: {
            dragmove(el){
                this.updateDottedLines({endId:"22",startId:"44"},true);
            },
            mouseover(el){
                document.body.style.cursor = 'pointer';
                el.strokeWidth = 4;
            },
            mouseout(el){
                document.body.style.cursor = 'default';
                el.strokeWidth = 2;
            },
            //根据入口,出口点位创建n个等间距的点位
            creatPoint_n(line,num){
                let start = line.linePoints[0];
                let end = line.linePoints[1];
                const pointNum = num;
                if(!num){num = 2}
                let range_row = parseInt((end[0] - start[0])/(num-1));
                let range_col = parseInt((end[1] - start[1])/(num-1));
                while (num > 2) {
                    num--;
                    let newPoint = [start[0] + range_row*(pointNum - num),start[1] + range_col*(pointNum - num)];
                    line.linePoints.splice(line.linePoints.length - 1,0,newPoint);
                }
 
                //生成每段的辅助点位
                this.createAuxiliaryPoint(line)
            },
            createAuxiliaryPoint(line){
                let startId = line.startId+ "" + line.endId;
                let _this = this,lines = [];
                for (let i=0;i<line.linePoints.length-1;i++){
                    let point = line.linePoints[i];
                    let point_next = line.linePoints[i+1];
                    let center = [(point[0] + point_next[0])/2,(point[1] + point_next[1])/2];
                    //生成点位
                    if(i === 0){
                        this.buildAnchor(point[0],point[1],"Build"+i+"anchor" + startId,startId);
                    }
                    if(i === line.linePoints.length-2){
                        this.buildAnchor(point_next[0],point_next[1],"Build"+i+"anchor_end"+ startId,startId);
                    }else{
                        this.buildAnchorMerge(point_next[0],point_next[1],"Build"+i+"anchor_end"+ startId,startId);
                    }
                    this.buildControl(center[0]-20,center[1]-30,"Point"+i+"anchor1"+ startId,startId);
                    this.buildControl(center[0]+20,center[1] + 30,"Point"+i+"anchor2"+ startId,startId);
                    //画贝塞尔曲线
                    this.shapes.push({
                        stroke: 'red',
                        strokeWidth: 2,
                        lineId:startId,
                        sceneFunc: (ctx, shape) => {
                            let layer = _this.$refs.layer.getNode();
                            ctx.beginPath();
                            if(i === 0){
                                ctx.moveTo(layer.findOne("#Build"+i+"anchor"+ startId).x(), layer.findOne("#Build"+i+"anchor"+ startId).y());
                            }else{
                                ctx.moveTo(layer.findOne("#Build"+(i-1)+"anchor_end"+ startId).x(), layer.findOne("#Build"+(i-1)+"anchor_end"+ startId).y());
                            }
                            ctx.bezierCurveTo(
                                layer.findOne("#Point"+i+"anchor1"+ startId).x(),
                                layer.findOne("#Point"+i+"anchor1"+ startId).y(),
                                layer.findOne("#Point"+i+"anchor2"+ startId).x(),
                                layer.findOne("#Point"+i+"anchor2"+ startId).y(),
                                layer.findOne("#Build"+i+"anchor_end"+ startId).x(),
                                layer.findOne("#Build"+i+"anchor_end"+ startId).y(),
                            );
                            ctx.fillStrokeShape(shape);
                        }
                    });
                    //创建辅助线
                    this.lines.push({
                        dash: [10, 10, 0, 10],
                        strokeWidth: 2,
                        stroke: '#2973FF',
                        lineCap: 'round',
                        id: 'PointLine' + i + "" + startId,
                        lineId:startId,
                        points: [0, 0],
                    });
                }
                this.$nextTick(()=>{
                    this.updateDottedLines(line)
                })
            },
            updateDottedLines(line,flag){
                let startId = line.startId+ "" + line.endId;
                let layer = this.$refs.layer.getNode();
                let startIndex = -1;
                this.lines.map((item,i) => {
                    if( startIndex === -1){startIndex = i}
                    let bezierLinePath = layer.findOne("#" + item.id);
                    let start = [];
                    let index = i-startIndex;
                    if(index === 0){
                        start.push(layer.findOne("#Build"+ index +"anchor"+ startId).x());
                        start.push(layer.findOne("#Build"+ index +"anchor"+ startId).y())
                    }else{
                        start.push(layer.findOne("#Build"+ (index-1) +"anchor_end"+ startId).x());
                        start.push(layer.findOne("#Build"+ (index-1) +"anchor_end"+ startId).y())
                    }
                    let point = [
                        start[0],start[1],
                        layer.findOne("#Point"+index+"anchor1"+ startId).x(),
                        layer.findOne("#Point"+index+"anchor1"+ startId).y(),
                        layer.findOne("#Point"+index+"anchor2"+ startId).x(),
                        layer.findOne("#Point"+index+"anchor2"+ startId).y(),
                        layer.findOne("#Build"+index+"anchor_end"+ startId).x(),
                        layer.findOne("#Build"+index+"anchor_end"+ startId).y(),
                    ];
                    bezierLinePath.points(point);
                });
            },
 
            //创建轨迹出入口的点
            buildAnchor(x, y,id,lineId) {
                this.anchors.push({
                    x: x,
                    y: y,
                    id:id,
                    lineId:lineId,
                    radius: 5,
                    stroke: '#b5d4ff',
                    fill: '#388aff',
                    strokeWidth: 0,
                    draggable: true,
                });
            },
            //创建合并的点,即中间共用点位
            buildAnchorMerge(x, y,id,lineId) {
                this.anchors.push({
                    x: x,
                    y: y,
                    id:id,
                    lineId:lineId,
                    radius: 5,
                    stroke: 'green',
                    fill: 'green',
                    strokeWidth: 2,
                    draggable: true,
                });
            },
            //创建贝塞尔曲线辅助移动点位
            buildControl(x, y,id,lineId) {
                this.anchors.push({
                    x: x,
                    y: y,
                    id:id,
                    lineId:lineId,
                    radius: 5,
                    stroke: '#2973FF',
                    fill: '#2973FF',
                    strokeWidth: 2,
                    draggable: true,
                });
            },
        }
    }
</script>

<style scoped>
 
    html,body{
        width: 100%;
        height: 100%;
    }
 
    .canvasBox {
        width: 1000px;
        height: 600px;
        margin: 0 auto;
        border: 1px solid #ccc;
        background-size: 100% 100%;
        position: relative;
    }
</style>

4、html实现多阶贝塞尔曲线,先看一下效果图,本小段结尾处有原文件链接

如何用python绘制三阶贝塞尔曲线 贝塞尔曲线代码实现_连接点_03


5、贝塞尔曲线路径算法,js贝塞尔曲线路径点【】