简体   繁体   中英

Javascript - Canvas CSS border-radius imitation

I'm trying to imitate the behavior of CSS border-radius property on a canvas. I've done something which is working, but there are some missing adaptations that the browser is handling, which i can't reproduce on the canvas (See image link below as an example)

However, i'm struggling to adapt the borders when they are getting out of the shape.

Here is an example, let's take an HTML shape of 100px (Width) X 100px (Height), and then apply the following radius : border-radius: 100px 52px 1px 1px;

Then draw this shape in a canvas with the same parameters.

And then i get this (RED SHAPE = Canvas shape, GREEN SHAPE = HTML shape) https://imgur.com/a/XGuca

(Sorry i'm not able to upload image, because of my reputation)

I'm using this function to draw the shape

function (xx, yy, ww, hh, rad, fill, stroke) {
    if (typeof(rad) === "undefined") rad = 5;
    this.beginPath();
    this.moveTo(xx, yy);
    this.arcTo(xx + ww, yy, xx + ww, yy + hh, rad.tr);
    this.arcTo(xx + ww, yy + hh, xx, yy + hh, rad.br);
    this.arcTo(xx, yy + hh, xx, yy, rad.bl);
    this.arcTo(xx, yy, xx + ww, yy, rad.tl);
    if (stroke) this.stroke();  // Default to no stroke
    if (fill || typeof(fill) === "undefined") this.fill();  // Default to fill
};

Here is the description of the parameters

xx: X axis position yy: Y axis position ww: Width hh: Height rad: {tl:0, tr:0, br:0, bl: 0} (For top-left, top-right, bottom-right, bottom-left RADIUS)

I'm not getting what i could do in order to make it working, could someone help me or give me a tip in order to do that ? Thanks !

PS: Sorry for my bad english

(See snippet below)

  // Ctx var ctx = document.getElementById("rounded-rect").getContext("2d"); //Round rect func ctx.constructor.prototype.fillRoundedRect = function (xx, yy, ww, hh, rad, fill, stroke) { if (typeof(rad) === "undefined") rad = 5; this.beginPath(); this.moveTo(xx, yy); this.arcTo(xx + ww, yy, xx + ww, yy + hh, rad.tr); this.arcTo(xx + ww, yy + hh, xx, yy + hh, rad.br); this.arcTo(xx, yy + hh, xx, yy, rad.bl); this.arcTo(xx, yy, xx + ww, yy, rad.tl); if (stroke) this.stroke(); // Default to no stroke if (fill || typeof(fill) === "undefined") this.fill(); // Default to fill }; ctx.fillStyle = "red"; ctx.strokeStyle = "#ddf"; var copy = document.getElementById('copy'); var tl = document.getElementById('tl'); var tr = document.getElementById('tr'); var bl = document.getElementById('bl'); var br = document.getElementById('br'); var off = document.getElementById('off'); function test() { ctx.clearRect(0, 0, 600, 500); /* 1.Top left */ /* 2. Top right */ /* 3. Bottom right */ /* 4. Bottom left */ var borders = [tl.value, tr.value, br.value, bl.value].join('px ') + 'px'; copy.style.borderRadius = borders; var copyRad = borders.replace(/px/g, '').split(' ').map(function (a) { return parseInt(a) }); var rad = { tl: copyRad[0], tr: copyRad[1], br: copyRad[2], bl: copyRad[3] }; var o = +off.value; ctx.fillRoundedRect(15 + o, 15 + o, 100, 100, rad); } tl.oninput = test; tr.oninput = test; bl.oninput = test; br.oninput = test; off.oninput = test; test(); 
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> html, body { margin: 0; padding: 0; } </style> </head> <body> <div style="display:inline-block; position: absolute; left:120px;top:120px; width: 100px; height: 100px; background:green; border-radius: 10px 5px 10px 20px;" id="copy"> </div> <canvas style="display: inline-block; position: absolute; zindex:0; left:0; top:0;" id="rounded-rect" width="600" height="500"> </canvas> <div style="top: 300px; position:absolute; z-index: 1;"> <label> Top left <input type="range" min="1" max="100" value="0" class="slider" id="tl"></label><br/> <label> Top right <input type="range" min="1" max="100" value="0" class="slider" id="tr"></label><br/> <label> Bottom left <input type="range" min="1" max="100" value="0" class="slider" id="bl"></label><br/> <label> Bottom right <input type="range" min="1" max="100" value="0" class="slider" id="br"></label><br/> <label> Offset <input type="range" min="1" max="200" value="0" class="slider" id="off"></label><br/> </div> </body> </html> 

Seems that the browser somehow corrects the radius if it gets out of the shape. I've created the correctRadius function that does something like this. Of course, the result is not exact the same, but at least the shape doesn't have any gaps.

 // Ctx var ctx = document.getElementById("rounded-rect").getContext("2d"); function correctRadius(r, w, h) { if (r.tl + r.tr > w) { r.tl -= (r.tl + r.tr - w) / 2; r.tr = w - r.tl; } if (r.bl + r.br > w) { r.br -= (r.br + r.bl - w) / 2; r.bl = w - r.br; } if (r.tl + r.bl > h) { r.tl -= (r.tl + r.bl - h) / 2; r.bl = h - r.tl; } if (r.tr + r.br > h) { r.tr -= (r.tr + r.br - h) / 2; r.br = h - r.tr; } } //Round rect func ctx.constructor.prototype.fillRoundedRect = function (xx, yy, ww, hh, rad, fill, stroke) { correctRadius(rad, ww, hh); if (typeof(rad) === "undefined") rad = 5; this.beginPath(); this.moveTo(xx, yy); this.arcTo(xx + ww, yy, xx + ww, yy + hh, rad.tr); this.arcTo(xx + ww, yy + hh, xx, yy + hh, rad.br); this.arcTo(xx, yy + hh, xx, yy, rad.bl); this.arcTo(xx, yy, xx + ww, yy, rad.tl); if (stroke) this.stroke(); // Default to no stroke if (fill || typeof(fill) === "undefined") this.fill(); // Default to fill }; ctx.fillStyle = "red"; ctx.strokeStyle = "#ddf"; var copy = document.getElementById('copy'); var tl = document.getElementById('tl'); var tr = document.getElementById('tr'); var bl = document.getElementById('bl'); var br = document.getElementById('br'); var off = document.getElementById('off'); function test() { ctx.clearRect(0, 0, 600, 500); /* 1.Top left */ /* 2. Top right */ /* 3. Bottom right */ /* 4. Bottom left */ var borders = [tl.value, tr.value, br.value, bl.value].join('px ') + 'px'; copy.style.borderRadius = borders; var copyRad = borders.replace(/px/g, '').split(' ').map(function (a) { return parseInt(a) }); var rad = { tl: copyRad[0], tr: copyRad[1], br: copyRad[2], bl: copyRad[3] }; var o = +off.value; ctx.fillRoundedRect(15 + o, 15 + o, 100, 100, rad); } tl.oninput = test; tr.oninput = test; bl.oninput = test; br.oninput = test; off.oninput = test; test(); 
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> html, body { margin: 0; padding: 0; } </style> </head> <body> <div style="display:inline-block; position: absolute; left:120px;top:120px; width: 100px; height: 100px; background:green; border-radius: 10px 5px 10px 20px;" id="copy"> </div> <canvas style="display: inline-block; position: absolute; zindex:0; left:0; top:0;" id="rounded-rect" width="600" height="500"> </canvas> <div style="top: 300px; position:absolute; z-index: 1;"> <label> Top left <input type="range" min="1" max="100" value="0" class="slider" id="tl"></label><br/> <label> Top right <input type="range" min="1" max="100" value="0" class="slider" id="tr"></label><br/> <label> Bottom left <input type="range" min="1" max="100" value="0" class="slider" id="bl"></label><br/> <label> Bottom right <input type="range" min="1" max="100" value="0" class="slider" id="br"></label><br/> <label> Offset <input type="range" min="1" max="200" value="0" class="slider" id="off"></label><br/> </div> </body> </html> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM