美文网首页
HTML5 Canvas笔记——编写可编辑和拖动的贝塞尔曲线

HTML5 Canvas笔记——编写可编辑和拖动的贝塞尔曲线

作者: 没昔 | 来源:发表于2020-05-19 21:35 被阅读0次

    HTML5 Canvas编写拖动和编辑图形的程序——贝塞尔曲线

    2-28.js

    var canvas = document.getElementById("canvas"),
        context = canvas.getContext("2d"),
        eraseAllButton = document.getElementById("eraseAllButton"),
        strokeStyleSelect = document.getElementById("strokeStyleSelect"),
        fillStyleSelect = document.getElementById("fillStyleSelect"),
        fillCheckbox = document.getElementById("fillCheckbox"),
        editCheckbox = document.getElementById('editCheckbox'),
        sidesSelect = document.getElementById('sidesSelect'),
     
        mousedown = {},
        rubberbandRect = {},
     
        dragging = false,
        draggingOffsetX,
        draggingOffsetY,
     
        sides = 8,
        startAngle = 0,
     
        guidewires = true,
        editing = false,
        polygons = [];
    
     
    //Function..................................................................
    
    function windowToCanvas(x, y) {
        var bbox = canvas.getBoundingClientRect();
        return { x: x - bbox.left * (canvas.width / bbox.width),
                 y: y - bbox.top  * (canvas.height / bbox.height)
               };
    }
     
    //Save and restore drawing surface..........................................
     
    function saveDrawingSurface() {
        drawingSurfaceImageData = context.getImageData(0, 0, canvas.width,
                                                       canvas.height);
    }
     
    function restoreDrawingSurface() {
        context.putImageData (drawingSurfaceImageData, 0, 0);
    }
     
    //Draw a polygon...........................................................
     
    function drawPolygon(polygon) {
        context.beginPath();
        polygon.createPath(context);
        polygon.stroke(context);
     
        if (fillCheckbox.checked) {
            polygon.fill(context);
        }
    }
     
    //Rubber bands.............................................................
     
    function updateRubberbandRectangle(loc) {
        rubberbandRect.width = Math.abs(loc.x - mousedown.x);
        rubberbandRect.height = Math.abs(loc.y - mousedown.y);
     
        if ( loc.x > mousedown.x) rubberbandRect.left = mousedown.x;
        else                      rubberbandRect.left = loc.x;
     
        if (loc.y > mousedown.y) rubberbandRect.top = mousedown.y;
        else                     rubberbandRect.top = loc.y;
    }
     
    function drawRubberbandShape (loc, sides, startAngle) {
        var polygon = new Polygon(mousedown.x, mousedown.y,
                                  rubberbandRect.width,
                                  parseInt(sidesSelect.value),
                                (Math.PI/180)* parseInt(startAngleSelect.value),
                                  context.strokeStyle,
                                  context.fillStyle,
                                  fillCheckbox.checked);
        drawPolygon(polygon);
     
        if(!dragging) {
            polygons.push(polygon);
        }
    }
     
    function updateRubberband(loc, sides, startAngle) {
        updateRubberbandRectangle(loc);
        drawRubberbandShape(loc, sides, startAngle);
    }
    
    //Guidewires...............................................................
     
    function drawHorizontalLine (y) {
        context.beginPath();
        context.moveTo(0, y+0.5);
        context.lineTo(context.canvas.width, y+0.5);
        context.stroke();
    }
     
    function drawVerticalLine (x) {
        context.beginPath();
        context.moveTo(x+0.5, 0);
        context.lineTo(x+0.5, context.canvas.height);
        context.stroke();
    }
     
    function drawGuidewires(x, y) {
        context.save();
        context.strokeStyle = 'rgba(0,0,230,0.4)';
        context.lineWidth = 0.5;
        drawVerticalLine(x);
        drawHorizontalLine(y);
        context.restore();
    }
     
    function drawPolygons() {
        polygons.forEach(function (polygon) {
            drawPolygon(polygon);
        });
    }
     
    //Dragging..............................................................
     
    function startDragging (loc) {
        saveDrawingSurface();
        mousedown.x = loc.x;
        mousedown.y = loc.y;
    }
     
    function startEditing() {
        canvas.style.cursor = "pointer";
        editing = true;
    }
     
    function stopEditing() {
        canvas.style.cursor = "crosshair";
        editing = false;
    }
     
    //Event handlers...........................................................
     
    canvas.onmousedown = function (e) {
        var loc = windowToCanvas(e.clientX, e.clientY);
     
        e.preventDefault();
     
        if (editing) {
            polygons.forEach( function (polygon) {
                polygon.createPath(context);
                if (context.isPointInPath(loc.x, loc.y)){
                    startDragging(loc);
                    dragging = polygon;
                    draggingOffsetX = loc.x - polygon.x;
                    draggingOffsetY = loc.y - polygon.y;
                    return;
                }
            });
        }
        else {
            startDragging(loc);
            dragging = true;
        }
    };
     
    canvas.onmousemove = function (e) {
        var loc = windowToCanvas(e.clientX, e.clientY);
     
        e.preventDefault();
     
        if (editing && dragging) {
            dragging.x = loc.x - draggingOffsetX;
            dragging.y = loc.y - draggingOffsetY;
            context.clearRect(0, 0, canvas.width,canvas.height);
            drawPolygons();
        }
        else {
            if(dragging) {
                restoreDrawingSurface();
                updateRubberband(loc, sides, startAngle);
     
                if (guidewires) {
                    drawGuidewires(mousedown.x, mousedown.y);
                }
            }
        }
    };
     
    canvas.onmouseup = function (e) {
        var loc = windowToCanvas(e.clientX, e.clientY);
     
        dragging = false;
     
        if (editing) {
     
        }
        else {
            restoreDrawingSurface();
            updateRubberband(loc);
        }
    };
     
    eraseAllButton.onclick = function (e) {
        context.clearRect (0, 0, canvas.width, canvas.height);
        saveDrawingSurface();
    };
     
    strokeStyleSelect.onchange = function (e) {
        context.strokeStyle = strokeStyleSelect.value;
    };
     
    fillStyleSelect.onchange = function (e) {
        context.fillStyle = fillStyleSelect.value;
    };
     
    editCheckbox.onchange = function (e) {
        if (editCheckbox.checked){
            startEditing();
        }
        else {
            stopEditing();
        }
    };
    
     
    //Initialization............................................................
    
    context.strokeStyle = strokeStyleSelect.value;
    context.fillStyle = fillStyleSelect.value;
     
    context.shadowColor = 'rgba(0,0,0,0.4)';
    context.shadowOffsetX = 2;
    context.shadowOffsetY = 2;
    context.shadowBlur = 4;
    
    

    2-29.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>可编辑的贝塞尔曲线</title>
        <style>
            body {
                background: #eeeeee;
            }
    
            .floatingControls {
                position: absolute;
                left: 150px;
                top: 100px;
                width: 300px;
                padding: 20px;
                border: thin solid rgba(0, 0, 0, 0.3);
                background: rgba(0, 0, 200, 0.1);
                color: blue;
                font: 14px Arial;
                -webkit-box-shadow: rgba(0, 0, 0, 0.2) 6px 6px 8px;
                -moz-box-shadow: rgba(0, 0, 0, 0.2) 6px 6px 8px;
                box-shadow: rgba(0, 0, 0, 0.2) 6px 6px 8px;
                display: none;
            }
    
            .floatingControls p {
                margin-top: 0px;
                margin-bottom: 20px;
            }
    
    
            #controls {
                position: absolute;
                left: 25px;
                top: 25px;
            }
    
            #canvas {
                background: #ffffff;
                cursor: pointer;
                margin-left: 10px;
                margin-top: 10px;
                -webkit-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
                -moz-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
                box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
            }
        </style>
    </head>
    <body>
    <canvas id="canvas" width="605" height="400">Canvas not supported</canvas>
    <div id='controls'>
        Stroke color: <select id='strokeStyleSelect'>
        <option value='red'>red</option>
        <option value='green'>green</option>
        <option value='blue'>blue</option>
        <option value='orange'>orange</option>
        <option value='cornflowerblue'>cornflowerblue</option>
        <option value='goldenrod'>goldenrod</option>
        <option value='navy' selected>navy</option>
        <option value='purple'>purple</option>
    </select>
        Guidewires: <input id='guidewireCheckbox' type='checkbox' checked/>
        <input id='eraseAllButton' type='button' value='Erase all'/>
    </div>
    <div id='instructions' class='floatingControls'>
        <p>拖动贝塞尔曲线的锚点和控制点以改变曲线的形状</p>
    
        <p>当你完成曲线形状的编辑后,点击曲线外的一点,以完成此图像</p>
    
        <input id='instructionsOkayButton' type='button' value='好的' autofocus/>
        <input id='instructionsNoMoreButton' type='button'
               value='不再提示'/>
               
    </div>
    <script src="2-30.js"></script>
    </body>
    </html>
    

    2-30.js

    var canvas = document.getElementById('canvas'),
        context = canvas.getContext('2d'),
        eraseAllButton = document.getElementById('eraseAllButton'),
        strokeStyleSelect = document.getElementById('strokeStyleSelect'),
        guidewireCheckbox = document.getElementById('guidewireCheckbox'),
        instructions = document.getElementById('instructions'),
        instructionsOkayButton = document.getElementById('instructionsOkayButton'),
        instructionsNoMoreButton = document.getElementById('instructionsNoMoreButton'),
    
        showInstructions = true,
    
        GRID_STROKE_STYLE = 'lightblue',
        GRID_SPACING = 10,
    
        CONTROL_POINT_RADIUS = 5,
        CONTROL_POINT_STROKE_STYLE = 'blue',
        CONTROL_POINT_FILL_STYLE = 'rgba(255, 255, 0, 0.5)',
    
        END_POINT_STROKE_STYLE = 'navy',
        END_POINT_FILL_STYLE = 'rgba(0, 255, 0, 0.5)',
    
        GUIDEWIRE_STROKE_STYLE = 'rgba(0,0,230,0.4)',
    
        drawingImageData,
    
        mousedown = {},
        rubberbandRect = {},
    
        dragging = false,
        draggingPoint = false,
    
        endPoints = [{}, {}],
        controlPoints = [{}, {}],
        editing = false,
    
        guidewires = guidewireCheckbox.checked;
    
    //Function……
    
    function drawGrid(color, stepX, stepY){
        context.save()
    
        context.strokeStyle = color;
        context.lineWidth = 0.5;
        context.clearRect(0, 0, context.canvas.width, context.canvas.height);
    
        for (var i = stepX + 0.5; i < context.canvas.width; i += stepX) {
            context.beginPath();
            context.moveTo(i, 0);
            context.lineTo(i, context.canvas.height);
            context.stroke();
        }
    
        for (var i = stepY + 0.5; i < context.canvas.height; i += stepY) {
            context.beginPath();
            context.moveTo(0, i);
            context.lineTo(context.canvas.width, i);
            context.stroke();
        }
    
        context.restore();
    };
    
    function updateRubberbandRectangle(loc){
        rubberbandRect.width  = Math.abs(loc.x - mousedown.x);
        rubberbandRect.height = Math.abs(loc.y - mousedown.y);
    
        if (loc.x > mousedown.x) rubberbandRect.left = mousedown.x;
        else                     rubberbandRect.left = loc.x;
    
        if (loc.y > mousedown.y) rubberbandRect.top = mousedown.y;
        else                     rubberbandRect.top = loc.y;
    };
    
    
    function windowToCanvas(x, y){
        var bbox = canvas.getBoundingClientRect();
    
        return {
            x: x - bbox.left * (canvas.width / bbox.width),
            y: y - bbox.top * (canvas.height / bbox.height)
        };
    };
    
    //保存和恢复绘图表面
    
    function saveDrawingSurface(){
        drawingImageData = context.getImageData(0, 0,
            canvas.width, canvas.height);
    };
    
    function restoreDrawingSurface(){
        context.putImageData(drawingImageData, 0, 0);
    };
    
    /**
     * 绘制贝塞尔曲线
     */
    function drawBezierCurve (){
        context.beginPath();
        context.moveTo(endPoints[0].x, endPoints[0].y);
        context.bezierCurveTo(controlPoints[0].x, controlPoints[0].y,
            controlPoints[1].x, controlPoints[1].y,
            endPoints[1].x, endPoints[1].y);
        context.stroke();
    };
    
    function updateEndAndControlPoints(){
        endPoints[0].x = rubberbandRect.left;
        endPoints[0].y = rubberbandRect.top;
    
        endPoints[1].x = rubberbandRect.left + rubberbandRect.width;
        endPoints[1].y = rubberbandRect.top  + rubberbandRect.height
    
        controlPoints[0].x = rubberbandRect.left;
        controlPoints[0].y = rubberbandRect.top  + rubberbandRect.height
    
        controlPoints[1].x = rubberbandRect.left + rubberbandRect.width;
        controlPoints[1].y = rubberbandRect.top;
    };
    
    function drawRubberbandShape(loc){
        updateEndAndControlPoints();
        drawBezierCurve();
    };
    
    function updateRubberband(loc){
        updateRubberbandRectangle(loc);
        drawRubberbandShape(loc);
    };
    
    //辅助线
    
    function drawHorizontalGuidewire(y){
        context.beginPath();
        context.moveTo(0, y + 0.5);
        context.lineTo(context.canvas.width, y + 0.5);
        context.stroke();
    };
    
    function drawVerticalGuidewire(x){
        context.beginPath();
        context.moveTo(x + 0.5, 0);
        context.lineTo(x + 0.5, context.canvas.height);
        context.stroke();
    };
    
    function drawGuidewires(x, y){
        context.save();
        context.strokeStyle = GUIDEWIRE_STROKE_STYLE;
        context.lineWidth = 0.5;
        drawVerticalGuidewire(x);
        drawHorizontalGuidewire(y);
        context.restore();
    };
    
    //绘制锚点和控制点
    
    function drawControlPoint(index){
        context.beginPath();
        context.arc(controlPoints[index].x, controlPoints[index].y,
            CONTROL_POINT_RADIUS, 0, Math.PI * 2, false);
        context.stroke();
        context.fill();
    };
    
    function drawControlPoints(){
        context.save();
        context.strokeStyle = CONTROL_POINT_STROKE_STYLE;
        context.fillStyle = CONTROL_POINT_FILL_STYLE;
    
        drawControlPoint(0);
        drawControlPoint(1);
    
        context.stroke();
        context.fill();
        context.restore();
    };
    
    function drawEndPoint(index){
        context.beginPath();
        context.arc(endPoints[index].x, endPoints[index].y,
            CONTROL_POINT_RADIUS, 0, Math.PI * 2, false);
        context.stroke();
        context.fill();
    };
    
    function drawEndPoints(){
        context.save();
        context.strokeStyle = END_POINT_STROKE_STYLE;
        context.fillStyle = END_POINT_FILL_STYLE;
    
        drawEndPoint(0);
        drawEndPoint(1);
    
        context.stroke();
        context.fill();
        context.restore();
    };
    
    function drawControlAndEndPoints(){
        drawControlPoints();
        drawEndPoints();
    };
    
    function cursorInEndPoint(loc){
        var pt;
    
        endPoints.forEach(function (point) {
            context.beginPath();
            context.arc(point.x, point.y,
                CONTROL_POINT_RADIUS, 0, Math.PI * 2, false);
    
            if (context.isPointInPath(loc.x, loc.y)) {
                pt = point;
            }
        });
    
        return pt;
    };
    
    function cursorInControlPoint(loc){
        var pt;
    
        controlPoints.forEach(function (point) {
            context.beginPath();
            context.arc(point.x, point.y,
                CONTROL_POINT_RADIUS, 0, Math.PI * 2, false);
    
            if (context.isPointInPath(loc.x, loc.y)) {
                pt = point;
            }
        });
    
        return pt;
    };
    
    function updateDraggingPoint(loc){
        draggingPoint.x = loc.x;
        draggingPoint.y = loc.y;
    };
    
    //事件
    
    
    canvas.onmousedown = function(e){
        //获取当前位置
        var loc = windowToCanvas(e.clientX,e.clientY);
        e.preventDefault();
    
        if (!editing){
    
            saveDrawingSurface();
            mousedown.x =loc.x;
            mousedown.y = loc.y;
            updateRubberbandRectangle(loc);
            dragging=true;
        }
        else{
            draggingPoint = cursorInControlPoint(loc);
            if (!draggingPoint){
    
                draggingPoint=cursorInEndPoint(loc);
            }
        }
    };
    canvas.onmousemove=function(e){
    
        var loc = windowToCanvas(e.clientX,e.clientY);
        if (dragging||draggingPoint){
            e.preventDefault();
            restoreDrawingSurface();
            if (guidewires){
                drawGuidewires(loc.x,loc.y);
            }
        }
        if (dragging) {
            updateRubberband(loc);
            drawControlAndEndPoints();
        }
        else if (draggingPoint) {
            updateDraggingPoint(loc);
            drawControlAndEndPoints();
            drawBezierCurve();
        }
    };
    
    canvas.onmouseup=function(e){
        var loc = windowToCanvas(e.clientX,e.clientY);
    
        restoreDrawingSurface();
    
        if (!editing) {
            updateRubberband(loc);
            drawControlAndEndPoints();
            dragging = false;
            editing = true;
            if (showInstructions) {
                instructions.style.display = 'inline';
            }
        }
        else {
            if (draggingPoint) drawControlAndEndPoints();
            else editing = false;
    
            drawBezierCurve();
            draggingPoint = undefined;
        }
    }
    
    // 给控件添加监听
    
    eraseAllButton.onclick =function() {
        context.clearRect(0, 0, canvas.width, canvas.height);
        drawGrid(GRID_STROKE_STYLE, GRID_SPACING, GRID_SPACING);
    
        saveDrawingSurface();
    
        editing = false;
        dragging = false;
        draggingPoint = undefined;
    };
    
    strokeStyleSelect.onchange = function() {
        context.strokeStyle = strokeStyleSelect.value;
    };
    
    guidewireCheckbox.onchange = function() {
        guidewires = guidewireCheckbox.checked;
    };
    
    // 给介绍框添加监听
    
    instructionsOkayButton.onclick = function() {
        instructions.style.display = 'none';
    };
    
    instructionsNoMoreButton.onclick =function() {
        instructions.style.display = 'none';
        showInstructions = false;
    };
    
    // 初始化
    // 
    
    context.strokeStyle = strokeStyleSelect.value;
    drawGrid(GRID_STROKE_STYLE, GRID_SPACING, GRID_SPACING);
    

    效果如图:


    贝塞尔曲线

    相关文章

      网友评论

          本文标题:HTML5 Canvas笔记——编写可编辑和拖动的贝塞尔曲线

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