【问题标题】:HTML5 Canvas Scallop ShapeHTML5 帆布扇贝形状
【发布时间】:2017-02-25 01:55:58
【问题描述】:

关于如何使用 Canvas 创建扇贝形状,我真的非常需要帮助 我尝试过使用云示例,但创建我想要的东西真的很困难。

我只是想知道矩形和圆形扇贝形状的代码。

这就是我想要的图像。

它的设计不必完全相同,但尽可能看起来像这样。

非常感谢您。

【问题讨论】:

    标签: javascript jquery html canvas


    【解决方案1】:

    你可以用虚线划出这样的形状,像这样(有点棘手)。

    JavaScript:

    const canvas = document.querySelector("#canvas");
    canvas.width = canvas.height = 300;
    const ctx = canvas.getContext("2d");
    const rect = [50, 50, 200, 200];
    //draw dotted line dash.
    ctx.lineCap = "round";
    ctx.setLineDash([0, 40]);
    ctx.lineDashOffset = 20;
    ctx.lineWidth = 42;
    ctx.strokeStyle = "purple";
    ctx.strokeRect(...rect);
    //remove disuse range.
    ctx.globalCompositeOperation = "destination-out";
    ctx.lineWidth = 38;
    ctx.strokeRect(...rect);
    ctx.fillRect(...rect);
    

    演示:
    http://jsdo.it/defghi1977/iFR7

    【讨论】:

    • 为什么不用二次曲线?
    • 在这种情况下,我的代码不需要二次曲线。但是使用二次曲线更通用,因为我的代码很难操作笔画宽度轮廓的形状。
    【解决方案2】:

    来自较旧的答案,但问题非常模糊,并且有很多额外的包袱。这是该答案的一个sn-p。它有一些额外的代码,可能有用但不直接相关。

    功能显示(大约一半)完成了大部分工作,将弧线添加到对象框。

    请参阅运行演示以获取说明

    const pointSize = 4;
    const pointCol = "#4AF";
    var arcDepth = -0.5; // depth of arc as a factor of line seg length
                           // Note to have arc go the other (positive) way you have
                           // to change the ctx.arc draw call by adding anticlockwise flag 
                           // see drawArc for more
                           
    const arcCol = "#F92";
    const arcWidth = 8;
    
    
    // Find a circle that fits 3 points.
    function fitCircleTo3P(p1x, p1y, p2x, p2y, p3x, p3y, arc) {
        var vx,
        vy,
        c,
        c1,
        u;
    
        c = (p2x - p1x) / (p1y - p2y); // slope of vector from vec 1 to vec 2
        c1 = (p3x - p2x) / (p2y - p3y); // slope of vector from vec 2 to vec 3
        // This will not happen in this example
        if (c === c1) { // if slope is the same they must be on the same line
            return null; // points are in a line
        }
        // locate the center
        if (p1y === p2y) { // special case with p1 and p2 have same y
            vx = (p1x + p2x) / 2;
            vy = c1 * vx + (((p2y + p3y) / 2) - c1 * ((p2x + p3x) / 2));
        } else
            if (p2y === p3y) { // special case with p2 and p3 have same y
                vx = (p2x + p3x) / 2;
                vy = c * vx + (((p1y + p2y) / 2) - c * ((p1x + p2x) / 2));
            } else {
                vx = ((((p2y + p3y) / 2) - c1 * ((p2x + p3x) / 2)) - (u = ((p1y + p2y) / 2) - c * ((p1x + p2x) / 2))) / (c - c1);
                vy = c * vx + u;
            }
        arc.x = vx;
        arc.y = vy;
        vx = p1x - vx;
        vy = p1y - vy;
        arc.rad = Math.sqrt(vx * vx + vy * vy);
        return arc;
    }
    
    var points = [];
    var arcs = [];
    function addArc(p1, p2, depth) {
        var arc = {
            p1 : p1,
            p2 : p2,
            depth : depth,
            rad : null, // radius
            a1 : null, // angle from
            a2 : null, // angle to
            x : null,
            y : null,
        }
        arcs.push(arc);
        return arc;
    }
    function calcArc(arc, depth) {
        var p = points[arc.p1]; // get points
        var pp = points[arc.p2];
        // change depth if needed
        depth = arc.depth = depth !== undefined ? depth : arc.depth;
        var vx = pp[0] - p[0]; // vector from p to pp
        var vy = pp[1] - p[1];
        var cx = (pp[0] + p[0]) / 2; // center point
        var cy = (pp[1] + p[1]) / 2; // center point
        var len = Math.sqrt(vx * vx + vy * vy); // get length
        cx -= vy * depth; // find 3 point at 90 deg to line and dist depth
        cy += vx * depth;
    
        // To have depth as a fixed length uncomment 4 lines below and comment out 2 lines above.
        //var nx = vx / len;  // normalise vector
        //var ny = vy / len;
        //cx -= ny * depth; // find 3 point at 90 deg to line and dist depth
        //cy += nx * depth;
    
    
        fitCircleTo3P(p[0], p[1], cx, cy, pp[0], pp[1], arc); // get the circle that fits
        arc.a1 = Math.atan2(p[1] - arc.y, p[0] - arc.x); // get angle from circle center to first point
        arc.a2 = Math.atan2(pp[1] - arc.y, pp[0] - arc.x); // get angle from circle center to second point
    
    }
    function addPoint(x, y) {
        points.push([x, y]);
    }
    function drawPoint(x, y, size, col) {
        ctx.fillStyle = col;
        ctx.beginPath();
        ctx.arc(x, y, size, 0, Math.PI * 2);
        ctx.fill();
    }
    function drawArcStart(width,col){
        ctx.lineCap = "round";
        ctx.strokeStyle = col;
        ctx.lineJoin = "round";
        ctx.lineWidth = width;
        ctx.beginPath();
        
    }
    function drawArc(arc){
        ctx.arc(arc.x,arc.y,arc.rad,arc.a1,arc.a2);
    }    
    function drawArcDone(){
        ctx.closePath();
        ctx.stroke();
    }
    function findClosestPoint(x, y, dist) {
        var index = -1;
        for (var i = 0; i < points.length; i++) {
            var p = points[i];
            var vx = x - p[0];
            var vy = y - p[1];
            var d = Math.sqrt(vx * vx + vy * vy);
            if (d < dist) {
                dist = d;
                index = i;
            }
        }
        return index;
    }
    
    var dragging = false;
    var drag = -1;
    var dragX, dragY;
    var recalcArcs = false;
    var box;
    //========================================================================
    // New box code from here down
    
    // creates the box when canvas is ready
    var onResize = function(){
        box = {
            x : canvas.width * (1/8),
            y : canvas.height * (1/8),
            w : canvas.width * (6/8),
            h : canvas.height * (6/8),
            recalculate : true,
            arcCount : 20, // number of arcs to try and fit. Does not mean that it will happen
        }
    }
    
    function display() {
        ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
        ctx.globalAlpha = 1; // reset alpha
        ctx.clearRect(0, 0, w, h);
    
    if(mouse.w !== 0){
        if(mouse.buttonRaw & 4){ // change arc depth
            if(mouse.w < 0){
                arcDepth *= 1/1.05;
            }else{
                arcDepth *= 1.05;
            }
            recalcArcs = true;
            
        }else{  // change arc count
            box.arcCount += Math.sign(mouse.w);
            box.arcCount = Math.max(4,box.arcCount);
            box.recalculate = true;
        }
        mouse.w = 0;
    }
    // drag out box;
    if(mouse.buttonRaw & 1){
        if(!dragging){
            box.x = mouse.x;
            box.y = mouse.y;
            dragging = true;
        }
        box.w = mouse.x - box.x;
        box.h = mouse.y - box.y;
        box.recalculate = true;
        if(box.w <0){
            box.x = box.x + box.w;
            box.w = - box.w;
        }
        if(box.h <0){
            box.y = box.y + box.h;
            box.h = - box.h;
        }
    }else{
        dragging = false;
    }
    // stop error
    if(box.w === 0 || box.h === 0){
        box.recalculate = false;
    }
    
    // calculate box arcs
    if(box.recalculate){
        // reset arrays
        points.length = 0;
        arcs.length = 0;
        // get perimeter length
        var perimLen = (box.w + box.h)* 2;
        // get estimated step size
        var step = perimLen / box.arcCount;
        // get inset size for width and hight
        var wInStep = (box.w - (Math.floor(box.w/step)-1)*step) / 2;
        var hInStep = (box.h - (Math.floor(box.h/step)-1)*step) / 2;
        // fix if box to narrow
        if(box.w < step){
            wInStep = 0;
            hInStep = 0;
            step = box.h / (Math.floor(box.h/step));
        }else if(box.h < step){
            wInStep = 0;
            hInStep = 0;
            step = box.w / (Math.floor(box.w/step));
            
        }
        
        // Add points clock wise
        var x = box.x + wInStep;
        while(x < box.x + box.w){ // across top
            addPoint(x,box.y);
            x += step;
        }
        var y = box.y + hInStep; 
        while(y < box.y + box.h){ // down right side
            addPoint(box.x + box.w,y);
            y += step;
        }
        x = box.x + box.w - wInStep;
        while(x > box.x){          // left along bottom
            addPoint(x,box.y + box.h);
            x -= step;
        }
        var y = box.y + box.h - hInStep;
        while(y > box.y){  // up along left side
            addPoint(box.x,y);
            y -= step;
        }
        // calculate arcs.
        for(var i =0; i <points.length; i++){
            calcArc(addArc(i,(i + 1) % points.length,arcDepth));
        }
        box.recalculate = false;
    }
    // recalculate arcs if needed
    for(var i = 0; i < arcs.length; i ++){
        if(recalcArcs){
            calcArc(arcs[i],arcDepth);
        }
    }
    // draw arcs
    drawArcStart(arcWidth,arcCol)
    for(var i = 0; i < arcs.length; i ++){
        drawArc(arcs[i]);
    }
    drawArcDone();
    recalcArcs = false;
    }
    
    
    //===========================================================================================
    // END OF ANSWER
    
    // Boiler plate code from here down. Does mouse,canvas,resize and what not
    var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0, firstRun = true; ;
    (function () {
        const RESIZE_DEBOUNCE_TIME = 100;
        var createCanvas,
        resizeCanvas,
        setGlobals,
        resizeCount = 0;
        createCanvas = function () {
            var c,
            cs;
            cs = (c = document.createElement("canvas")).style;
            cs.position = "absolute";
            cs.top = cs.left = "0px";
            cs.zIndex = 1000;
            document.body.appendChild(c);
            return c;
        }
        resizeCanvas = function () {
            if (canvas === undefined) {
                canvas = createCanvas();
            }
            canvas.width = innerWidth;
            canvas.height = innerHeight;
            ctx = canvas.getContext("2d");
            if (typeof setGlobals === "function") {
                setGlobals();
            }
            if (typeof onResize === "function") {
                if (firstRun) {
                    onResize();
                    firstRun = false;
                } else {
                    resizeCount += 1;
                    setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
                }
            }
        }
        function debounceResize() {
            resizeCount -= 1;
            if (resizeCount <= 0) {
                onResize();
            }
        }
        setGlobals = function () {
            cw = (w = canvas.width) / 2;
            ch = (h = canvas.height) / 2;
        }
        mouse = (function () {
            function preventDefault(e) {
                e.preventDefault();
            }
            var mouse = {
                x : 0,
                y : 0,
                w : 0,
                alt : false,
                shift : false,
                ctrl : false,
                buttonRaw : 0,
                over : false,
                bm : [1, 2, 4, 6, 5, 3],
                active : false,
                bounds : null,
                crashRecover : null,
                mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
            };
            var m = mouse;
            function mouseMove(e) {
                var t = e.type;
                m.bounds = m.element.getBoundingClientRect();
                m.x = e.pageX - m.bounds.left;
                m.y = e.pageY - m.bounds.top;
                m.alt = e.altKey;
                m.shift = e.shiftKey;
                m.ctrl = e.ctrlKey;
                if (t === "mousedown") {
                    m.buttonRaw |= m.bm[e.which - 1];
                } else if (t === "mouseup") {
                    m.buttonRaw &= m.bm[e.which + 2];
                } else if (t === "mouseout") {
                    m.buttonRaw = 0;
                    m.over = false;
                } else if (t === "mouseover") {
                    m.over = true;
                } else if (t === "mousewheel") {
                    m.w = e.wheelDelta;
                } else if (t === "DOMMouseScroll") {
                    m.w = -e.detail;
                }
                e.preventDefault();
            }
            m.start = function (element) {
                if (m.element !== undefined) {
                    m.removeMouse();
                }
                m.element = element === undefined ? document : element;
                m.mouseEvents.forEach(n => {
                    m.element.addEventListener(n, mouseMove);
                });
                m.element.addEventListener("contextmenu", preventDefault, false);
                m.active = true;
            }
            m.remove = function () {
                if (m.element !== undefined) {
                    m.mouseEvents.forEach(n => {
                        m.element.removeEventListener(n, mouseMove);
                    });
                    m.element.removeEventListener("contextmenu", preventDefault);
                    m.element = m.callbacks = undefined;
                    m.active = false;
                }
            }
            return mouse;
        })();
    
    
        function update(timer) { // Main update loop
            if (ctx === undefined) {
                return;
            }
            globalTime = timer;
            display(); // call demo code
            requestAnimationFrame(update);
        }
        setTimeout(function () {
            resizeCanvas();
            mouse.start(canvas, true);
            window.addEventListener("resize", resizeCanvas);
            requestAnimationFrame(update);
        }, 0);
    })();
    Left click drag to create a box&lt;br&gt;Mouse wheel to change arc count&lt;br&gt;Hold right button down and wheel to change arc depth.&lt;br&gt;

    【讨论】:

    • 谢谢,我也在这里看到了这个帖子。但我不想免费绘制它。我只是想展示它。所以基本上我想要一个固定扇贝形状的代码。
    • 汉这个“按住右键和滚轮来改变弧深。” 用触控板是不可能的...你能不能把它改成“按住不放用左脚摸鼻子” 代替?
    【解决方案3】:

    使用https://www.w3schools.com/tags/canvas_beziercurveto.asp“贝塞尔曲线法”制作复杂的形状。

    我建议继续 desmos 并弄乱贝塞尔曲线以了解复杂性。我希望这会有所帮助:)

    编辑:贝塞尔曲线是这样工作的:

    ctx.bezierCurveTo(控制点x,控制点y,第二控制点x,第二控制点y,精加工x,精加工y);

    【讨论】:

    • 哇这很复杂。
    • @DownCrow 抱歉,这是唯一的方法。相信我,一旦掌握了窍门,这将非常容易。 :)
    • @DownCrow 为了什么?贝塞尔曲线/二次曲线以自定义方式制作,因此您可以制作任何您想要的形状。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-07
    • 2018-07-24
    • 1970-01-01
    • 1970-01-01
    • 2013-11-25
    • 2012-02-15
    相关资源
    最近更新 更多