2009-08-10 22 views
100

Sadece dikdörtgen doldurabileceğimizi gördüm, ancak yuvarlak köşeli bir tane yok, bunu nasıl yapabilirim?HTML Tuvalinde yuvarlak bir Dikdörtgen nasıl çizilir?

+0

Bu adam güzel yönteminde (... kenarlıklı, doldurulmuş) yuvarlak dikdörtgenler nasıl koydu: http://js-bits.blogspot.com/2010/07/canvas-rounded-corner- rectangles.html – nerdess

cevap

39

HTML5 tuvali, yuvarlatılmış köşeleri olan bir dikdörtgen çizmek için bir yöntem sağlamaz.

lineTo() ve arc() yöntemlerini kullanmaya ne dersiniz?

arc() yöntemi yerine quadraticCurveTo() yöntemini de kullanabilirsiniz.

+0

Nedense Firefox 3.5 ve Opera 10.0'da arcTo ile ilgili sorunlar yaşıyorum. Bu siteye benzer: http://ditchnet.org/canvas/CanvasRoundedCornerExample.html – bgw

+0

arcTo, en son FF sürümünde düzeltildi. –

231

Aynı şeyi yapmam gerekiyor ve bunu yapmak için bir yöntem oluşturdum. Corgalore

  • tarafından sağlanan köşe başına

    // Now you can just call 
     
    var ctx = document.getElementById("rounded-rect").getContext("2d"); 
     
    // Draw using default border radius, 
     
    // stroke it but no fill (function's default values) 
     
    roundRect(ctx, 5, 5, 50, 50); 
     
    // To change the color on the rectangle, just manipulate the context 
     
    ctx.strokeStyle = "rgb(255, 0, 0)"; 
     
    ctx.fillStyle = "rgba(255, 255, 0, .5)"; 
     
    roundRect(ctx, 100, 5, 100, 100, 20, true); 
     
    // Manipulate it again 
     
    ctx.strokeStyle = "#0f0"; 
     
    ctx.fillStyle = "#ddd"; 
     
    // Different radii for each corner, others default to 0 
     
    roundRect(ctx, 300, 5, 200, 100, { 
     
        tl: 50, 
     
        br: 25 
     
    }, true); 
     
    
     
    /** 
     
    * Draws a rounded rectangle using the current state of the canvas. 
     
    * If you omit the last three params, it will draw a rectangle 
     
    * outline with a 5 pixel border radius 
     
    * @param {CanvasRenderingContext2D} ctx 
     
    * @param {Number} x The top left x coordinate 
     
    * @param {Number} y The top left y coordinate 
     
    * @param {Number} width The width of the rectangle 
     
    * @param {Number} height The height of the rectangle 
     
    * @param {Number} [radius = 5] The corner radius; It can also be an object 
     
    *     to specify different radii for corners 
     
    * @param {Number} [radius.tl = 0] Top left 
     
    * @param {Number} [radius.tr = 0] Top right 
     
    * @param {Number} [radius.br = 0] Bottom right 
     
    * @param {Number} [radius.bl = 0] Bottom left 
     
    * @param {Boolean} [fill = false] Whether to fill the rectangle. 
     
    * @param {Boolean} [stroke = true] Whether to stroke the rectangle. 
     
    */ 
     
    function roundRect(ctx, x, y, width, height, radius, fill, stroke) { 
     
        if (typeof stroke == 'undefined') { 
     
        stroke = true; 
     
        } 
     
        if (typeof radius === 'undefined') { 
     
        radius = 5; 
     
        } 
     
        if (typeof radius === 'number') { 
     
        radius = {tl: radius, tr: radius, br: radius, bl: radius}; 
     
        } else { 
     
        var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0}; 
     
        for (var side in defaultRadius) { 
     
         radius[side] = radius[side] || defaultRadius[side]; 
     
        } 
     
        } 
     
        ctx.beginPath(); 
     
        ctx.moveTo(x + radius.tl, y); 
     
        ctx.lineTo(x + width - radius.tr, y); 
     
        ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr); 
     
        ctx.lineTo(x + width, y + height - radius.br); 
     
        ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height); 
     
        ctx.lineTo(x + radius.bl, y + height); 
     
        ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl); 
     
        ctx.lineTo(x, y + radius.tl); 
     
        ctx.quadraticCurveTo(x, y, x + radius.tl, y); 
     
        ctx.closePath(); 
     
        if (fill) { 
     
        ctx.fill(); 
     
        } 
     
        if (stroke) { 
     
        ctx.stroke(); 
     
        } 
     
    
     
    }
    <canvas id="rounded-rect" width="500" height="200"> 
     
        <!-- Insert fallback content here --> 
     
    </canvas>

    • Farklı yarıçapları
  • +11

    Mükemmel çalışır. Bu seçilen cevap olmalı. +1 –

    +2

    Vay, sadece bu cevap üzerinde ilk downvote aldım, downvoter neden açıkladıysa güzel olurdu –

    +1

    Mükemmel cevap ... Bu nasıl tuval için henüz yerli değil mi ?! Teşekkürler. – andygoestohollywood

    6

    Burada yazdığım biri başka açıklama için http://js-bits.blogspot.com/2010/07/canvas-rounded-corner-rectangles.html bakın ... yerine kuadratik eğrilerinin arkları kullanır yarıçap üzerinde daha iyi kontrol için. Ayrıca, okşayarak bırakır ve sizin Burada

    /* Canvas 2d context - roundRect 
    * 
    * Accepts 5 parameters, the start_x and start_y points, the end_x and end_y points, and the radius of the corners 
    * 
    * No return value 
    */ 
    
    CanvasRenderingContext2D.prototype.roundRect = function(sx,sy,ex,ey,r) { 
        var r2d = Math.PI/180; 
        if((ex - sx) - (2 * r) < 0) { r = ((ex - sx)/2); } //ensure that the radius isn't too large for x 
        if((ey - sy) - (2 * r) < 0) { r = ((ey - sy)/2); } //ensure that the radius isn't too large for y 
        this.beginPath(); 
        this.moveTo(sx+r,sy); 
        this.lineTo(ex-r,sy); 
        this.arc(ex-r,sy+r,r,r2d*270,r2d*360,false); 
        this.lineTo(ex,ey-r); 
        this.arc(ex-r,ey-r,r,r2d*0,r2d*90,false); 
        this.lineTo(sx+r,ey); 
        this.arc(sx+r,ey-r,r,r2d*90,r2d*180,false); 
        this.lineTo(sx,sy+r); 
        this.arc(sx+r,sy+r,r,r2d*180,r2d*270,false); 
        this.closePath(); 
    } 
    

    kadar dolduran bir örnektir:

    var _e = document.getElementById('#my_canvas'); 
    var _cxt = _e.getContext("2d"); 
    _cxt.roundRect(35,10,260,120,20); 
    _cxt.strokeStyle = "#000"; 
    _cxt.stroke(); 
    
    +0

    Bu size yarıçap üzerinde nasıl daha iyi kontrol sağlar? X/y yarıçapına (oval köşelere) izin vereceğinizi ve ayrıca her köşe için farklı yarıçaplar belirleyeceğinizi düşündüm –

    +3

    "r2d" ifadeniz muhtemelen "d2r" olarak adlandırılmak isteniyor. – Grumdrig

    +1

    @JuanMendes: Bu çözümdeki yuvarlatılmış köşelerin (ark tabanlı) şekilleri (kuadratik tabanlı) çözümünüzdekinden daha daireseldir. Sanırım "yarıçap üzerinde daha iyi kontrol" ile kastettiği şey bu. – nobar

    11

    Juan, ben tek tek her dikdörtgen köşe yarıçapını değiştirmek için izin vermek için yönteme hafif bir iyileşme yaptı:

    /** 
    * Draws a rounded rectangle using the current state of the canvas. 
    * If you omit the last three params, it will draw a rectangle 
    * outline with a 5 pixel border radius 
    * @param {Number} x The top left x coordinate 
    * @param {Number} y The top left y coordinate 
    * @param {Number} width The width of the rectangle 
    * @param {Number} height The height of the rectangle 
    * @param {Object} radius All corner radii. Defaults to 0,0,0,0; 
    * @param {Boolean} fill Whether to fill the rectangle. Defaults to false. 
    * @param {Boolean} stroke Whether to stroke the rectangle. Defaults to true. 
    */ 
    CanvasRenderingContext2D.prototype.roundRect = function (x, y, width, height, radius, fill, stroke) { 
        var cornerRadius = { upperLeft: 0, upperRight: 0, lowerLeft: 0, lowerRight: 0 }; 
        if (typeof stroke == "undefined") { 
         stroke = true; 
        } 
        if (typeof radius === "object") { 
         for (var side in radius) { 
          cornerRadius[side] = radius[side]; 
         } 
        } 
    
        this.beginPath(); 
        this.moveTo(x + cornerRadius.upperLeft, y); 
        this.lineTo(x + width - cornerRadius.upperRight, y); 
        this.quadraticCurveTo(x + width, y, x + width, y + cornerRadius.upperRight); 
        this.lineTo(x + width, y + height - cornerRadius.lowerRight); 
        this.quadraticCurveTo(x + width, y + height, x + width - cornerRadius.lowerRight, y + height); 
        this.lineTo(x + cornerRadius.lowerLeft, y + height); 
        this.quadraticCurveTo(x, y + height, x, y + height - cornerRadius.lowerLeft); 
        this.lineTo(x, y + cornerRadius.upperLeft); 
        this.quadraticCurveTo(x, y, x + cornerRadius.upperLeft, y); 
        this.closePath(); 
        if (stroke) { 
         this.stroke(); 
        } 
        if (fill) { 
         this.fill(); 
        } 
    } 
    

    böyle kullanın:

    var canvas = document.getElementById("canvas"); 
    var c = canvas.getContext("2d"); 
    c.fillStyle = "blue"; 
    c.roundRect(50, 100, 50, 100, {upperLeft:10,upperRight:10}, true, true); 
    
    74

    Ben @ jhoff en solüsyonu ile başladı, ama genişlik/yükseklik parametreleri kullanmak için yeniden yazdı ve arcTo kullanarak oldukça biraz daha kısa ve öz yapar:

    CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) { 
        if (w < 2 * r) r = w/2; 
        if (h < 2 * r) r = h/2; 
        this.beginPath(); 
        this.moveTo(x+r, y); 
        this.arcTo(x+w, y, x+w, y+h, r); 
        this.arcTo(x+w, y+h, x, y+h, r); 
        this.arcTo(x, y+h, x, y, r); 
        this.arcTo(x, y, x+w, y, r); 
        this.closePath(); 
        return this; 
    } 
    

    Ayrıca bağlamı dönen Eğer zincir biraz sağlayabilirsiniz. Örneğin: - Aynı şekilde fillRect çağrılabilir

    ctx.roundRect(35, 10, 225, 110, 20).stroke(); //or .fill() for a filled rect 
    
    +4

    Bu iyi çözümün haricinde Tuval oluşturma bağlamıyla uğraşmazdım. –

    +0

    Bu çözümdeki sorun, her köşe için yarıçapı bağımsız olarak kontrol edememenizdir. Yeterince esnek değil. Aşağıdaki çözümüme bakın. Çok güzel ve zarif bir çözüm için – Corgalore

    +1

    +1. – TachyonVortex

    2

    bir kanvas içeriğini kullanarak normal araçlar ile fonksiyonu daha tutarlı hale getirmek için, tuval içerik sınıfı 'fillRoundedRect' yöntemi içerir uzatılabilir denir:

    var canv = document.createElement("canvas"); 
    var cctx = canv.getContext("2d"); 
    
    // If thie canvasContext class doesn't have a fillRoundedRect, extend it now 
    if (!cctx.constructor.prototype.fillRoundedRect) { 
        // Extend the canvaseContext class with a fillRoundedRect method 
        cctx.constructor.prototype.fillRoundedRect = 
        function (xx,yy, ww,hh, rad, fill, stroke) { 
         if (typeof(rad) == "undefined") rad = 5; 
         this.beginPath(); 
         this.moveTo(xx+rad, yy); 
         this.arcTo(xx+ww, yy, xx+ww, yy+hh, rad); 
         this.arcTo(xx+ww, yy+hh, xx, yy+hh, rad); 
         this.arcTo(xx, yy+hh, xx, yy, rad); 
         this.arcTo(xx, yy, xx+ww, yy, rad); 
         if (stroke) this.stroke(); // Default to no stroke 
         if (fill || typeof(fill)=="undefined") this.fill(); // Default to fill 
        }; // end of fillRoundedRect method 
    } 
    

    kod tuval bağlam nesnesi için yapıcı prototipi bir 'fillRoundedRect' özelliği içeren ve bir ekler olmadığını denetler - ilk kez etrafında. Bu fillRect yöntem ile aynı şekilde çağrılır: Grumdring yaptığı gibi

    ctx.fillStyle = "#eef"; ctx.strokeStyle = "#ddf"; 
        // ctx.fillRect(10,10, 200,100); 
        ctx.fillRoundedRect(10,10, 200,100, 5); 
    

    yöntem arcTo yöntemini kullanır. Yöntemde, this, ctx nesnesine bir başvurudır. İnme argümanı, tanımlanmamışsa varsayılan olarak yanlıştır. Doldurma argümanı, tanımlanmamışsa dikdörtgeni doldurmak için varsayılan değerdir.

    (bütün uygulamaları bu şekilde uzamasına izin eğer Firefox üzerinde test, bilmiyorum.)

    +1

    Eklemeyi öneririm: 'rad = Math.min (rad, ww/2, hh/2);' bu, Grumdrig'in sürümünde olduğu gibi büyük yarıçaplarla çalışır. – nobar

    2

    Opera, ffs. Opera WebKit gidiyor yana

    if (window["CanvasRenderingContext2D"]) { 
        /** @expose */ 
        CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { 
         if (w < 2*r) r = w/2; 
         if (h < 2*r) r = h/2; 
         this.beginPath(); 
         if (r < 1) { 
          this.rect(x, y, w, h); 
         } else { 
          if (window["opera"]) { 
           this.moveTo(x+r, y); 
           this.arcTo(x+r, y, x, y+r, r); 
           this.lineTo(x, y+h-r); 
           this.arcTo(x, y+h-r, x+r, y+h, r); 
           this.lineTo(x+w-r, y+h); 
           this.arcTo(x+w-r, y+h, x+w, y+h-r, r); 
           this.lineTo(x+w, y+r); 
           this.arcTo(x+w, y+r, x+w-r, y, r); 
          } else { 
           this.moveTo(x+r, y); 
           this.arcTo(x+w, y, x+w, y+h, r); 
           this.arcTo(x+w, y+h, x, y+h, r); 
           this.arcTo(x, y+h, x, y, r); 
           this.arcTo(x, y, x+w, y, r); 
          } 
         } 
         this.closePath(); 
        }; 
        /** @expose */ 
        CanvasRenderingContext2D.prototype.fillRoundRect = function(x, y, w, h, r) { 
         this.roundRect(x, y, w, h, r); 
         this.fill(); 
        }; 
        /** @expose */ 
        CanvasRenderingContext2D.prototype.strokeRoundRect = function(x, y, w, h, r) { 
         this.roundRect(x, y, w, h, r); 
         this.stroke(); 
        }; 
    } 
    

    , bu da eski halinde geçerli kalmalıdır.

    7

    drawPolygon işlevi aşağıdaki yuvarlatılmış köşeleri olan herhangi bir çokgen çizmek için kullanılabilir.

    See it running here.

    function drawPolygon(ctx, pts, radius) { 
        if (radius > 0) { 
        pts = getRoundedPoints(pts, radius); 
        } 
        var i, pt, len = pts.length; 
        ctx.beginPath(); 
        for (i = 0; i < len; i++) { 
        pt = pts[i]; 
        if (i == 0) {   
         ctx.moveTo(pt[0], pt[1]); 
        } else { 
         ctx.lineTo(pt[0], pt[1]); 
        } 
        if (radius > 0) { 
         ctx.quadraticCurveTo(pt[2], pt[3], pt[4], pt[5]); 
        } 
        } 
        ctx.closePath(); 
    } 
    
    function getRoundedPoints(pts, radius) { 
        var i1, i2, i3, p1, p2, p3, prevPt, nextPt, 
         len = pts.length, 
         res = new Array(len); 
        for (i2 = 0; i2 < len; i2++) { 
        i1 = i2-1; 
        i3 = i2+1; 
        if (i1 < 0) { 
         i1 = len - 1; 
        } 
        if (i3 == len) { 
         i3 = 0; 
        } 
        p1 = pts[i1]; 
        p2 = pts[i2]; 
        p3 = pts[i3]; 
        prevPt = getRoundedPoint(p1[0], p1[1], p2[0], p2[1], radius, false); 
        nextPt = getRoundedPoint(p2[0], p2[1], p3[0], p3[1], radius, true); 
        res[i2] = [prevPt[0], prevPt[1], p2[0], p2[1], nextPt[0], nextPt[1]]; 
        } 
        return res; 
    }; 
    
    function getRoundedPoint(x1, y1, x2, y2, radius, first) { 
        var total = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)), 
         idx = first ? radius/total : (total - radius)/total; 
        return [x1 + (idx * (x2 - x1)), y1 + (idx * (y2 - y1))]; 
    }; 
    

    işlevi aşağıdaki gibi, çokgen noktaları ile bir dizi alır:

    var canvas = document.getElementById("cv"); 
    var ctx = canvas.getContext("2d"); 
    ctx.strokeStyle = "#000000"; 
    ctx.lineWidth = 5; 
    
    drawPolygon(ctx, [[20, 20], 
            [120, 20], 
            [120, 120], 
            [ 20, 120]], 10); 
    ctx.stroke(); 
    

    Bu bağlantı noktası ve bir çözeltinin, bir daha genel versiyon here yayınlanmıştır.

    2
    var canvas = document.createElement("canvas"); 
        document.body.appendChild(canvas); 
        var ctx = canvas.getContext("2d"); 
        ctx.beginPath(); 
        ctx.moveTo(100,100); 
        ctx.arcTo(0,100,0,0,30); 
        ctx.arcTo(0,0,100,0,30); 
        ctx.arcTo(100,0,100,100,30); 
        ctx.arcTo(100,100,0,100,30); 
        ctx.fill(); 
    
    İlgili konular